Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamping building and release with dagger multibuild pipeline #25

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
34 changes: 20 additions & 14 deletions .github/workflows/publish_release.yml
@@ -1,26 +1,32 @@
name: Publish Release
name: Dagger Release Pipeline

on:
push:
tags:
- v*
- "v*"
branches: [main]

permissions:
contents: write
packages: write

jobs:
build:
runs-on: ubuntu-20.04
publish-release:
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Checkout repo
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup go env
uses: actions/setup-go@master

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.21"
- name: goreleaser with tag
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --rm-dist
env:
cache: true

- name: Run Dagger Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go run ci/dagger.go release ${{ env.GITHUB_TOKEN }}
32 changes: 32 additions & 0 deletions .github/workflows/pull_request.yml
@@ -0,0 +1,32 @@
name: Dagger Pull Request Pipeline

on:
pull_request:
paths-ignore:
- '*.md'
- 'assets/**'

permissions:
contents: write # This is required for actions/checkout
packages: write # This is required for publishing the package

jobs:
test-build:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.21"
cache: true

- name: Run Dagger Pull Request
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go run ci/dagger.go pull-request
2 changes: 0 additions & 2 deletions .gitignore
Expand Up @@ -16,9 +16,7 @@

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
/bin

/harbor
Expand Down
21 changes: 17 additions & 4 deletions .goreleaser.yaml
Expand Up @@ -3,7 +3,7 @@ project_name: harbor
before:
hooks:
- go mod tidy

builds:
- main: ./cmd/harbor/main.go

Expand All @@ -18,18 +18,31 @@ builds:
goarch:
- amd64
- arm64
- arm
ignore:
- goos: windows
goarch: arm
- goos: windows
goarch: arm64
mod_timestamp: "{{ .CommitTimestamp }}"
archives:
- format: tar.gz
format_overrides:
- goos: windows
format: zip

nfpms:
- package_name: harbor
homepage: https://github.com/goharbor/harbor-cli/
maintainer: Vadim Bauer
description: |-
[Sandbox] Official Harbor CLI
formats:
- rpm
- deb
- apk
- archlinux

sboms:
- artifacts: archive
checksum:
name_template: 'checksums.txt'

Expand All @@ -40,7 +53,7 @@ release:
name_template: "HarborCLI {{.Tag}}"
# draft: true
# prerelease: auto

changelog:
sort: asc
filters:
Expand Down
58 changes: 58 additions & 0 deletions build.go
@@ -0,0 +1,58 @@
package main

import (
"context"
"fmt"
"strings"
"os"

"dagger.io/dagger"
)

func main() {
if err := build(context.Background()); err != nil {
fmt.Println(err)
}
}

func build(ctx context.Context) error {
fmt.Println("Building with Dagger")
oses := []string{"linux", "darwin", "windows"}
arches := []string{"amd64", "arm64"}

client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
return err
}
defer client.Close()
src := client.Host().Directory(".")
outputs := client.Directory()
golangcont := client.Container().From("golang:latest")
golangcont = golangcont.WithDirectory("/src", src).WithWorkdir("/src")

golangcont = golangcont.WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find . -type f -name 'main.go' -print -quit) && echo $MAIN_GO_PATH > main_go_path.txt" })

// reading the content of main_go_path.txt file and fetching the actual path of main.go
main_go_txt_file, _ := golangcont.File("main_go_path.txt").Contents(ctx)
trimmedPath := strings.TrimPrefix(main_go_txt_file, "./")
result := "/src/" + trimmedPath
main_go_path := strings.TrimRight(result, "\n")

for _, goos := range oses {
for _, goarch := range arches {
path := fmt.Sprintf("build/%s/%s/", goos, goarch)
build := golangcont.WithEnvVariable("GOOS", goos)
build = build.WithEnvVariable("GOARCH", goarch)
build = build.WithExec([]string{"go", "build", "-o", path, main_go_path})
// get reference to build output directory in container
outputs = outputs.WithDirectory(path, build.Directory(path))
}
}
// write build artifacts to host
_, err = outputs.Export(ctx, ".")
if err != nil {
return err
}
fmt.Println("BUILD COMPLETED!")
return nil
}
140 changes: 140 additions & 0 deletions ci/dagger.go
@@ -0,0 +1,140 @@
package main

import (
"context"
"flag"
"fmt"
"log"
"os"

"dagger.io/dagger"
)

const (
GO_VERSION = "1.22"
SYFT_VERSION = "v0.105.0"
GORELEASER_VERSION = "v1.24.0"
APP_NAME = "dagger-harbor-cli"
BUILD_PATH = "dist"
)

var (
err error
res string
is_local bool
GithubToken string
)

func main() {
// Set a global flag when running locally
flag.BoolVar(&is_local, "local", false, "whether to run locally [global]")
flag.Parse()
task := flag.Arg(0)
GithubToken = flag.Arg(1)

if len(task) == 0 {
log.Fatalln("Missing argument. Expected either 'pull-request' or 'release'.")
}
if task != "pull-request" && task != "release" {
log.Fatalln("Invalid argument. Expected either 'pull-request' or 'release'.")
}

ctx := context.Background()
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stdout))
if err != nil {
panic(err)
}
defer func() {
log.Printf("Closing Dagger client...")
client.Close()
}()

log.Println("Connected to Dagger")
switch task {
case "pull-request":
res, err = pullrequest(ctx, client)
case "release":
res, err = release(ctx, client)
}

if err != nil {
panic(fmt.Sprintf("Error %s: %+v\n", task, err))

}
log.Println(res)
}

// `example: go run ci/dagger.go [-local,-help] pull-request `
func pullrequest(ctx context.Context, client *dagger.Client) (string, error) {
directory := client.Host().Directory(".")

// Create a go container with the source code mounted
golang := client.Container().
From(fmt.Sprintf("golang:%s-alpine", GO_VERSION)).
WithMountedDirectory("/src", directory).WithWorkdir("/src").
WithMountedCache("/go/pkg/mod", client.CacheVolume("gomod")).
WithEnvVariable("CGO_ENABLED", "0")

_, err := golang.WithExec([]string{"go", "test", "./..."}).
Stderr(ctx)

if err != nil {
return "", err
}

log.Println("Tests passed successfully!")
goreleaser := goreleaserContainer(ctx, client, directory).WithExec([]string{"release", "--snapshot", "--clean"})
_, err = goreleaser.Stderr(ctx)

if err != nil {
return "", err
}

if is_local {
// Retrieve the dist directory from the container
dist := goreleaser.Directory(BUILD_PATH)

// Export the dist directory when running locally
_, err = dist.Export(ctx, BUILD_PATH)
if err != nil {
return "", err
}
log.Printf("Exported %v to local successfully!", BUILD_PATH)

}
return "Pull-Request tasks completed successfully!", nil
}

// `example: go run ci/dagger.go release`
func release(ctx context.Context, client *dagger.Client) (string, error) {
directory := client.Host().Directory(".")
goreleaser := goreleaserContainer(ctx, client, directory).WithExec([]string{"--clean"})

_, err = goreleaser.Stderr(ctx)
if err != nil {
return "", err
}

return "Release tasks completed successfully!", nil
}

// goreleaserContainer returns a goreleaser container with the syft binary mounted and GITHUB_TOKEN secret set
//
// `example: goreleaserContainer(ctx, client, directory).WithExec([]string{"build"})`
func goreleaserContainer(ctx context.Context, client *dagger.Client, directory *dagger.Directory) *dagger.Container {
token := client.SetSecret("github_token", GithubToken)

// Export the syft binary from the syft container as a file
syft := client.Container().From(fmt.Sprintf("anchore/syft:%s", SYFT_VERSION)).
WithMountedCache("/go/pkg/mod", client.CacheVolume("gomod")).
File("/syft")

// Run go build to check if the binary compiles
return client.Container().From(fmt.Sprintf("goreleaser/goreleaser:%s", GORELEASER_VERSION)).
WithMountedCache("/go/pkg/mod", client.CacheVolume("gomod")).
WithFile("/bin/syft", syft).
WithMountedDirectory("/src", directory).WithWorkdir("/src").
WithEnvVariable("TINI_SUBREAPER", "true").
WithSecretVariable("GITHUB_TOKEN", token)

}
14 changes: 14 additions & 0 deletions ci/go.mod
@@ -0,0 +1,14 @@
module dagger-harbor-cli/ci

go 1.21

require dagger.io/dagger v0.5.2

require (
github.com/Khan/genqlient v0.5.0 // indirect
github.com/adrg/xdg v0.4.0 // indirect
github.com/iancoleman/strcase v0.2.0 // indirect
github.com/vektah/gqlparser/v2 v2.5.1 // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
)