From f97108f32d4cc32794906b49a523f1107a3538e0 Mon Sep 17 00:00:00 2001 From: Althaf66 Date: Sat, 8 Jun 2024 13:17:48 +0530 Subject: [PATCH 01/17] created confirmation Signed-off-by: Althaf66 --- cmd/harbor/root/user/elevate.go | 11 +++++++---- pkg/views/confirmation.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 pkg/views/confirmation.go diff --git a/cmd/harbor/root/user/elevate.go b/cmd/harbor/root/user/elevate.go index 9369b9c7..974a25ba 100644 --- a/cmd/harbor/root/user/elevate.go +++ b/cmd/harbor/root/user/elevate.go @@ -5,6 +5,7 @@ import ( "github.com/goharbor/harbor-cli/pkg/api" "github.com/goharbor/harbor-cli/pkg/prompt" + "github.com/goharbor/harbor-cli/pkg/views" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -25,10 +26,12 @@ func ElevateUserCmd() *cobra.Command { userId = prompt.GetUserIdFromUser() } - // Todo : Ask for the confirmation before elevating the user to admin role - - err = api.ElevateUser(userId) - + confirm := views.UserElevateConfirm() + if confirm { + err = api.ElevateUser(userId) + } else { + log.Error("Permission denied for elevate user to admin.") + } if err != nil { log.Errorf("failed to elevate user: %v", err) } diff --git a/pkg/views/confirmation.go b/pkg/views/confirmation.go new file mode 100644 index 00000000..4c10082a --- /dev/null +++ b/pkg/views/confirmation.go @@ -0,0 +1,28 @@ +package views + +import ( + "github.com/charmbracelet/huh" + log "github.com/sirupsen/logrus" +) + +func UserElevateConfirm() bool{ + var confirm bool + + err := huh.NewConfirm(). + Title("Are you sure to elevate the user to admin role?"). + Affirmative("Yes"). + Negative("No"). + Value(&confirm).Run() + if err != nil { + log.Fatal(err) + } + + return confirm +} + + + + + + + \ No newline at end of file From 4667f2dd9d840fbd80ba24f5291ff29db133e07b Mon Sep 17 00:00:00 2001 From: Althaf66 Date: Sat, 8 Jun 2024 13:27:03 +0530 Subject: [PATCH 02/17] updated confirmation file Signed-off-by: Althaf66 --- cmd/harbor/root/user/elevate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/harbor/root/user/elevate.go b/cmd/harbor/root/user/elevate.go index 974a25ba..19bf5df9 100644 --- a/cmd/harbor/root/user/elevate.go +++ b/cmd/harbor/root/user/elevate.go @@ -26,7 +26,7 @@ func ElevateUserCmd() *cobra.Command { userId = prompt.GetUserIdFromUser() } - confirm := views.UserElevateConfirm() + confirm,err := views.ConfirmElevation() if confirm { err = api.ElevateUser(userId) } else { From a926e8495da42a144247553b00e10000a0143f9e Mon Sep 17 00:00:00 2001 From: Althaf66 Date: Sat, 8 Jun 2024 13:29:12 +0530 Subject: [PATCH 03/17] updated confirmation.go file. Signed-off-by: Althaf66 --- pkg/views/confirmation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/views/confirmation.go b/pkg/views/confirmation.go index 4c10082a..71f9e291 100644 --- a/pkg/views/confirmation.go +++ b/pkg/views/confirmation.go @@ -5,7 +5,7 @@ import ( log "github.com/sirupsen/logrus" ) -func UserElevateConfirm() bool{ +func ConfirmElevation() (bool, error){ var confirm bool err := huh.NewConfirm(). @@ -17,7 +17,7 @@ func UserElevateConfirm() bool{ log.Fatal(err) } - return confirm + return confirm, nil } From b6baf48f4a69cbe9f38c74de8ce1f7205036d9ae Mon Sep 17 00:00:00 2001 From: Althaf66 Date: Sat, 8 Jun 2024 18:19:39 +0530 Subject: [PATCH 04/17] updated elevate cmd Signed-off-by: Althaf66 --- cmd/harbor/root/user/elevate.go | 2 +- pkg/views/confirmation.go | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/cmd/harbor/root/user/elevate.go b/cmd/harbor/root/user/elevate.go index 19bf5df9..25cc7299 100644 --- a/cmd/harbor/root/user/elevate.go +++ b/cmd/harbor/root/user/elevate.go @@ -26,7 +26,7 @@ func ElevateUserCmd() *cobra.Command { userId = prompt.GetUserIdFromUser() } - confirm,err := views.ConfirmElevation() + confirm, err := views.ConfirmElevation() if confirm { err = api.ElevateUser(userId) } else { diff --git a/pkg/views/confirmation.go b/pkg/views/confirmation.go index 71f9e291..8bbf7067 100644 --- a/pkg/views/confirmation.go +++ b/pkg/views/confirmation.go @@ -5,24 +5,17 @@ import ( log "github.com/sirupsen/logrus" ) -func ConfirmElevation() (bool, error){ +func ConfirmElevation() (bool, error) { var confirm bool - + err := huh.NewConfirm(). - Title("Are you sure to elevate the user to admin role?"). - Affirmative("Yes"). - Negative("No"). - Value(&confirm).Run() + Title("Are you sure to elevate the user to admin role?"). + Affirmative("Yes"). + Negative("No"). + Value(&confirm).Run() if err != nil { log.Fatal(err) } return confirm, nil } - - - - - - - \ No newline at end of file From d048c18b7a220ff6029d7ee42ef70fd8b652542b Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Sun, 11 Aug 2024 02:09:48 +0530 Subject: [PATCH 05/17] feat: add table view to artifact list - create a new list view - add util function for handling size Signed-off-by: 35C4n0r --- cmd/harbor/root/artifact/list.go | 5 ++-- pkg/utils/helper.go | 5 ++++ pkg/views/artifact/list/view.go | 48 ++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 pkg/views/artifact/list/view.go diff --git a/cmd/harbor/root/artifact/list.go b/cmd/harbor/root/artifact/list.go index 37e928fa..22be9278 100644 --- a/cmd/harbor/root/artifact/list.go +++ b/cmd/harbor/root/artifact/list.go @@ -5,6 +5,7 @@ import ( "github.com/goharbor/harbor-cli/pkg/api" "github.com/goharbor/harbor-cli/pkg/prompt" "github.com/goharbor/harbor-cli/pkg/utils" + artifactViews "github.com/goharbor/harbor-cli/pkg/views/artifact/list" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -30,9 +31,7 @@ func ListArtifactCommand() *cobra.Command { if err != nil { log.Errorf("failed to list artifacts: %v", err) } - - log.Infof("Artifacts: %v", resp) - + artifactViews.ListArtifacts(resp.Payload) }, } diff --git a/pkg/utils/helper.go b/pkg/utils/helper.go index 8484ce7d..612f814f 100644 --- a/pkg/utils/helper.go +++ b/pkg/utils/helper.go @@ -35,3 +35,8 @@ func FormatUrl(url string) string { } return url } + +func FormatSize(size int64) string { + mbSize := float64(size) / (1024 * 1024) + return fmt.Sprintf("%.2fMiB", mbSize) +} diff --git a/pkg/views/artifact/list/view.go b/pkg/views/artifact/list/view.go new file mode 100644 index 00000000..3b851aee --- /dev/null +++ b/pkg/views/artifact/list/view.go @@ -0,0 +1,48 @@ +package list + +import ( + "fmt" + "github.com/charmbracelet/bubbles/table" + tea "github.com/charmbracelet/bubbletea" + "github.com/goharbor/go-client/pkg/sdk/v2.0/models" + "github.com/goharbor/harbor-cli/pkg/utils" + "github.com/goharbor/harbor-cli/pkg/views/base/tablelist" + "os" + "strconv" +) + +var columns = []table.Column{ + {Title: "ID", Width: 6}, + {Title: "Artifact Digest", Width: 20}, + {Title: "Type", Width: 12}, + {Title: "Size", Width: 12}, + {Title: "Vulnerabilities", Width: 15}, + {Title: "Push Time", Width: 12}, +} + +func ListArtifacts(artifacts []*models.Artifact) { + var rows []table.Row + for _, artifact := range artifacts { + pushTime, _ := utils.FormatCreatedTime(artifact.PushTime.String()) + artifactSize := utils.FormatSize(artifact.Size) + var totalVulnerabilities int64 + for _, scan := range artifact.ScanOverview { + totalVulnerabilities += scan.Summary.Total + } + rows = append(rows, table.Row{ + strconv.FormatInt(int64(artifact.ID), 10), + artifact.Digest[:16], + artifact.Type, + artifactSize, + strconv.FormatInt(totalVulnerabilities, 10), + pushTime, + }) + } + + m := tablelist.NewModel(columns, rows, len(rows)) + + if _, err := tea.NewProgram(m).Run(); err != nil { + fmt.Println("Error running program:", err) + os.Exit(1) + } +} From f9b8901d29f11927b8c8411a233f7ce74857e32b Mon Sep 17 00:00:00 2001 From: ALTHAF Date: Sun, 18 Aug 2024 10:48:53 +0530 Subject: [PATCH 06/17] modified project create cmd Signed-off-by: ALTHAF --- cmd/harbor/root/project/create.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cmd/harbor/root/project/create.go b/cmd/harbor/root/project/create.go index 275e4bc2..9366e518 100644 --- a/cmd/harbor/root/project/create.go +++ b/cmd/harbor/root/project/create.go @@ -14,9 +14,8 @@ func CreateProjectCommand() *cobra.Command { cmd := &cobra.Command{ Use: "create [project name]", Short: "create project", - Args: cobra.ExactArgs(1), + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - opts.ProjectName = args[0] var err error createView := &create.CreateView{ ProjectName: opts.ProjectName, @@ -28,8 +27,6 @@ func CreateProjectCommand() *cobra.Command { if len(args) > 0 { opts.ProjectName = args[0] err = api.CreateProject(opts) - } else if opts.ProjectName != "" && opts.RegistryID != "" && opts.StorageLimit != "" { - err = api.CreateProject(opts) } else { err = createProjectView(createView) } From 3c71a0270af30d09cbb1c7d824f6de19ddad3bf8 Mon Sep 17 00:00:00 2001 From: rahuldibro Date: Wed, 21 Aug 2024 20:47:39 +0530 Subject: [PATCH 07/17] implemented docker publish function using dagger Signed-off-by: rahuldibro Signed-off-by: bishal7679 --- .github/workflows/docker_publish.yml | 31 ++++++ .gitignore | 4 + dagger.gen.go | 143 ++++++++++++++++++++++++- dagger.json | 7 ++ internal/dagger/dagger.gen.go | 149 +++++++++++++++++++++++++++ main.go | 52 +++++++--- 6 files changed, 372 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/docker_publish.yml diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml new file mode 100644 index 00000000..4f9f1d3f --- /dev/null +++ b/.github/workflows/docker_publish.yml @@ -0,0 +1,31 @@ +name: Docker Publish + +on: + push: + branches: [main] + pull_request: + paths-ignore: + - '*.md' + +jobs: + docker-publish: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Call Docker-Publish Function + uses: dagger/dagger-for-github@v6.1.0 + if: always() + env: + REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} + COSIGN_KEY: ${{ secrets.COSIGN_KEY }} + with: + version: "latest" + verb: call + module: github.com/bishal7679/harbor-cli@v0.6.2 + args: docker-publish --directory-arg=. --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} --private-key=${{ env.COSIGN_KEY }} --password=${{ env.COSIGN_PASSWORD }} diff --git a/.gitignore b/.gitignore index 4f6c7b6e..bba0048c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,7 @@ go.work /harbor dist/ +/dagger.gen.go +/internal/dagger +/internal/querybuilder +/internal/telemetry diff --git a/dagger.gen.go b/dagger.gen.go index 72620bff..720d5b68 100644 --- a/dagger.gen.go +++ b/dagger.gen.go @@ -192,12 +192,125 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st } } return (*HarborCli).GrepDir(&parent, ctx, directoryArg, pattern) + case "LintCode": + var parent HarborCli + err = json.Unmarshal(parentJSON, &parent) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) + } + var directoryArg *dagger.Directory + if inputArgs["directoryArg"] != nil { + err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) + } + } + return (*HarborCli).LintCode(&parent, ctx, directoryArg), nil + case "BuildHarbor": + var parent HarborCli + err = json.Unmarshal(parentJSON, &parent) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) + } + var directoryArg *dagger.Directory + if inputArgs["directoryArg"] != nil { + err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) + } + } + return (*HarborCli).BuildHarbor(&parent, ctx, directoryArg), nil + case "PullRequest": + var parent HarborCli + err = json.Unmarshal(parentJSON, &parent) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) + } + var directoryArg *dagger.Directory + if inputArgs["directoryArg"] != nil { + err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) + } + } + var githubToken string + if inputArgs["githubToken"] != nil { + err = json.Unmarshal([]byte(inputArgs["githubToken"]), &githubToken) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg githubToken", err)) + } + } + (*HarborCli).PullRequest(&parent, ctx, directoryArg, githubToken) + return nil, nil + case "Release": + var parent HarborCli + err = json.Unmarshal(parentJSON, &parent) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) + } + var directoryArg *dagger.Directory + if inputArgs["directoryArg"] != nil { + err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) + } + } + var githubToken string + if inputArgs["githubToken"] != nil { + err = json.Unmarshal([]byte(inputArgs["githubToken"]), &githubToken) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg githubToken", err)) + } + } + (*HarborCli).Release(&parent, ctx, directoryArg, githubToken) + return nil, nil + case "DockerPublish": + var parent HarborCli + err = json.Unmarshal(parentJSON, &parent) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) + } + var directoryArg *dagger.Directory + if inputArgs["directoryArg"] != nil { + err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) + } + } + var regUsername string + if inputArgs["regUsername"] != nil { + err = json.Unmarshal([]byte(inputArgs["regUsername"]), ®Username) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg regUsername", err)) + } + } + var regPassword *dagger.Secret + if inputArgs["regPassword"] != nil { + err = json.Unmarshal([]byte(inputArgs["regPassword"]), ®Password) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg regPassword", err)) + } + } + var privateKey *dagger.Secret + if inputArgs["privateKey"] != nil { + err = json.Unmarshal([]byte(inputArgs["privateKey"]), &privateKey) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg privateKey", err)) + } + } + var password *dagger.Secret + if inputArgs["password"] != nil { + err = json.Unmarshal([]byte(inputArgs["password"]), &password) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg password", err)) + } + } + return (*HarborCli).DockerPublish(&parent, ctx, directoryArg, regUsername, regPassword, privateKey, password), nil default: return nil, fmt.Errorf("unknown function %s", fnName) } case "": return dag.Module(). - WithDescription("A generated module for HarborCli functions\n\nThis module has been generated via dagger init and serves as a reference to\nbasic module structure as you get started with Dagger.\n\nTwo functions have been pre-created. You can modify, delete, or add to them,\nas needed. They demonstrate usage of arguments and return types using simple\necho and grep commands. The functions can be called from the dagger CLI or\nfrom one of the SDKs.\n\nThe first line in this comment block is a short description line and the\nrest is a long description with more detail on the module's purpose or usage,\nif appropriate. All modules should have a short description.\n"). WithObject( dag.TypeDef().WithObject("HarborCli"). WithFunction( @@ -214,7 +327,33 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st dag.TypeDef().WithKind(dagger.StringKind)). WithDescription("Returns lines that match a pattern in the files of the provided Directory"). WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). - WithArg("pattern", dag.TypeDef().WithKind(dagger.StringKind)))), nil + WithArg("pattern", dag.TypeDef().WithKind(dagger.StringKind))). + WithFunction( + dag.Function("LintCode", + dag.TypeDef().WithObject("Container")). + WithArg("directoryArg", dag.TypeDef().WithObject("Directory"))). + WithFunction( + dag.Function("BuildHarbor", + dag.TypeDef().WithObject("Directory")). + WithArg("directoryArg", dag.TypeDef().WithObject("Directory"))). + WithFunction( + dag.Function("PullRequest", + dag.TypeDef().WithKind(dagger.VoidKind).WithOptional(true)). + WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). + WithArg("githubToken", dag.TypeDef().WithKind(dagger.StringKind))). + WithFunction( + dag.Function("Release", + dag.TypeDef().WithKind(dagger.VoidKind).WithOptional(true)). + WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). + WithArg("githubToken", dag.TypeDef().WithKind(dagger.StringKind))). + WithFunction( + dag.Function("DockerPublish", + dag.TypeDef().WithKind(dagger.StringKind)). + WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). + WithArg("regUsername", dag.TypeDef().WithKind(dagger.StringKind)). + WithArg("regPassword", dag.TypeDef().WithObject("Secret")). + WithArg("privateKey", dag.TypeDef().WithObject("Secret")). + WithArg("password", dag.TypeDef().WithObject("Secret")))), nil default: return nil, fmt.Errorf("unknown object %s", parentName) } diff --git a/dagger.json b/dagger.json index b857c505..28a0119a 100644 --- a/dagger.json +++ b/dagger.json @@ -1,5 +1,12 @@ { "name": "harbor-cli", "sdk": "go", + "dependencies": [ + { + "name": "cosign", + "source": "github.com/scottames/daggerverse/cosign@8359122a7b90f2c8c6f3165570fdcbec6e923023" + } + ], + "source": ".", "engineVersion": "v0.12.3" } diff --git a/internal/dagger/dagger.gen.go b/internal/dagger/dagger.gen.go index f1f47c8a..0ace072d 100644 --- a/internal/dagger/dagger.gen.go +++ b/internal/dagger/dagger.gen.go @@ -128,6 +128,9 @@ type CacheVolumeID string // The `ContainerID` scalar type represents an identifier for an object of type Container. type ContainerID string +// The `CosignID` scalar type represents an identifier for an object of type Cosign. +type CosignID string + // The `CurrentModuleID` scalar type represents an identifier for an object of type CurrentModule. type CurrentModuleID string @@ -1776,6 +1779,134 @@ func (r *Container) Workdir(ctx context.Context) (string, error) { return response, q.Execute(ctx) } +// Cosign represents the cosign Dagger module type +type Cosign struct { + query *querybuilder.Selection + + id *CosignID +} + +func (r *Cosign) WithGraphQLQuery(q *querybuilder.Selection) *Cosign { + return &Cosign{ + query: q, + } +} + +// A unique identifier for this Cosign. +func (r *Cosign) ID(ctx context.Context) (CosignID, error) { + if r.id != nil { + return *r.id, nil + } + q := r.query.Select("id") + + var response CosignID + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + +// XXX_GraphQLType is an internal function. It returns the native GraphQL type name +func (r *Cosign) XXX_GraphQLType() string { + return "Cosign" +} + +// XXX_GraphQLIDType is an internal function. It returns the native GraphQL type name for the ID of this object +func (r *Cosign) XXX_GraphQLIDType() string { + return "CosignID" +} + +// XXX_GraphQLID is an internal function. It returns the underlying type ID +func (r *Cosign) XXX_GraphQLID(ctx context.Context) (string, error) { + id, err := r.ID(ctx) + if err != nil { + return "", err + } + return string(id), nil +} + +func (r *Cosign) MarshalJSON() ([]byte, error) { + id, err := r.ID(marshalCtx) + if err != nil { + return nil, err + } + return json.Marshal(id) +} +func (r *Cosign) UnmarshalJSON(bs []byte) error { + var id string + err := json.Unmarshal(bs, &id) + if err != nil { + return err + } + *r = *dag.LoadCosignFromID(CosignID(id)) + return nil +} + +// CosignSignOpts contains options for Cosign.Sign +type CosignSignOpts struct { + // + // registry username + // + RegistryUsername string + // + // name of the image + // + RegistryPassword *Secret + // + // Docker config + // + DockerConfig *File + // + // Cosign container image + // + CosignImage string + // + // Cosign container image user + // + CosignUser string +} + +// Sign will run cosign from the image, as defined by the cosignImage +// parameter, to sign the given Container image digests +// +// Note: keyless signing not supported as-is +// +// See https://edu.chainguard.dev/open-source/sigstore/cosign/an-introduction-to-cosign/ +func (r *Cosign) Sign(ctx context.Context, privateKey *Secret, password *Secret, digests []string, opts ...CosignSignOpts) ([]string, error) { + assertNotNil("privateKey", privateKey) + assertNotNil("password", password) + q := r.query.Select("sign") + for i := len(opts) - 1; i >= 0; i-- { + // `registryUsername` optional argument + if !querybuilder.IsZeroValue(opts[i].RegistryUsername) { + q = q.Arg("registryUsername", opts[i].RegistryUsername) + } + // `registryPassword` optional argument + if !querybuilder.IsZeroValue(opts[i].RegistryPassword) { + q = q.Arg("registryPassword", opts[i].RegistryPassword) + } + // `dockerConfig` optional argument + if !querybuilder.IsZeroValue(opts[i].DockerConfig) { + q = q.Arg("dockerConfig", opts[i].DockerConfig) + } + // `cosignImage` optional argument + if !querybuilder.IsZeroValue(opts[i].CosignImage) { + q = q.Arg("cosignImage", opts[i].CosignImage) + } + // `cosignUser` optional argument + if !querybuilder.IsZeroValue(opts[i].CosignUser) { + q = q.Arg("cosignUser", opts[i].CosignUser) + } + } + q = q.Arg("privateKey", privateKey) + q = q.Arg("password", password) + q = q.Arg("digests", digests) + + var response []string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // Reflective module API provided to functions at runtime. type CurrentModule struct { query *querybuilder.Selection @@ -6229,6 +6360,14 @@ func (r *Client) Container(opts ...ContainerOpts) *Container { } } +func (r *Client) Cosign() *Cosign { + q := r.query.Select("cosign") + + return &Cosign{ + query: q, + } +} + // The FunctionCall context that the SDK caller is currently executing in. // // If the caller is not currently executing in a function, this will return an error. @@ -6406,6 +6545,16 @@ func (r *Client) LoadContainerFromID(id ContainerID) *Container { } } +// Load a Cosign from its ID. +func (r *Client) LoadCosignFromID(id CosignID) *Cosign { + q := r.query.Select("loadCosignFromID") + q = q.Arg("id", id) + + return &Cosign{ + query: q, + } +} + // Load a CurrentModule from its ID. func (r *Client) LoadCurrentModuleFromID(id CurrentModuleID) *CurrentModule { q := r.query.Select("loadCurrentModuleFromID") diff --git a/main.go b/main.go index 2bd8105c..ce1d7bee 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ const ( SYFT_VERSION = "v1.9.0" GORELEASER_VERSION = "v2.1.0" APP_NAME = "dagger-harbor-cli" + PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" ) type HarborCli struct{} @@ -53,17 +54,7 @@ func (m *HarborCli) BuildHarbor(ctx context.Context, directoryArg *dagger.Direct oses := []string{"linux", "darwin", "windows"} arches := []string{"amd64", "arm64"} outputs := dag.Directory() - golangcont := dag.Container(). - From("golang:latest"). - WithMountedDirectory("/src", directoryArg). - WithWorkdir("/src"). - WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find ./cmd -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") + golangcont, main_go_path := fetchMainGoPath(ctx, directoryArg) for _, goos := range oses { for _, goarch := range arches { @@ -99,6 +90,44 @@ func (m *HarborCli) Release(ctx context.Context, directoryArg *dagger.Directory, log.Println("Release tasks completed successfully 🎉") } +func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, regUsername string, regPassword *dagger.Secret, privateKey *dagger.Secret, password *dagger.Secret) string { + +builder, main_go_path := fetchMainGoPath(ctx, directoryArg) +builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) + +// Create a minimal runtime container +runtime := dag.Container(). +From("alpine:latest"). +WithWorkdir("/root/"). +WithFile("/root/harbor", builder.File("/src/harbor")). +WithEntrypoint([]string{"./harbor"}) + +addr, _ := runtime.Publish(ctx,PUBLISH_ADDRESS) +_ , err := dag.Cosign().Sign(ctx,privateKey,password,[]string{addr},dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) +if err != nil { + panic(err) +} +fmt.Printf("Published to %s 🎉\n", addr) +return addr +} + +func fetchMainGoPath(ctx context.Context, directoryArg *dagger.Directory) (*dagger.Container, string) { + + container := dag.Container(). + From("golang:1.22-alpine"). + WithMountedDirectory("/src", directoryArg). + WithWorkdir("/src"). + WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find ./cmd -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, _ := container.File("main_go_path.txt").Contents(ctx) + trimmedPath := strings.TrimPrefix(main_go_txt_file, "./") + result := "/src/" + trimmedPath + main_go_path := strings.TrimRight(result, "\n") + + return container, main_go_path +} + func goreleaserContainer(directoryArg *dagger.Directory, githubToken string) *dagger.Container { token := dag.SetSecret("github_token", githubToken) @@ -112,5 +141,4 @@ func goreleaserContainer(directoryArg *dagger.Directory, githubToken string) *da WithMountedDirectory("/src", directoryArg).WithWorkdir("/src"). WithEnvVariable("TINI_SUBREAPER", "true"). WithSecretVariable("GITHUB_TOKEN", token) - } From 782b749a521a5c54acf62316234719b86548ae41 Mon Sep 17 00:00:00 2001 From: Bishal Das <70086051+bishal7679@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:28:22 +0530 Subject: [PATCH 08/17] Update docker_publish.yml Signed-off-by: bishal7679 --- .github/workflows/docker_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 4f9f1d3f..a84db018 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -2,7 +2,7 @@ name: Docker Publish on: push: - branches: [main] + branches: [docker-publish] pull_request: paths-ignore: - '*.md' From 0c1077f9b11be68144fed269b0e6f4d14e7adbbb Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Mon, 26 Aug 2024 22:41:46 +0530 Subject: [PATCH 09/17] updated docker publish func flag Signed-off-by: bishal7679 --- .github/workflows/docker_publish.yml | 3 +-- main.go | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index a84db018..cb91952d 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -18,7 +18,6 @@ jobs: - name: Call Docker-Publish Function uses: dagger/dagger-for-github@v6.1.0 - if: always() env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} @@ -28,4 +27,4 @@ jobs: version: "latest" verb: call module: github.com/bishal7679/harbor-cli@v0.6.2 - args: docker-publish --directory-arg=. --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} --private-key=${{ env.COSIGN_KEY }} --password=${{ env.COSIGN_PASSWORD }} + args: docker-publish --directory-arg=. --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} --private-key=${{ env.COSIGN_KEY }} --cosign-password=${{ env.COSIGN_PASSWORD }} diff --git a/main.go b/main.go index ce1d7bee..83b29921 100644 --- a/main.go +++ b/main.go @@ -90,7 +90,7 @@ func (m *HarborCli) Release(ctx context.Context, directoryArg *dagger.Directory, log.Println("Release tasks completed successfully 🎉") } -func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, regUsername string, regPassword *dagger.Secret, privateKey *dagger.Secret, password *dagger.Secret) string { +func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, regUsername string, regPassword *dagger.Secret, privateKey *dagger.Secret, cosignPassword *dagger.Secret) string { builder, main_go_path := fetchMainGoPath(ctx, directoryArg) builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) @@ -103,7 +103,7 @@ WithFile("/root/harbor", builder.File("/src/harbor")). WithEntrypoint([]string{"./harbor"}) addr, _ := runtime.Publish(ctx,PUBLISH_ADDRESS) -_ , err := dag.Cosign().Sign(ctx,privateKey,password,[]string{addr},dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) +_ , err := dag.Cosign().Sign(ctx,privateKey,cosignPassword,[]string{addr},dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) if err != nil { panic(err) } From 2393b77c5bf5fea45e23c5e61a9b61c811cc055e Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Fri, 30 Aug 2024 00:19:50 +0530 Subject: [PATCH 10/17] updated docker publish function Signed-off-by: bishal7679 --- .github/workflows/docker_publish.yml | 9 +--- dagger.gen.go | 2 +- main.go | 67 +++++++++++++++++----------- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index cb91952d..3dd3e2ba 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -18,13 +18,8 @@ jobs: - name: Call Docker-Publish Function uses: dagger/dagger-for-github@v6.1.0 - env: - REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} - COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} - COSIGN_KEY: ${{ secrets.COSIGN_KEY }} with: version: "latest" verb: call - module: github.com/bishal7679/harbor-cli@v0.6.2 - args: docker-publish --directory-arg=. --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} --private-key=${{ env.COSIGN_KEY }} --cosign-password=${{ env.COSIGN_PASSWORD }} + module: github.com/bishal7679/harbor-cli@v0.6.3 + args: docker-publish --directory-arg=. --cosign-password=${{ env.COSIGN_PASSWORD }} --cosign-key=${{ env.COSIGN_KEY }} --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} diff --git a/dagger.gen.go b/dagger.gen.go index 720d5b68..df1e2e2f 100644 --- a/dagger.gen.go +++ b/dagger.gen.go @@ -305,7 +305,7 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg password", err)) } } - return (*HarborCli).DockerPublish(&parent, ctx, directoryArg, regUsername, regPassword, privateKey, password), nil + return (*HarborCli).DockerPublish(&parent, ctx, directoryArg), nil default: return nil, fmt.Errorf("unknown function %s", fnName) } diff --git a/main.go b/main.go index 83b29921..05e1f1ec 100644 --- a/main.go +++ b/main.go @@ -5,15 +5,27 @@ import ( "fmt" "log" "strings" + "github.com/goharbor/harbor-cli/internal/dagger" ) const ( - GO_VERSION = "1.22.5" - SYFT_VERSION = "v1.9.0" + GO_VERSION = "1.22.5" + SYFT_VERSION = "v1.9.0" GORELEASER_VERSION = "v2.1.0" - APP_NAME = "dagger-harbor-cli" - PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" + APP_NAME = "dagger-harbor-cli" + PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" + key = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY----- +eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 +OCwicCI6MX0sInNhbHQiOiJsTlFISmloQ0RXdXFvYkJWUUZ5eFhON29JVmRDakJ4 +T284aVA3OXlDSGVFPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 +Iiwibm9uY2UiOiJtalNQWk5WSlYzK1ZMb2lSb1M4NDZNdmJxNitGUGp2YSJ9LCJj +aXBoZXJ0ZXh0IjoiZXF6OHdCTzlFZEU0UUVwVzU1L0FvMmNLWFFKSWxhRFNOVjB5 +dnVCY2VQV2VFRmtZd1hzb1JGTnJFL0dNRm5wM29kdVAxQlRyNW0zQ3ZNU1NoV1pu +bTRvSXkyTlZFUlZHbDdxT0E2bmlZdlhzZnBBeERjeTZZL0dsZ2lOc3ZuaWV2cW12 +dFYzS3pFNzU1RVArMUpxT3pheCtMZUs5dEdQd3VlZTU4Y0hqV29KQ05veFpQTm9r +Y0c5OTZJN0ZjNUtzbHdvbXZHQ0VRdkhBYlE9PSJ9 +-----END ENCRYPTED COSIGN PRIVATE KEY-----` ) type HarborCli struct{} @@ -90,41 +102,44 @@ func (m *HarborCli) Release(ctx context.Context, directoryArg *dagger.Directory, log.Println("Release tasks completed successfully 🎉") } -func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, regUsername string, regPassword *dagger.Secret, privateKey *dagger.Secret, cosignPassword *dagger.Secret) string { - -builder, main_go_path := fetchMainGoPath(ctx, directoryArg) -builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) +func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, cosignPasword string, cosignKey string, regUsername string, regPassword string) string { -// Create a minimal runtime container -runtime := dag.Container(). -From("alpine:latest"). -WithWorkdir("/root/"). -WithFile("/root/harbor", builder.File("/src/harbor")). -WithEntrypoint([]string{"./harbor"}) + builder, main_go_path := fetchMainGoPath(ctx, directoryArg) + builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) -addr, _ := runtime.Publish(ctx,PUBLISH_ADDRESS) -_ , err := dag.Cosign().Sign(ctx,privateKey,cosignPassword,[]string{addr},dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) -if err != nil { - panic(err) -} -fmt.Printf("Published to %s 🎉\n", addr) -return addr + // Create a minimal runtime container + runtime := dag.Container(). + From("alpine:latest"). + WithWorkdir("/root/"). + WithFile("/root/harbor", builder.File("/src/harbor")). + WithEntrypoint([]string{"./harbor"}) + + addr, _ := runtime.Publish(ctx, PUBLISH_ADDRESS) + cosign_password := dag.SetSecret("cosign_password", cosignPasword) + cosign_key := dag.SetSecret("private_key", cosignKey) + regpassword := dag.SetSecret("reg_password", regPassword) + _, err := dag.Cosign().Sign(ctx, cosign_key, cosign_password, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regpassword}) + if err != nil { + panic(err) + } + fmt.Printf("Published to %s 🎉\n", addr) + return addr } func fetchMainGoPath(ctx context.Context, directoryArg *dagger.Directory) (*dagger.Container, string) { container := dag.Container(). - From("golang:1.22-alpine"). - WithMountedDirectory("/src", directoryArg). - WithWorkdir("/src"). - WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find ./cmd -type f -name 'main.go' -print -quit) && echo $MAIN_GO_PATH > main_go_path.txt"}) + From("golang:1.22-alpine"). + WithMountedDirectory("/src", directoryArg). + WithWorkdir("/src"). + WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find ./cmd -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, _ := container.File("main_go_path.txt").Contents(ctx) trimmedPath := strings.TrimPrefix(main_go_txt_file, "./") result := "/src/" + trimmedPath main_go_path := strings.TrimRight(result, "\n") - + return container, main_go_path } From bdaaa1763320f36f6fc5f573e40918a252424135 Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Fri, 30 Aug 2024 00:23:05 +0530 Subject: [PATCH 11/17] updated docker publish function Signed-off-by: bishal7679 --- .github/workflows/docker_publish.yml | 2 +- main.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 3dd3e2ba..b3bfda3b 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -22,4 +22,4 @@ jobs: version: "latest" verb: call module: github.com/bishal7679/harbor-cli@v0.6.3 - args: docker-publish --directory-arg=. --cosign-password=${{ env.COSIGN_PASSWORD }} --cosign-key=${{ env.COSIGN_KEY }} --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} + args: docker-publish --directory-arg=. --cosign-key=${{ env.COSIGN_KEY }} --cosign-password=${{ env.COSIGN_PASSWORD }} --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} diff --git a/main.go b/main.go index 05e1f1ec..d47f9d0b 100644 --- a/main.go +++ b/main.go @@ -102,7 +102,7 @@ func (m *HarborCli) Release(ctx context.Context, directoryArg *dagger.Directory, log.Println("Release tasks completed successfully 🎉") } -func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, cosignPasword string, cosignKey string, regUsername string, regPassword string) string { +func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, cosignKey string, cosignPassword string, regUsername string, regPassword string) string { builder, main_go_path := fetchMainGoPath(ctx, directoryArg) builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) @@ -115,7 +115,7 @@ func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Dire WithEntrypoint([]string{"./harbor"}) addr, _ := runtime.Publish(ctx, PUBLISH_ADDRESS) - cosign_password := dag.SetSecret("cosign_password", cosignPasword) + cosign_password := dag.SetSecret("cosign_password", cosignPassword) cosign_key := dag.SetSecret("private_key", cosignKey) regpassword := dag.SetSecret("reg_password", regPassword) _, err := dag.Cosign().Sign(ctx, cosign_key, cosign_password, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regpassword}) From 5cd71beeafa4962653c9f82bca90b0fd320ca108 Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Fri, 30 Aug 2024 21:33:37 +0530 Subject: [PATCH 12/17] updated docker publish and github workflow Signed-off-by: bishal7679 --- .github/workflows/docker_publish.yml | 2 +- main.go | 16 ++-------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index b3bfda3b..5834e5c2 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -22,4 +22,4 @@ jobs: version: "latest" verb: call module: github.com/bishal7679/harbor-cli@v0.6.3 - args: docker-publish --directory-arg=. --cosign-key=${{ env.COSIGN_KEY }} --cosign-password=${{ env.COSIGN_PASSWORD }} --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} + args: docker-publish --directory-arg=. --cosign-key=${{ secrets.COSIGN_KEY }} --cosign-password=${{ env.COSIGN_PASSWORD }} --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} diff --git a/main.go b/main.go index d47f9d0b..8b9d50fc 100644 --- a/main.go +++ b/main.go @@ -15,17 +15,6 @@ const ( GORELEASER_VERSION = "v2.1.0" APP_NAME = "dagger-harbor-cli" PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" - key = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY----- -eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 -OCwicCI6MX0sInNhbHQiOiJsTlFISmloQ0RXdXFvYkJWUUZ5eFhON29JVmRDakJ4 -T284aVA3OXlDSGVFPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 -Iiwibm9uY2UiOiJtalNQWk5WSlYzK1ZMb2lSb1M4NDZNdmJxNitGUGp2YSJ9LCJj -aXBoZXJ0ZXh0IjoiZXF6OHdCTzlFZEU0UUVwVzU1L0FvMmNLWFFKSWxhRFNOVjB5 -dnVCY2VQV2VFRmtZd1hzb1JGTnJFL0dNRm5wM29kdVAxQlRyNW0zQ3ZNU1NoV1pu -bTRvSXkyTlZFUlZHbDdxT0E2bmlZdlhzZnBBeERjeTZZL0dsZ2lOc3ZuaWV2cW12 -dFYzS3pFNzU1RVArMUpxT3pheCtMZUs5dEdQd3VlZTU4Y0hqV29KQ05veFpQTm9r -Y0c5OTZJN0ZjNUtzbHdvbXZHQ0VRdkhBYlE9PSJ9 ------END ENCRYPTED COSIGN PRIVATE KEY-----` ) type HarborCli struct{} @@ -102,7 +91,7 @@ func (m *HarborCli) Release(ctx context.Context, directoryArg *dagger.Directory, log.Println("Release tasks completed successfully 🎉") } -func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, cosignKey string, cosignPassword string, regUsername string, regPassword string) string { +func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, cosignKey *dagger.Secret, cosignPassword string, regUsername string, regPassword string) string { builder, main_go_path := fetchMainGoPath(ctx, directoryArg) builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) @@ -116,9 +105,8 @@ func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Dire addr, _ := runtime.Publish(ctx, PUBLISH_ADDRESS) cosign_password := dag.SetSecret("cosign_password", cosignPassword) - cosign_key := dag.SetSecret("private_key", cosignKey) regpassword := dag.SetSecret("reg_password", regPassword) - _, err := dag.Cosign().Sign(ctx, cosign_key, cosign_password, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regpassword}) + _, err := dag.Cosign().Sign(ctx, cosignKey, cosign_password, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regpassword}) if err != nil { panic(err) } From 99bd328ae2c81af71eb59ad248725834ff2aa25d Mon Sep 17 00:00:00 2001 From: bishal7679 Date: Mon, 2 Sep 2024 21:33:36 +0530 Subject: [PATCH 13/17] updated release dagger function Signed-off-by: bishal7679 --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 8b9d50fc..a2450065 100644 --- a/main.go +++ b/main.go @@ -82,7 +82,7 @@ func (m *HarborCli) PullRequest(ctx context.Context, directoryArg *dagger.Direct } func (m *HarborCli) Release(ctx context.Context, directoryArg *dagger.Directory, githubToken string) { - goreleaser := goreleaserContainer(directoryArg, githubToken).WithExec([]string{"--clean"}) + goreleaser := goreleaserContainer(directoryArg, githubToken).WithExec([]string{"release", "--clean"}) _, err := goreleaser.Stderr(ctx) if err != nil { log.Printf("Error occured during release: %s", err) From 6b2e10cd5e6565711b6520dc5c181ef84bb9c422 Mon Sep 17 00:00:00 2001 From: Bishal Das <70086051+bishal7679@users.noreply.github.com> Date: Mon, 2 Sep 2024 21:49:01 +0530 Subject: [PATCH 14/17] Update docker_publish.yml --- .github/workflows/docker_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_publish.yml b/.github/workflows/docker_publish.yml index 5834e5c2..6be0df9f 100644 --- a/.github/workflows/docker_publish.yml +++ b/.github/workflows/docker_publish.yml @@ -21,5 +21,5 @@ jobs: with: version: "latest" verb: call - module: github.com/bishal7679/harbor-cli@v0.6.3 + module: github.com/bishal7679/harbor-cli@v0.6.4 args: docker-publish --directory-arg=. --cosign-key=${{ secrets.COSIGN_KEY }} --cosign-password=${{ env.COSIGN_PASSWORD }} --reg-username=${{ env.REGISTRY_USERNAME }} --reg-password=${{ env.REGISTRY_PASSWORD }} From 06807d706a25e0fdb5255fa0754081ba0b5ea37d Mon Sep 17 00:00:00 2001 From: Vadim Bauer Date: Tue, 24 Sep 2024 15:46:15 +0200 Subject: [PATCH 15/17] lint --- .gitattributes | 4 + README.md | 37 ++- dagger.gen.go | 143 +++-------- dagger.json | 2 +- go.mod | 22 +- go.sum | 44 ++-- internal/dagger/dagger.gen.go | 337 +++++++++++++++++--------- internal/querybuilder/querybuilder.go | 43 ++-- internal/telemetry/attrs.go | 3 - internal/telemetry/env.go | 3 +- internal/telemetry/init.go | 75 +++--- internal/telemetry/logging.go | 27 ++- internal/telemetry/span.go | 8 + internal/telemetry/transform.go | 87 +++---- main.go | 62 +++-- 15 files changed, 490 insertions(+), 407 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..3a454933 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +/dagger.gen.go linguist-generated +/internal/dagger/** linguist-generated +/internal/querybuilder/** linguist-generated +/internal/telemetry/** linguist-generated diff --git a/README.md b/README.md index ac2381e7..1dbc06b4 100644 --- a/README.md +++ b/README.md @@ -100,37 +100,32 @@ Windows | ✅ # Installation -## Build From Source - -```bash -git clone https://github.com/goharbor/harbor-cli.git -cd harbor-cli/cmd/harbor -go build . -sudo mv harbor /usr/local/bin/ -``` ## Linux and MacOS - use `amd64/arm64` as per your system architecture +Homebrew is the recommended way to install Harbor CLI on MacOS and Linux. -```bash -## Linux -tar -xzf harbor_0.0.1_linux_amd64.tar.gz -cd harbor_0.0.1_linux_amd64 -sudo mv harbor /usr/local/bin/ - -## MacOS -tar -xzf harbor_0.0.1_darwin_amd64.tar.gz -cd harbor_0.0.1_darwin_amd64 -sudo mv harbor /usr/local/bin/ -``` ## Windows -```bash +```shell + winget install harbor + ``` + + +# Build From Source + +Make sure you have latest [Dagger](https://docs.dagger.io/) installed in your system. + +```bash +git clone https://github.com/goharbor/harbor-cli.git +dagger call build +``` + + # Community * **Twitter:** [@project_harbor](https://twitter.com/project_harbor) diff --git a/dagger.gen.go b/dagger.gen.go index df1e2e2f..be7ecf6e 100644 --- a/dagger.gen.go +++ b/dagger.gen.go @@ -9,13 +9,14 @@ import ( "log/slog" "os" - "github.com/goharbor/harbor-cli/internal/dagger" - "github.com/goharbor/harbor-cli/internal/telemetry" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + semconv "go.opentelemetry.io/otel/semconv/v1.24.0" "go.opentelemetry.io/otel/trace" + + "github.com/goharbor/harbor-cli/internal/dagger" + "github.com/goharbor/harbor-cli/internal/querybuilder" + "github.com/goharbor/harbor-cli/internal/telemetry" ) var dag = dagger.Connect() @@ -33,7 +34,7 @@ func setMarshalContext(ctx context.Context) { dagger.SetMarshalContext(ctx) } -type DaggerObject = dagger.DaggerObject +type DaggerObject = querybuilder.GraphQLMarshaller type ExecError = dagger.ExecError @@ -143,83 +144,34 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st switch parentName { case "HarborCli": switch fnName { - case "Echo": + case "Build": var parent HarborCli err = json.Unmarshal(parentJSON, &parent) if err != nil { panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) } - var stringArg string - if inputArgs["stringArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["stringArg"]), &stringArg) + var source *dagger.Directory + if inputArgs["source"] != nil { + err = json.Unmarshal([]byte(inputArgs["source"]), &source) if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg stringArg", err)) + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg source", err)) } } - return (*HarborCli).Echo(&parent, stringArg), nil - case "ContainerEcho": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var stringArg string - if inputArgs["stringArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["stringArg"]), &stringArg) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg stringArg", err)) - } - } - return (*HarborCli).ContainerEcho(&parent, stringArg), nil - case "GrepDir": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var directoryArg *dagger.Directory - if inputArgs["directoryArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) - } - } - var pattern string - if inputArgs["pattern"] != nil { - err = json.Unmarshal([]byte(inputArgs["pattern"]), &pattern) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg pattern", err)) - } - } - return (*HarborCli).GrepDir(&parent, ctx, directoryArg, pattern) + return (*HarborCli).Build(&parent, ctx, source), nil case "LintCode": var parent HarborCli err = json.Unmarshal(parentJSON, &parent) if err != nil { panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) } - var directoryArg *dagger.Directory - if inputArgs["directoryArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) - } - } - return (*HarborCli).LintCode(&parent, ctx, directoryArg), nil - case "BuildHarbor": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var directoryArg *dagger.Directory - if inputArgs["directoryArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) + var source *dagger.Directory + if inputArgs["source"] != nil { + err = json.Unmarshal([]byte(inputArgs["source"]), &source) if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg source", err)) } } - return (*HarborCli).BuildHarbor(&parent, ctx, directoryArg), nil + return (*HarborCli).LintCode(&parent, ctx, source), nil case "PullRequest": var parent HarborCli err = json.Unmarshal(parentJSON, &parent) @@ -277,6 +229,20 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) } } + var cosignKey *dagger.Secret + if inputArgs["cosignKey"] != nil { + err = json.Unmarshal([]byte(inputArgs["cosignKey"]), &cosignKey) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg cosignKey", err)) + } + } + var cosignPassword string + if inputArgs["cosignPassword"] != nil { + err = json.Unmarshal([]byte(inputArgs["cosignPassword"]), &cosignPassword) + if err != nil { + panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg cosignPassword", err)) + } + } var regUsername string if inputArgs["regUsername"] != nil { err = json.Unmarshal([]byte(inputArgs["regUsername"]), ®Username) @@ -284,28 +250,14 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg regUsername", err)) } } - var regPassword *dagger.Secret + var regPassword string if inputArgs["regPassword"] != nil { err = json.Unmarshal([]byte(inputArgs["regPassword"]), ®Password) if err != nil { panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg regPassword", err)) } } - var privateKey *dagger.Secret - if inputArgs["privateKey"] != nil { - err = json.Unmarshal([]byte(inputArgs["privateKey"]), &privateKey) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg privateKey", err)) - } - } - var password *dagger.Secret - if inputArgs["password"] != nil { - err = json.Unmarshal([]byte(inputArgs["password"]), &password) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg password", err)) - } - } - return (*HarborCli).DockerPublish(&parent, ctx, directoryArg), nil + return (*HarborCli).DockerPublish(&parent, ctx, directoryArg, cosignKey, cosignPassword, regUsername, regPassword), nil default: return nil, fmt.Errorf("unknown function %s", fnName) } @@ -314,28 +266,13 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st WithObject( dag.TypeDef().WithObject("HarborCli"). WithFunction( - dag.Function("Echo", - dag.TypeDef().WithKind(dagger.StringKind)). - WithArg("stringArg", dag.TypeDef().WithKind(dagger.StringKind))). - WithFunction( - dag.Function("ContainerEcho", - dag.TypeDef().WithObject("Container")). - WithDescription("Returns a container that echoes whatever string argument is provided"). - WithArg("stringArg", dag.TypeDef().WithKind(dagger.StringKind))). - WithFunction( - dag.Function("GrepDir", - dag.TypeDef().WithKind(dagger.StringKind)). - WithDescription("Returns lines that match a pattern in the files of the provided Directory"). - WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). - WithArg("pattern", dag.TypeDef().WithKind(dagger.StringKind))). + dag.Function("Build", + dag.TypeDef().WithObject("Directory")). + WithArg("source", dag.TypeDef().WithObject("Directory").WithOptional(true), dagger.FunctionWithArgOpts{DefaultPath: "./"})). WithFunction( dag.Function("LintCode", dag.TypeDef().WithObject("Container")). - WithArg("directoryArg", dag.TypeDef().WithObject("Directory"))). - WithFunction( - dag.Function("BuildHarbor", - dag.TypeDef().WithObject("Directory")). - WithArg("directoryArg", dag.TypeDef().WithObject("Directory"))). + WithArg("source", dag.TypeDef().WithObject("Directory").WithOptional(true), dagger.FunctionWithArgOpts{DefaultPath: "./"})). WithFunction( dag.Function("PullRequest", dag.TypeDef().WithKind(dagger.VoidKind).WithOptional(true)). @@ -350,10 +287,10 @@ func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName st dag.Function("DockerPublish", dag.TypeDef().WithKind(dagger.StringKind)). WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). + WithArg("cosignKey", dag.TypeDef().WithObject("Secret")). + WithArg("cosignPassword", dag.TypeDef().WithKind(dagger.StringKind)). WithArg("regUsername", dag.TypeDef().WithKind(dagger.StringKind)). - WithArg("regPassword", dag.TypeDef().WithObject("Secret")). - WithArg("privateKey", dag.TypeDef().WithObject("Secret")). - WithArg("password", dag.TypeDef().WithObject("Secret")))), nil + WithArg("regPassword", dag.TypeDef().WithKind(dagger.StringKind)))), nil default: return nil, fmt.Errorf("unknown object %s", parentName) } diff --git a/dagger.json b/dagger.json index 28a0119a..1eef0255 100644 --- a/dagger.json +++ b/dagger.json @@ -8,5 +8,5 @@ } ], "source": ".", - "engineVersion": "v0.12.3" + "engineVersion": "v0.13.3" } diff --git a/go.mod b/go.mod index c530436e..5267b01a 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/sync v0.7.0 + golang.org/x/sync v0.8.0 ) require ( @@ -74,7 +74,7 @@ require ( github.com/go-openapi/validate v0.24.0 // indirect github.com/goharbor/go-client v0.210.0 github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -98,11 +98,19 @@ require ( go.opentelemetry.io/otel/sdk/log v0.4.0 go.opentelemetry.io/otel/trace v1.28.0 go.opentelemetry.io/proto/otlp v1.3.1 - golang.org/x/net v0.27.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 // indirect ) + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 + +replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.3.0 + +replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.3.0 diff --git a/go.sum b/go.sum index 0c1353d1..4dc6e2f2 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 h1:CWyXh/jylQWp2dtiV33mY4iSSp6yf4lmn+c7/tN+ObI= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0/go.mod h1:nCLIt0w3Ept2NwF8ThLmrppXsfT07oC8k0XNDxd8sVU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -182,24 +182,24 @@ go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4B go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240726164100-16a4e332762c h1:KqiIx/gBzoTWk+2aWIVE4Uct6yDk6bN8AmSohSMNVRg= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240726164100-16a4e332762c/go.mod h1:6U8l6PZAmnFSxIgHa3LRBpM9dScM77veAD+FUalfy9M= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 h1:zBPZAISA9NOc5cE8zydqDiS0itvg/P/0Hn9m72a5gvM= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0/go.mod h1:gcj2fFjEsqpV3fXuzAA+0Ze1p2/4MJ4T7d77AmkvueQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 h1:oM0GTNKGlc5qHctWeIGTVyda4iFFalOzMZ3Ehj5rwB4= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88/go.mod h1:JGG8ebaMO5nXOPnvKEl+DiA4MGwFjCbjsxT1WHIEBPY= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 h1:ccBrA8nCY5mM0y5uO7FT0ze4S0TuFcWdDB2FxGMTjkI= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0/go.mod h1:/9pb6634zi2Lk8LYg9Q0X8Ar6jka4dkFOylBLbVQPCE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= -go.opentelemetry.io/otel/log v0.4.0 h1:/vZ+3Utqh18e8TPjuc3ecg284078KWrR8BRz+PQAj3o= -go.opentelemetry.io/otel/log v0.4.0/go.mod h1:DhGnQvky7pHy82MIRV43iXh3FlKN8UUKftn0KbLOq6I= +go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs= +go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys= go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/sdk/log v0.4.0 h1:1mMI22L82zLqf6KtkjrRy5BbagOTWdJsqMY/HSqILAA= -go.opentelemetry.io/otel/sdk/log v0.4.0/go.mod h1:AYJ9FVF0hNOgAVzUG/ybg/QttnXhUePWAupmCqtdESo= +go.opentelemetry.io/otel/sdk/log v0.3.0 h1:GEjJ8iftz2l+XO1GF2856r7yYVh74URiF9JMcAacr5U= +go.opentelemetry.io/otel/sdk/log v0.3.0/go.mod h1:BwCxtmux6ACLuys1wlbc0+vGBd+xytjmjajwqqIul2g= go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= @@ -210,21 +210,21 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f h1:b1Ln/PG8orm0SsBbHZWke8dDp2lrCD4jSmfglFpTZbk= -google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f h1:RARaIm8pxYuxyNPbBQf5igT7XdOyCNtat1qAT2ZxjU4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= diff --git a/internal/dagger/dagger.gen.go b/internal/dagger/dagger.gen.go index 0ace072d..df14f551 100644 --- a/internal/dagger/dagger.gen.go +++ b/internal/dagger/dagger.gen.go @@ -49,7 +49,7 @@ func assertNotNil(argName string, value any) { } } -type DaggerObject querybuilder.GraphQLMarshaller +type DaggerObject = querybuilder.GraphQLMarshaller // getCustomError parses a GraphQL error into a more specific error type. func getCustomError(err error) error { @@ -355,6 +355,7 @@ type Container struct { stderr *string stdout *string sync *ContainerID + up *Void user *string workdir *string } @@ -819,34 +820,6 @@ func (r *Container) Mounts(ctx context.Context) ([]string, error) { return response, q.Execute(ctx) } -// ContainerPipelineOpts contains options for Container.Pipeline -type ContainerPipelineOpts struct { - // Description of the sub-pipeline. - Description string - // Labels to apply to the sub-pipeline. - Labels []PipelineLabel -} - -// Creates a named sub-pipeline. -func (r *Container) Pipeline(name string, opts ...ContainerPipelineOpts) *Container { - q := r.query.Select("pipeline") - for i := len(opts) - 1; i >= 0; i-- { - // `description` optional argument - if !querybuilder.IsZeroValue(opts[i].Description) { - q = q.Arg("description", opts[i].Description) - } - // `labels` optional argument - if !querybuilder.IsZeroValue(opts[i].Labels) { - q = q.Arg("labels", opts[i].Labels) - } - } - q = q.Arg("name", name) - - return &Container{ - query: q, - } -} - // The platform this container executes and publishes as. func (r *Container) Platform(ctx context.Context) (Platform, error) { if r.platform != nil { @@ -997,6 +970,38 @@ func (r *Container) Terminal(opts ...ContainerTerminalOpts) *Container { } } +// ContainerUpOpts contains options for Container.Up +type ContainerUpOpts struct { + // List of frontend/backend port mappings to forward. + // + // Frontend is the port accepting traffic on the host, backend is the service port. + Ports []PortForward + // Bind each tunnel port to a random port on the host. + Random bool +} + +// Starts a Service and creates a tunnel that forwards traffic from the caller's network to that service. +// +// Be sure to set any exposed ports before calling this api. +func (r *Container) Up(ctx context.Context, opts ...ContainerUpOpts) error { + if r.up != nil { + return nil + } + q := r.query.Select("up") + for i := len(opts) - 1; i >= 0; i-- { + // `ports` optional argument + if !querybuilder.IsZeroValue(opts[i].Ports) { + q = q.Arg("ports", opts[i].Ports) + } + // `random` optional argument + if !querybuilder.IsZeroValue(opts[i].Random) { + q = q.Arg("random", opts[i].Random) + } + } + + return q.Execute(ctx) +} + // Retrieves the user to be set for all commands. func (r *Container) User(ctx context.Context) (string, error) { if r.user != nil { @@ -1010,6 +1015,17 @@ func (r *Container) User(ctx context.Context) (string, error) { return response, q.Execute(ctx) } +// Retrieves this container plus the given OCI anotation. +func (r *Container) WithAnnotation(name string, value string) *Container { + q := r.query.Select("withAnnotation") + q = q.Arg("name", name) + q = q.Arg("value", value) + + return &Container{ + query: q, + } +} + // Configures default arguments for future commands. func (r *Container) WithDefaultArgs(args []string) *Container { q := r.query.Select("withDefaultArgs") @@ -1137,8 +1153,6 @@ func (r *Container) WithEnvVariable(name string, value string, opts ...Container // ContainerWithExecOpts contains options for Container.WithExec type ContainerWithExecOpts struct { - // DEPRECATED: For true this can be removed. For false, use `useEntrypoint` instead. - SkipEntrypoint bool // If the container has an entrypoint, prepend it to the args. UseEntrypoint bool // Content to write to the command's standard input before closing (e.g., "Hello world"). @@ -1159,10 +1173,6 @@ type ContainerWithExecOpts struct { func (r *Container) WithExec(args []string, opts ...ContainerWithExecOpts) *Container { q := r.query.Select("withExec") for i := len(opts) - 1; i >= 0; i-- { - // `skipEntrypoint` optional argument - if !querybuilder.IsZeroValue(opts[i].SkipEntrypoint) { - q = q.Arg("skipEntrypoint", opts[i].SkipEntrypoint) - } // `useEntrypoint` optional argument if !querybuilder.IsZeroValue(opts[i].UseEntrypoint) { q = q.Arg("useEntrypoint", opts[i].UseEntrypoint) @@ -1601,6 +1611,16 @@ func (r *Container) WithWorkdir(path string) *Container { } } +// Retrieves this container minus the given OCI annotation. +func (r *Container) WithoutAnnotation(name string) *Container { + q := r.query.Select("withoutAnnotation") + q = q.Arg("name", name) + + return &Container{ + query: q, + } +} + // Retrieves this container with unset default arguments for future commands. func (r *Container) WithoutDefaultArgs() *Container { q := r.query.Select("withoutDefaultArgs") @@ -1683,6 +1703,16 @@ func (r *Container) WithoutFile(path string) *Container { } } +// Retrieves this container with the files at the given paths removed. +func (r *Container) WithoutFiles(paths []string) *Container { + q := r.query.Select("withoutFiles") + q = q.Arg("paths", paths) + + return &Container{ + query: q, + } +} + // Indicate that subsequent operations should not be featured more prominently in the UI. // // This is the initial state of all containers. @@ -2385,6 +2415,7 @@ func (r *DaggerEngineCacheEntrySet) UnmarshalJSON(bs []byte) error { type Directory struct { query *querybuilder.Selection + digest *string export *string id *DirectoryID sync *DirectoryID @@ -2446,6 +2477,19 @@ func (r *Directory) Diff(other *Directory) *Directory { } } +// Return the directory's digest. The format of the digest is not guaranteed to be stable between releases of Dagger. It is guaranteed to be stable between invocations of the same Dagger engine. +func (r *Directory) Digest(ctx context.Context) (string, error) { + if r.digest != nil { + return *r.digest, nil + } + q := r.query.Select("digest") + + var response string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // Retrieves a directory at the given path. func (r *Directory) Directory(path string) *Directory { q := r.query.Select("directory") @@ -2621,34 +2665,6 @@ func (r *Directory) UnmarshalJSON(bs []byte) error { return nil } -// DirectoryPipelineOpts contains options for Directory.Pipeline -type DirectoryPipelineOpts struct { - // Description of the sub-pipeline. - Description string - // Labels to apply to the sub-pipeline. - Labels []PipelineLabel -} - -// Creates a named sub-pipeline. -func (r *Directory) Pipeline(name string, opts ...DirectoryPipelineOpts) *Directory { - q := r.query.Select("pipeline") - for i := len(opts) - 1; i >= 0; i-- { - // `description` optional argument - if !querybuilder.IsZeroValue(opts[i].Description) { - q = q.Arg("description", opts[i].Description) - } - // `labels` optional argument - if !querybuilder.IsZeroValue(opts[i].Labels) { - q = q.Arg("labels", opts[i].Labels) - } - } - q = q.Arg("name", name) - - return &Directory{ - query: q, - } -} - // Force evaluation in the engine. func (r *Directory) Sync(ctx context.Context) (*Directory, error) { q := r.query.Select("sync") @@ -2855,6 +2871,16 @@ func (r *Directory) WithoutFile(path string) *Directory { } } +// Retrieves this directory with the files at the given paths removed. +func (r *Directory) WithoutFiles(paths []string) *Directory { + q := r.query.Select("withoutFiles") + q = q.Arg("paths", paths) + + return &Directory{ + query: q, + } +} + // A definition of a custom enum defined in a Module. type EnumTypeDef struct { query *querybuilder.Selection @@ -3278,6 +3304,7 @@ type File struct { query *querybuilder.Selection contents *string + digest *string export *string id *FileID name *string @@ -3312,6 +3339,31 @@ func (r *File) Contents(ctx context.Context) (string, error) { return response, q.Execute(ctx) } +// FileDigestOpts contains options for File.Digest +type FileDigestOpts struct { + // If true, exclude metadata from the digest. + ExcludeMetadata bool +} + +// Return the file's digest. The format of the digest is not guaranteed to be stable between releases of Dagger. It is guaranteed to be stable between invocations of the same Dagger engine. +func (r *File) Digest(ctx context.Context, opts ...FileDigestOpts) (string, error) { + if r.digest != nil { + return *r.digest, nil + } + q := r.query.Select("digest") + for i := len(opts) - 1; i >= 0; i-- { + // `excludeMetadata` optional argument + if !querybuilder.IsZeroValue(opts[i].ExcludeMetadata) { + q = q.Arg("excludeMetadata", opts[i].ExcludeMetadata) + } + } + + var response string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // FileExportOpts contains options for File.Export type FileExportOpts struct { // If allowParentDirPath is true, the path argument can be a directory path, in which case the file will be created in that directory. @@ -3594,6 +3646,10 @@ type FunctionWithArgOpts struct { Description string // A default value to use for this argument if not explicitly set by the caller, if any DefaultValue JSON + // If the argument is a Directory or File type, default to load path from context directory, relative to root directory. + DefaultPath string + // Patterns to ignore when loading the contextual argument value. + Ignore []string } // Returns the function with the provided argument @@ -3609,6 +3665,14 @@ func (r *Function) WithArg(name string, typeDef *TypeDef, opts ...FunctionWithAr if !querybuilder.IsZeroValue(opts[i].DefaultValue) { q = q.Arg("defaultValue", opts[i].DefaultValue) } + // `defaultPath` optional argument + if !querybuilder.IsZeroValue(opts[i].DefaultPath) { + q = q.Arg("defaultPath", opts[i].DefaultPath) + } + // `ignore` optional argument + if !querybuilder.IsZeroValue(opts[i].Ignore) { + q = q.Arg("ignore", opts[i].Ignore) + } } q = q.Arg("name", name) q = q.Arg("typeDef", typeDef) @@ -3634,6 +3698,7 @@ func (r *Function) WithDescription(description string) *Function { type FunctionArg struct { query *querybuilder.Selection + defaultPath *string defaultValue *JSON description *string id *FunctionArgID @@ -3646,6 +3711,19 @@ func (r *FunctionArg) WithGraphQLQuery(q *querybuilder.Selection) *FunctionArg { } } +// Only applies to arguments of type File or Directory. If the argument is not set, load it from the given path in the context directory +func (r *FunctionArg) DefaultPath(ctx context.Context) (string, error) { + if r.defaultPath != nil { + return *r.defaultPath, nil + } + q := r.query.Select("defaultPath") + + var response string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // A default value to use for this argument when not explicitly set by the caller, if any. func (r *FunctionArg) DefaultValue(ctx context.Context) (JSON, error) { if r.defaultValue != nil { @@ -3721,6 +3799,16 @@ func (r *FunctionArg) UnmarshalJSON(bs []byte) error { return nil } +// Only applies to arguments of type Directory. The ignore patterns are applied to the input directory, and matching entries are filtered out, in a cache-efficient manner. +func (r *FunctionArg) Ignore(ctx context.Context) ([]string, error) { + q := r.query.Select("ignore") + + var response []string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // The name of the argument in lowerCamelCase format. func (r *FunctionArg) Name(ctx context.Context) (string, error) { if r.name != nil { @@ -4105,8 +4193,9 @@ func (r *GeneratedCode) WithVCSIgnoredPaths(paths []string) *GeneratedCode { type GitModuleSource struct { query *querybuilder.Selection - cloneURL *string + cloneRef *string commit *string + htmlRepoURL *string htmlURL *string id *GitModuleSourceID root *string @@ -4120,12 +4209,12 @@ func (r *GitModuleSource) WithGraphQLQuery(q *querybuilder.Selection) *GitModule } } -// The URL to clone the root of the git repo from -func (r *GitModuleSource) CloneURL(ctx context.Context) (string, error) { - if r.cloneURL != nil { - return *r.cloneURL, nil +// The ref to clone the root of the git repo from +func (r *GitModuleSource) CloneRef(ctx context.Context) (string, error) { + if r.cloneRef != nil { + return *r.cloneRef, nil } - q := r.query.Select("cloneURL") + q := r.query.Select("cloneRef") var response string @@ -4155,6 +4244,19 @@ func (r *GitModuleSource) ContextDirectory() *Directory { } } +// The URL to access the web view of the repository (e.g., GitHub, GitLab, Bitbucket) +func (r *GitModuleSource) HTMLRepoURL(ctx context.Context) (string, error) { + if r.htmlRepoURL != nil { + return *r.htmlRepoURL, nil + } + q := r.query.Select("htmlRepoURL") + + var response string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // The URL to the source's git repo in a web browser func (r *GitModuleSource) HTMLURL(ctx context.Context) (string, error) { if r.htmlURL != nil { @@ -4919,6 +5021,7 @@ type LocalModuleSource struct { query *querybuilder.Selection id *LocalModuleSourceID + relHostPath *string rootSubpath *string } @@ -4986,6 +5089,19 @@ func (r *LocalModuleSource) UnmarshalJSON(bs []byte) error { return nil } +// The relative path to the module root from the host directory +func (r *LocalModuleSource) RelHostPath(ctx context.Context) (string, error) { + if r.relHostPath != nil { + return *r.relHostPath, nil + } + q := r.query.Select("relHostPath") + + var response string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // The path to the root of the module source under the context directory. This directory contains its configuration file. It also contains its source code (possibly as a subdirectory). func (r *LocalModuleSource) RootSubpath(ctx context.Context) (string, error) { if r.rootSubpath != nil { @@ -5491,6 +5607,7 @@ type ModuleSource struct { asString *string configExists *bool + digest *string id *ModuleSourceID kind *ModuleSourceKind moduleName *string @@ -5621,6 +5738,19 @@ func (r *ModuleSource) Dependencies(ctx context.Context) ([]ModuleDependency, er return convert(response), nil } +// Return the module source's content digest. The format of the digest is not guaranteed to be stable between releases of Dagger. It is guaranteed to be stable between invocations of the same Dagger engine. +func (r *ModuleSource) Digest(ctx context.Context) (string, error) { + if r.digest != nil { + return *r.digest, nil + } + q := r.query.Select("digest") + + var response string + + q = q.Bind(&response) + return response, q.Execute(ctx) +} + // The directory containing the module configuration and source code (source code may be in a subdir). func (r *ModuleSource) Directory(path string) *Directory { q := r.query.Select("directory") @@ -5864,6 +5994,27 @@ func (r *ModuleSource) WithDependencies(dependencies []*ModuleDependency) *Modul } } +// ModuleSourceWithInitOpts contains options for ModuleSource.WithInit +type ModuleSourceWithInitOpts struct { + // Merge module dependencies into the current project's + Merge bool +} + +// Sets module init arguments +func (r *ModuleSource) WithInit(opts ...ModuleSourceWithInitOpts) *ModuleSource { + q := r.query.Select("withInit") + for i := len(opts) - 1; i >= 0; i-- { + // `merge` optional argument + if !querybuilder.IsZeroValue(opts[i].Merge) { + q = q.Arg("merge", opts[i].Merge) + } + } + + return &ModuleSource{ + query: q, + } +} + // Update the module source with a new name. func (r *ModuleSource) WithName(name string) *ModuleSource { q := r.query.Select("withName") @@ -6288,15 +6439,6 @@ func (r *Port) Protocol(ctx context.Context) (NetworkProtocol, error) { return response, q.Execute(ctx) } -type WithClientFunc func(r *Client) *Client - -// With calls the provided function with current Client. -// -// This is useful for reusability and readability by not breaking the calling chain. -func (r *Client) With(f WithClientFunc) *Client { - return f(r) -} - func (r *Client) WithGraphQLQuery(q *querybuilder.Selection) *Client { return &Client{ query: q, @@ -6941,6 +7083,8 @@ func (r *Client) ModuleDependency(source *ModuleSource, opts ...ModuleDependency type ModuleSourceOpts struct { // If true, enforce that the source is a stable version for source kinds that support versioning. Stable bool + // The relative path to the module root from the host directory + RelHostPath string } // Create a new module source instance from a source ref string. @@ -6951,6 +7095,10 @@ func (r *Client) ModuleSource(refString string, opts ...ModuleSourceOpts) *Modul if !querybuilder.IsZeroValue(opts[i].Stable) { q = q.Arg("stable", opts[i].Stable) } + // `relHostPath` optional argument + if !querybuilder.IsZeroValue(opts[i].RelHostPath) { + q = q.Arg("relHostPath", opts[i].RelHostPath) + } } q = q.Arg("refString", refString) @@ -6959,35 +7107,6 @@ func (r *Client) ModuleSource(refString string, opts ...ModuleSourceOpts) *Modul } } -// PipelineOpts contains options for Client.Pipeline -type PipelineOpts struct { - // Description of the sub-pipeline. - Description string - // Labels to apply to the sub-pipeline. - Labels []PipelineLabel -} - -// Creates a named sub-pipeline. -func (r *Client) Pipeline(name string, opts ...PipelineOpts) *Client { - q := r.query.Select("pipeline") - for i := len(opts) - 1; i >= 0; i-- { - // `description` optional argument - if !querybuilder.IsZeroValue(opts[i].Description) { - q = q.Arg("description", opts[i].Description) - } - // `labels` optional argument - if !querybuilder.IsZeroValue(opts[i].Labels) { - q = q.Arg("labels", opts[i].Labels) - } - } - q = q.Arg("name", name) - - return &Client{ - query: q, - client: r.client, - } -} - // SecretOpts contains options for Client.Secret type SecretOpts struct { Accessor string @@ -8092,7 +8211,7 @@ func getClientParams() (graphql.Client, *querybuilder.Selection) { r = r.WithContext(fallbackSpanContext(r.Context())) // propagate span context via headers (i.e. for Dagger-in-Dagger) - otel.GetTextMapPropagator().Inject(r.Context(), propagation.HeaderCarrier(r.Header)) + telemetry.Propagator.Inject(r.Context(), propagation.HeaderCarrier(r.Header)) return dialTransport.RoundTrip(r) }), @@ -8106,7 +8225,7 @@ func fallbackSpanContext(ctx context.Context) context.Context { if trace.SpanContextFromContext(ctx).IsValid() { return ctx } - return otel.GetTextMapPropagator().Extract(ctx, telemetry.NewEnvCarrier(true)) + return telemetry.Propagator.Extract(ctx, telemetry.NewEnvCarrier(true)) } // TODO: pollutes namespace, move to non internal package in dagger.io/dagger diff --git a/internal/querybuilder/querybuilder.go b/internal/querybuilder/querybuilder.go index f57b134c..2f5acc97 100644 --- a/internal/querybuilder/querybuilder.go +++ b/internal/querybuilder/querybuilder.go @@ -17,10 +17,11 @@ func Query() *Selection { } type Selection struct { - name string - alias string - args map[string]*argument - bind any + name string + alias string + args map[string]*argument + bind any + multiple bool prev *Selection @@ -52,8 +53,14 @@ func (s *Selection) SelectWithAlias(alias, name string) *Selection { return sel } -func (s *Selection) Select(name ...string) *Selection { - return s.SelectWithAlias("", strings.Join(name, " ")) +func (s *Selection) Select(name string) *Selection { + return s.SelectWithAlias("", name) +} + +func (s *Selection) SelectMultiple(name ...string) *Selection { + sel := s.SelectWithAlias("", strings.Join(name, " ")) + sel.multiple = true + return sel } func (s *Selection) Arg(name string, value any) *Selection { @@ -70,13 +77,6 @@ func (s *Selection) Arg(name string, value any) *Selection { func (s *Selection) Bind(v interface{}) *Selection { sel := *s - // When there's multiple fields, bind the parent. - if strings.Contains(sel.name, " ") { - prev := *s.prev - prev.bind = v - sel.prev = &prev - return &sel - } sel.bind = v return &sel } @@ -104,17 +104,12 @@ func (s *Selection) Build(ctx context.Context) (string, error) { b.WriteString("query") path := s.path() - multiple := false for _, sel := range path { - if multiple { + if sel.prev != nil && sel.prev.multiple { return "", fmt.Errorf("sibling selections not end of chain") } - if strings.Contains(sel.name, " ") { - multiple = true - } - b.WriteRune('{') if sel.alias != "" { @@ -151,12 +146,10 @@ func (s *Selection) unpack(data any) error { k = i.alias } - // Try to assert type of the value - switch f := data.(type) { - case map[string]any: - data = f[k] - default: - data = f + if !i.multiple { + if f, ok := data.(map[string]any); ok { + data = f[k] + } } if i.bind != nil { diff --git a/internal/telemetry/attrs.go b/internal/telemetry/attrs.go index 36d2fa20..f830f3fc 100644 --- a/internal/telemetry/attrs.go +++ b/internal/telemetry/attrs.go @@ -75,9 +75,6 @@ const ( // Indicates the units for the progress numbers. ProgressUnitsAttr = "dagger.io/progress.units" - // The client ID that generated this telemetry. - ClientIDAttr = "dagger.io/client.id" - // The stdio stream a log corresponds to (1 for stdout, 2 for stderr). StdioStreamAttr = "stdio.stream" diff --git a/internal/telemetry/env.go b/internal/telemetry/env.go index 82c3528c..3c1b23ff 100644 --- a/internal/telemetry/env.go +++ b/internal/telemetry/env.go @@ -5,13 +5,12 @@ import ( "os" "strings" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" ) func PropagationEnv(ctx context.Context) []string { carrier := NewEnvCarrier(false) - otel.GetTextMapPropagator().Inject(ctx, carrier) + Propagator.Inject(ctx, carrier) return carrier.Env } diff --git a/internal/telemetry/init.go b/internal/telemetry/init.go index 66d2e6d0..724b5dd3 100644 --- a/internal/telemetry/init.go +++ b/internal/telemetry/init.go @@ -73,8 +73,16 @@ func ConfiguredSpanExporter(ctx context.Context) (sdktrace.SpanExporter, bool) { switch proto { case "http/protobuf", "http": + headers := map[string]string{} + if hs := os.Getenv("OTEL_EXPORTER_OTLP_HEADERS"); hs != "" { + for _, header := range strings.Split(hs, ",") { + name, value, _ := strings.Cut(header, "=") + headers[name] = value + } + } configuredSpanExporter, err = otlptracehttp.New(ctx, - otlptracehttp.WithEndpointURL(endpoint)) + otlptracehttp.WithEndpointURL(endpoint), + otlptracehttp.WithHeaders(headers)) case "grpc": var u *url.URL u, err = url.Parse(endpoint) @@ -180,9 +188,9 @@ func ConfiguredLogExporter(ctx context.Context) (sdklog.Exporter, bool) { return configuredLogExporter, configuredLogExporter != nil } -// FallbackResource is the fallback resource definition. A more specific +// fallbackResource is the fallback resource definition. A more specific // resource should be set in Init. -func FallbackResource() *resource.Resource { +func fallbackResource() *resource.Resource { return resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("dagger"), @@ -192,7 +200,6 @@ func FallbackResource() *resource.Resource { var ( // set by Init, closed by Close tracerProvider *sdktrace.TracerProvider = sdktrace.NewTracerProvider() - loggerProvider *sdklog.LoggerProvider = sdklog.NewLoggerProvider() ) type Config struct { @@ -230,6 +237,7 @@ const NearlyImmediate = 100 * time.Millisecond // sent live span telemetry. var LiveTracesEnabled = os.Getenv("OTEL_EXPORTER_OTLP_TRACES_LIVE") != "" +var Resource *resource.Resource var SpanProcessors = []sdktrace.SpanProcessor{} var LogProcessors = []sdklog.Processor{} @@ -247,19 +255,29 @@ func InitEmbedded(ctx context.Context, res *resource.Resource) context.Context { return Init(ctx, traceCfg) } +// Propagator is a composite propagator of everything we could possibly want. +// +// Do not rely on otel.GetTextMapPropagator() - it's prone to change from a +// random import. +var Propagator = propagation.NewCompositeTextMapPropagator( + propagation.Baggage{}, + propagation.TraceContext{}, +) + +// closeCtx holds on to the initial context returned by Init. Close will +// extract its providers and close them. +var closeCtx context.Context + // Init sets up the global OpenTelemetry providers tracing, logging, and // someday metrics providers. It is called by the CLI, the engine, and the // container shim, so it needs to be versatile. func Init(ctx context.Context, cfg Config) context.Context { // Set up a text map propagator so that things, well, propagate. The default // is a noop. - otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( - propagation.TraceContext{}, - propagation.Baggage{}, - )) + otel.SetTextMapPropagator(Propagator) // Inherit trace context from env if present. - ctx = otel.GetTextMapPropagator().Extract(ctx, NewEnvCarrier(true)) + ctx = Propagator.Extract(ctx, NewEnvCarrier(true)) // Log to slog. otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) { @@ -267,9 +285,13 @@ func Init(ctx context.Context, cfg Config) context.Context { })) if cfg.Resource == nil { - cfg.Resource = FallbackResource() + cfg.Resource = fallbackResource() } + // Set up the global resource so we can pass it into dynamically allocated + // log/trace providers at runtime. + Resource = cfg.Resource + if cfg.Detect { if exp, ok := ConfiguredSpanExporter(ctx); ok { if LiveTracesEnabled { @@ -319,50 +341,35 @@ func Init(ctx context.Context, cfg Config) context.Context { // Set up a log provider if configured. if len(cfg.LiveLogExporters) > 0 { - logOpts := []sdklog.LoggerProviderOption{} + logOpts := []sdklog.LoggerProviderOption{ + sdklog.WithResource(cfg.Resource), + } for _, exp := range cfg.LiveLogExporters { processor := sdklog.NewBatchProcessor(exp, sdklog.WithExportInterval(NearlyImmediate)) LogProcessors = append(LogProcessors, processor) logOpts = append(logOpts, sdklog.WithProcessor(processor)) } - loggerProvider = sdklog.NewLoggerProvider(logOpts...) - - // TODO: someday do the following (once it exists) - // Register our TracerProvider as the global so any imported - // instrumentation in the future will default to using it. - // otel.SetLoggerProvider(loggerProvider) + ctx = WithLoggerProvider(ctx, sdklog.NewLoggerProvider(logOpts...)) } - return ctx -} + closeCtx = ctx -// Flush drains telemetry data, and is typically called just before a client -// goes away. -// -// NB: now that we wait for all spans to complete, this is less necessary, but -// it seems wise to keep it anyway, as the spots where it are needed are hard -// to find. -func Flush(ctx context.Context) { - if tracerProvider != nil { - if err := tracerProvider.ForceFlush(ctx); err != nil { - slog.Error("failed to flush spans", "error", err) - } - } + return ctx } // Close shuts down the global OpenTelemetry providers, flushing any remaining // data to the configured exporters. func Close() { - flushCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ctx := closeCtx + flushCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 30*time.Second) defer cancel() - Flush(flushCtx) if tracerProvider != nil { if err := tracerProvider.Shutdown(flushCtx); err != nil { slog.Error("failed to shut down tracer provider", "error", err) } } - if loggerProvider != nil { + if loggerProvider := LoggerProvider(ctx); loggerProvider != nil { if err := loggerProvider.Shutdown(flushCtx); err != nil { slog.Error("failed to shut down logger provider", "error", err) } diff --git a/internal/telemetry/logging.go b/internal/telemetry/logging.go index 29fbd423..a152a042 100644 --- a/internal/telemetry/logging.go +++ b/internal/telemetry/logging.go @@ -7,11 +7,28 @@ import ( "time" "go.opentelemetry.io/otel/log" + sdklog "go.opentelemetry.io/otel/sdk/log" ) +type loggerProviderKey struct{} + +// WithLoggerProvider returns a new context with the given LoggerProvider. +func WithLoggerProvider(ctx context.Context, provider *sdklog.LoggerProvider) context.Context { + return context.WithValue(ctx, loggerProviderKey{}, provider) +} + +// LoggerProvider returns the LoggerProvider from the context. +func LoggerProvider(ctx context.Context) *sdklog.LoggerProvider { + loggerProvider := sdklog.NewLoggerProvider() + if val := ctx.Value(loggerProviderKey{}); val != nil { + loggerProvider = val.(*sdklog.LoggerProvider) + } + return loggerProvider +} + // Logger returns a logger with the given name. -func Logger(name string) log.Logger { - return loggerProvider.Logger(name) // TODO more instrumentation attrs +func Logger(ctx context.Context, name string) log.Logger { + return LoggerProvider(ctx).Logger(name) // TODO more instrumentation attrs } // SpanStdio returns a pair of io.WriteClosers which will send log records with @@ -23,9 +40,9 @@ func Logger(name string) log.Logger { // stdout/stderr and terminates them with an EOF, to confirm that all data has // been received. It should not be used for general-purpose logging. // -// Both streamsm must be closed to ensure that draining completes. +// Both streams must be closed to ensure that draining completes. func SpanStdio(ctx context.Context, name string, attrs ...log.KeyValue) SpanStreams { - logger := Logger(name) + logger := Logger(ctx, name) return SpanStreams{ Stdout: &spanStream{ Writer: &Writer{ @@ -56,7 +73,7 @@ type Writer struct { func NewWriter(ctx context.Context, name string, attrs ...log.KeyValue) io.Writer { return &Writer{ ctx: ctx, - logger: Logger(name), + logger: Logger(ctx, name), attrs: attrs, } } diff --git a/internal/telemetry/span.go b/internal/telemetry/span.go index 35c7cadf..cfeb85b4 100644 --- a/internal/telemetry/span.go +++ b/internal/telemetry/span.go @@ -1,6 +1,8 @@ package telemetry import ( + "context" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" @@ -30,6 +32,12 @@ func Passthrough() trace.SpanStartOption { return trace.WithAttributes(attribute.Bool(UIPassthroughAttr, true)) } +// Tracer returns a Tracer for the given library using the provider from +// the current span. +func Tracer(ctx context.Context, lib string) trace.Tracer { + return trace.SpanFromContext(ctx).TracerProvider().Tracer(lib) +} + // End is a helper to end a span with an error if the function returns an error. // // It is optimized for use as a defer one-liner with a function that has a diff --git a/internal/telemetry/transform.go b/internal/telemetry/transform.go index 99c86c18..cfca401e 100644 --- a/internal/telemetry/transform.go +++ b/internal/telemetry/transform.go @@ -48,7 +48,7 @@ func LogsToPB(sdl []sdklog.Record) []*otlplogsv1.ResourceLogs { if !iOk { // Either the resource or instrumentation scope were unknown. scopeLog = &otlplogsv1.ScopeLogs{ - Scope: InstrumentationScope(sd.InstrumentationScope()), + Scope: InstrumentationScopeToPB(sd.InstrumentationScope()), LogRecords: []*otlplogsv1.LogRecord{}, SchemaUrl: sd.InstrumentationScope().SchemaURL, } @@ -61,7 +61,7 @@ func LogsToPB(sdl []sdklog.Record) []*otlplogsv1.ResourceLogs { resources++ // The resource was unknown. rs = &otlplogsv1.ResourceLogs{ - Resource: Resource(res), + Resource: ResourceToPB(res), ScopeLogs: []*otlplogsv1.ScopeLogs{scopeLog}, SchemaUrl: res.SchemaURL(), } @@ -87,7 +87,7 @@ func LogsToPB(sdl []sdklog.Record) []*otlplogsv1.ResourceLogs { return rss } -func InstrumentationScope(il instrumentation.Scope) *otlpcommonv1.InstrumentationScope { +func InstrumentationScopeToPB(il instrumentation.Scope) *otlpcommonv1.InstrumentationScope { if il == (instrumentation.Scope{}) { return nil } @@ -103,7 +103,7 @@ func logRecord(l sdklog.Record) *otlplogsv1.LogRecord { l.WalkAttributes(func(kv log.KeyValue) bool { attrs = append(attrs, &otlpcommonv1.KeyValue{ Key: kv.Key, - Value: logValueToPB(kv.Value), + Value: LogValueToPB(kv.Value), }) return true }) @@ -113,7 +113,7 @@ func logRecord(l sdklog.Record) *otlplogsv1.LogRecord { TimeUnixNano: uint64(l.Timestamp().UnixNano()), SeverityNumber: otlplogsv1.SeverityNumber(l.Severity()), SeverityText: l.SeverityText(), - Body: logValueToPB(l.Body()), + Body: LogValueToPB(l.Body()), Attributes: attrs, // DroppedAttributesCount: 0, // Flags: 0, @@ -124,13 +124,22 @@ func logRecord(l sdklog.Record) *otlplogsv1.LogRecord { return s } -// Resource transforms a Resource into an OTLP Resource. -func Resource(r resource.Resource) *otlpresourcev1.Resource { +// ResourceToPB transforms a Resource into an OTLP Resource. +func ResourceToPB(r resource.Resource) *otlpresourcev1.Resource { return &otlpresourcev1.Resource{Attributes: resourceAttributes(r)} } -// Resource transforms a Resource into an OTLP Resource. -func ResourcePtr(r *resource.Resource) *otlpresourcev1.Resource { +// ResourceFromPB creates a *resource.Resource from a schema URL and +// protobuf encoded attributes. +func ResourceFromPB(schemaURL string, pb *otlpresourcev1.Resource) *resource.Resource { + if schemaURL == "" { + return resource.NewSchemaless(AttributesFromProto(pb.Attributes)...) + } + return resource.NewWithAttributes(schemaURL, AttributesFromProto(pb.Attributes)...) +} + +// ResourcePtrToPB transforms a *Resource into an OTLP Resource. +func ResourcePtrToPB(r *resource.Resource) *otlpresourcev1.Resource { if r == nil { return nil } @@ -325,7 +334,7 @@ func SpansToPB(sdl []sdktrace.ReadOnlySpan) []*otlptracev1.ResourceSpans { if !iOk { // Either the resource or instrumentation scope were unknown. scopeSpan = &otlptracev1.ScopeSpans{ - Scope: InstrumentationScope(sd.InstrumentationScope()), + Scope: InstrumentationScopeToPB(sd.InstrumentationScope()), Spans: []*otlptracev1.Span{}, SchemaUrl: sd.InstrumentationScope().SchemaURL, } @@ -338,7 +347,7 @@ func SpansToPB(sdl []sdktrace.ReadOnlySpan) []*otlptracev1.ResourceSpans { resources++ // The resource was unknown. rs = &otlptracev1.ResourceSpans{ - Resource: ResourcePtr(sd.Resource()), + Resource: ResourcePtrToPB(sd.Resource()), ScopeSpans: []*otlptracev1.ScopeSpans{scopeSpan}, SchemaUrl: sd.Resource().SchemaURL(), } @@ -380,11 +389,11 @@ func spanToPB(sd sdktrace.ReadOnlySpan) *otlptracev1.Span { Status: status(sd.Status().Code, sd.Status().Description), StartTimeUnixNano: uint64(sd.StartTime().UnixNano()), EndTimeUnixNano: uint64(sd.EndTime().UnixNano()), - Links: linksToPB(sd.Links()), + Links: SpanLinksToPB(sd.Links()), Kind: spanKindToPB(sd.SpanKind()), Name: sd.Name(), Attributes: KeyValues(sd.Attributes()), - Events: spanEventsToPB(sd.Events()), + Events: SpanEventsToPB(sd.Events()), DroppedAttributesCount: uint32(sd.DroppedAttributes()), DroppedEventsCount: uint32(sd.DroppedEvents()), DroppedLinksCount: uint32(sd.DroppedLinks()), @@ -429,7 +438,7 @@ func KeyValues(attrs []attribute.KeyValue) []*otlpcommonv1.KeyValue { } // linksFromPB transforms span Links to OTLP span linksFromPB. -func linksToPB(links []sdktrace.Link) []*otlptracev1.Span_Link { +func SpanLinksToPB(links []sdktrace.Link) []*otlptracev1.Span_Link { if len(links) == 0 { return nil } @@ -465,8 +474,8 @@ func buildSpanFlags(sc trace.SpanContext) uint32 { return uint32(flags) } -// spanEventsToPB transforms span Events to an OTLP span events. -func spanEventsToPB(es []sdktrace.Event) []*otlptracev1.Span_Event { +// SpanEventsToPB transforms span Events to an OTLP span events. +func SpanEventsToPB(es []sdktrace.Event) []*otlptracev1.Span_Event { if len(es) == 0 { return nil } @@ -563,22 +572,22 @@ func (s *readOnlySpan) Attributes() []attribute.KeyValue { } func (s *readOnlySpan) Links() []sdktrace.Link { - return linksFromPB(s.pb.Links) + return SpanLinksFromPB(s.pb.Links) } func (s *readOnlySpan) Events() []sdktrace.Event { - return spanEventsFromPB(s.pb.Events) + return SpanEventsFromPB(s.pb.Events) } func (s *readOnlySpan) Status() sdktrace.Status { return sdktrace.Status{ - Code: statusCode(s.pb.Status), + Code: StatusCodeFromPB(s.pb.Status), Description: s.pb.Status.GetMessage(), } } func (s *readOnlySpan) InstrumentationScope() instrumentation.Scope { - return instrumentationScope(s.is) + return InstrumentationScopeFromPB(s.is) } // Deprecated: use InstrumentationScope. @@ -588,13 +597,7 @@ func (s *readOnlySpan) InstrumentationLibrary() instrumentation.Library { // Resource returns information about the entity that produced the span. func (s *readOnlySpan) Resource() *resource.Resource { - if s.resource == nil { - return nil - } - if s.schemaURL != "" { - return resource.NewWithAttributes(s.schemaURL, AttributesFromProto(s.resource.Attributes)...) - } - return resource.NewSchemaless(AttributesFromProto(s.resource.Attributes)...) + return ResourceFromPB(s.schemaURL, s.resource) } // DroppedAttributes returns the number of attributes dropped by the span @@ -624,7 +627,7 @@ func (s *readOnlySpan) ChildSpanCount() int { var _ sdktrace.ReadOnlySpan = &readOnlySpan{} // status transform a OTLP span status into span code. -func statusCode(st *otlptracev1.Status) codes.Code { +func StatusCodeFromPB(st *otlptracev1.Status) codes.Code { if st == nil { return codes.Unset } @@ -636,8 +639,8 @@ func statusCode(st *otlptracev1.Status) codes.Code { } } -// linksFromPB transforms OTLP span links to span Links. -func linksFromPB(links []*otlptracev1.Span_Link) []sdktrace.Link { +// SpanLinksFromPB transforms OTLP span links to span Links. +func SpanLinksFromPB(links []*otlptracev1.Span_Link) []sdktrace.Link { if len(links) == 0 { return nil } @@ -669,8 +672,8 @@ func linksFromPB(links []*otlptracev1.Span_Link) []sdktrace.Link { return sl } -// spanEventsFromPB transforms OTLP span events to span Events. -func spanEventsFromPB(es []*otlptracev1.Span_Event) []sdktrace.Event { +// SpanEventsFromPB transforms OTLP span events to span Events. +func SpanEventsFromPB(es []*otlptracev1.Span_Event) []sdktrace.Event { if len(es) == 0 { return nil } @@ -814,7 +817,7 @@ func anyArrayToAttrValue(anyVals []*otlpcommonv1.AnyValue) attribute.Value { } } -func instrumentationScope(is *otlpcommonv1.InstrumentationScope) instrumentation.Scope { +func InstrumentationScopeFromPB(is *otlpcommonv1.InstrumentationScope) instrumentation.Scope { if is == nil { return instrumentation.Scope{} } @@ -833,11 +836,11 @@ func LogsFromPB(resLogs []*otlplogsv1.ResourceLogs) []sdklog.Record { logRec.SetTraceID(trace.TraceID(rec.GetTraceId())) logRec.SetSpanID(trace.SpanID(rec.GetSpanId())) logRec.SetTimestamp(time.Unix(0, int64(rec.GetTimeUnixNano()))) - logRec.SetBody(logValueFromPB(rec.GetBody())) + logRec.SetBody(LogValueFromPB(rec.GetBody())) logRec.SetSeverity(log.Severity(rec.GetSeverityNumber())) logRec.SetSeverityText(rec.GetSeverityText()) logRec.SetObservedTimestamp(time.Unix(0, int64(rec.GetObservedTimeUnixNano()))) - logRec.SetAttributes(logKVs(rec.GetAttributes())...) + logRec.SetAttributes(LogKeyValuesFromPB(rec.GetAttributes())...) logs = append(logs, logRec) } } @@ -845,7 +848,7 @@ func LogsFromPB(resLogs []*otlplogsv1.ResourceLogs) []sdklog.Record { return logs } -func logKVs(kvs []*otlpcommonv1.KeyValue) []log.KeyValue { +func LogKeyValuesFromPB(kvs []*otlpcommonv1.KeyValue) []log.KeyValue { res := make([]log.KeyValue, len(kvs)) for i, kv := range kvs { res[i] = logKeyValue(kv) @@ -856,7 +859,7 @@ func logKVs(kvs []*otlpcommonv1.KeyValue) []log.KeyValue { func logKeyValue(v *otlpcommonv1.KeyValue) log.KeyValue { return log.KeyValue{ Key: v.GetKey(), - Value: logValueFromPB(v.GetValue()), + Value: LogValueFromPB(v.GetValue()), } } @@ -880,7 +883,7 @@ func attrValue(v *otlpcommonv1.AnyValue) attribute.Value { } } -func logValueFromPB(v *otlpcommonv1.AnyValue) log.Value { +func LogValueFromPB(v *otlpcommonv1.AnyValue) log.Value { switch x := v.Value.(type) { case *otlpcommonv1.AnyValue_StringValue: return log.StringValue(v.GetStringValue()) @@ -899,7 +902,7 @@ func logValueFromPB(v *otlpcommonv1.AnyValue) log.Value { case *otlpcommonv1.AnyValue_ArrayValue: vals := make([]log.Value, 0, len(x.ArrayValue.GetValues())) for _, v := range x.ArrayValue.GetValues() { - vals = append(vals, logValueFromPB(v)) + vals = append(vals, LogValueFromPB(v)) } return log.SliceValue(vals...) case *otlpcommonv1.AnyValue_BytesValue: @@ -911,7 +914,7 @@ func logValueFromPB(v *otlpcommonv1.AnyValue) log.Value { } // Value transforms an attribute Value into an OTLP AnyValue. -func logValueToPB(v log.Value) *otlpcommonv1.AnyValue { +func LogValueToPB(v log.Value) *otlpcommonv1.AnyValue { av := new(otlpcommonv1.AnyValue) switch v.Kind() { case log.KindBool: @@ -933,7 +936,7 @@ func logValueToPB(v log.Value) *otlpcommonv1.AnyValue { case log.KindSlice: array := &otlpcommonv1.ArrayValue{} for _, e := range v.AsSlice() { - array.Values = append(array.Values, logValueToPB(e)) + array.Values = append(array.Values, LogValueToPB(e)) } av.Value = &otlpcommonv1.AnyValue_ArrayValue{ ArrayValue: array, @@ -943,7 +946,7 @@ func logValueToPB(v log.Value) *otlpcommonv1.AnyValue { for _, e := range v.AsMap() { kvList.Values = append(kvList.Values, &otlpcommonv1.KeyValue{ Key: e.Key, - Value: logValueToPB(e.Value), + Value: LogValueToPB(e.Value), }) } av.Value = &otlpcommonv1.AnyValue_KvlistValue{ diff --git a/main.go b/main.go index 7b6d223f..ae3a0908 100644 --- a/main.go +++ b/main.go @@ -19,51 +19,28 @@ const ( type HarborCli struct{} -func (m *HarborCli) Echo(stringArg string) string { - return stringArg -} - -// Returns a container that echoes whatever string argument is provided -func (m *HarborCli) ContainerEcho(stringArg string) *dagger.Container { - return dag.Container().From("alpine:latest").WithExec([]string{"echo", stringArg}) - -} - -// Returns lines that match a pattern in the files of the provided Directory -func (m *HarborCli) GrepDir(ctx context.Context, directoryArg *dagger.Directory, pattern string) (string, error) { - return dag.Container(). - From("alpine:latest"). - WithMountedDirectory("/mnt", directoryArg). - WithWorkdir("/mnt"). - WithExec([]string{"grep", "-R", pattern, "."}). - Stdout(ctx) +func (m *HarborCli) Build( + ctx context.Context, + // +optional + // +defaultPath="./" + source *dagger.Directory) *dagger.Directory { -} - -func (m *HarborCli) LintCode(ctx context.Context, directoryArg *dagger.Directory) *dagger.Container { - fmt.Println("👀 Running linter with Dagger...") - return dag.Container(). - From("golangci/golangci-lint:v1.59.1-alpine"). - WithMountedDirectory("/src", directoryArg). - WithWorkdir("/src"). - WithExec([]string{"golangci-lint", "run", "--timeout", "5m"}) - -} - -func (m *HarborCli) BuildHarbor(ctx context.Context, directoryArg *dagger.Directory) *dagger.Directory { fmt.Println("🛠️ Building with Dagger...") oses := []string{"linux", "darwin", "windows"} arches := []string{"amd64", "arm64"} outputs := dag.Directory() - golangcont, main_go_path := fetchMainGoPath(ctx, directoryArg) + golangcont, main_go_path := fetchMainGoPath(ctx, source) for _, goos := range oses { for _, goarch := range arches { path := fmt.Sprintf("build/%s/%s/", goos, goarch) build := golangcont.WithEnvVariable("GOOS", goos). + WithMountedCache("/go/pkg/mod", dag.CacheVolume("go-mod-"+GO_VERSION)). + WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). + WithMountedCache("/go/build-cache", dag.CacheVolume("go-build-"+GO_VERSION)). + WithEnvVariable("GOCACHE", "/go/build-cache"). WithEnvVariable("GOARCH", goarch). WithExec([]string{"go", "build", "-o", path + "harbor", main_go_path}) - // Get reference to build output directory in container outputs = outputs.WithDirectory(path, build.Directory(path)) } @@ -71,6 +48,25 @@ func (m *HarborCli) BuildHarbor(ctx context.Context, directoryArg *dagger.Direct return outputs } +func (m *HarborCli) LintCode( + ctx context.Context, + // +optional + // +defaultPath="./" + source *dagger.Directory, +) *dagger.Container { + fmt.Println("👀 Running linter with Dagger...") + return dag.Container(). + From("golangci/golangci-lint:v1.59.1-alpine"). + WithMountedCache("/go/pkg/mod", dag.CacheVolume("go-mod-"+GO_VERSION)). + WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). + WithMountedCache("/go/build-cache", dag.CacheVolume("go-build-"+GO_VERSION)). + WithEnvVariable("GOCACHE", "/go/build-cache"). + WithMountedDirectory("/src", source). + WithWorkdir("/src"). + WithExec([]string{"golangci-lint", "run", "--timeout", "5m"}) + +} + func (m *HarborCli) PullRequest(ctx context.Context, directoryArg *dagger.Directory, githubToken string) { goreleaser := goreleaserContainer(directoryArg, githubToken).WithExec([]string{"release", "--snapshot", "--clean"}) _, err := goreleaser.Stderr(ctx) From 62d7055108f3d4bd6d2916d18d784a1481a375a2 Mon Sep 17 00:00:00 2001 From: Vadim Bauer Date: Tue, 24 Sep 2024 16:12:27 +0200 Subject: [PATCH 16/17] simplified some flows TODO, change PublishImage to support multi arch images --- comments.patch | 177 +++++++++++++++++++++++++++++ dagger.gen.go | 297 ------------------------------------------------- main.go | 62 +++++------ 3 files changed, 203 insertions(+), 333 deletions(-) create mode 100644 comments.patch delete mode 100644 dagger.gen.go diff --git a/comments.patch b/comments.patch new file mode 100644 index 00000000..413ed6a2 --- /dev/null +++ b/comments.patch @@ -0,0 +1,177 @@ +Subject: [PATCH] comments +--- +Index: main.go +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/main.go b/main.go +--- a/main.go (revision 96fa3fa98fb6ceb760e89eb10f1704e753b888ed) ++++ b/main.go (date 1724857880146) +@@ -3,17 +3,16 @@ + import ( + "context" + "fmt" +- "log" +- "strings" + "github.com/goharbor/harbor-cli/internal/dagger" ++ "log" + ) + + const ( +- GO_VERSION = "1.22.5" +- SYFT_VERSION = "v1.9.0" ++ GO_VERSION = "1.22.5" ++ SYFT_VERSION = "v1.9.0" + GORELEASER_VERSION = "v2.1.0" +- APP_NAME = "dagger-harbor-cli" +- PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" ++ APP_NAME = "dagger-harbor-cli" ++ PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" + ) + + type HarborCli struct{} +@@ -46,25 +45,31 @@ + WithMountedDirectory("/src", directoryArg). + WithWorkdir("/src"). + WithExec([]string{"golangci-lint", "run", "--timeout", "5m"}) +- + } + +-func (m *HarborCli) BuildHarbor(ctx context.Context, directoryArg *dagger.Directory) *dagger.Directory { ++// Builds the Harbor CLI for multiple OS and architectures ++func (m *HarborCli) Build(ctx context.Context, source *dagger.Directory) *dagger.Directory { ++ + fmt.Println("🛠️ Building with Dagger...") + oses := []string{"linux", "darwin", "windows"} + arches := []string{"amd64", "arm64"} + outputs := dag.Directory() +- golangcont, main_go_path := fetchMainGoPath(ctx, directoryArg) +- + for _, goos := range oses { + for _, goarch := range arches { +- path := fmt.Sprintf("build/%s/%s/", goos, goarch) +- build := golangcont.WithEnvVariable("GOOS", goos). ++ bin_path := fmt.Sprintf("build/%s/%s/", goos, goarch) ++ builder := dag.Container(). ++ From("golang:1.22-alpine"). ++ WithMountedDirectory("/src", source). ++ WithWorkdir("/src"). ++ WithMountedCache("/go/pkg/mod", dag.CacheVolume("go-mod-122-5")). ++ WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). ++ WithMountedCache("/go/build-cache", dag.CacheVolume("go-build-122-5")). ++ WithEnvVariable("GOCACHE", "/go/build-cache"). ++ WithEnvVariable("GOOS", goos). + WithEnvVariable("GOARCH", goarch). +- WithExec([]string{"go", "build", "-o", path + "harbor", main_go_path}) +- ++ WithExec([]string{"go", "build", "-o", bin_path + "harbor", "/src/cmd/harbor/main.go"}) + // Get reference to build output directory in container +- outputs = outputs.WithDirectory(path, build.Directory(path)) ++ outputs = outputs.WithDirectory(bin_path, builder.Directory(bin_path)) + } + } + return outputs +@@ -90,44 +95,26 @@ + log.Println("Release tasks completed successfully 🎉") + } + +-func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, regUsername string, regPassword *dagger.Secret, privateKey *dagger.Secret, cosignPassword *dagger.Secret) string { ++func (m *HarborCli) DockerPublish(ctx context.Context, source *dagger.Directory) string { + +-builder, main_go_path := fetchMainGoPath(ctx, directoryArg) +-builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) ++ builder := m.Build(ctx, source) + +-// Create a minimal runtime container +-runtime := dag.Container(). +-From("alpine:latest"). +-WithWorkdir("/root/"). +-WithFile("/root/harbor", builder.File("/src/harbor")). +-WithEntrypoint([]string{"./harbor"}) ++ // Create a minimal runtime container ++ runtime := dag.Container(). ++ From("alpine:latest"). ++ WithWorkdir("/root/"). ++ WithFile("/root/harbor", builder.File("/src/harbor")). ++ WithEntrypoint([]string{"./harbor"}) + +-addr, _ := runtime.Publish(ctx,PUBLISH_ADDRESS) +-_ , err := dag.Cosign().Sign(ctx,privateKey,cosignPassword,[]string{addr},dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) +-if err != nil { +- panic(err) +-} +-fmt.Printf("Published to %s 🎉\n", addr) +-return addr ++ addr, _ := runtime.Publish(ctx, PUBLISH_ADDRESS) ++ //_, err := dag.Cosign().Sign(ctx, privateKey, cosignPassword, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) ++ //if err != nil { ++ // panic(err) ++ //} ++ fmt.Printf("Published to %s 🎉\n", addr) ++ return addr + } + +-func fetchMainGoPath(ctx context.Context, directoryArg *dagger.Directory) (*dagger.Container, string) { +- +- container := dag.Container(). +- From("golang:1.22-alpine"). +- WithMountedDirectory("/src", directoryArg). +- WithWorkdir("/src"). +- WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find ./cmd -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, _ := container.File("main_go_path.txt").Contents(ctx) +- trimmedPath := strings.TrimPrefix(main_go_txt_file, "./") +- result := "/src/" + trimmedPath +- main_go_path := strings.TrimRight(result, "\n") +- +- return container, main_go_path +-} +- + func goreleaserContainer(directoryArg *dagger.Directory, githubToken string) *dagger.Container { + token := dag.SetSecret("github_token", githubToken) + +Index: dagger.json +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/dagger.json b/dagger.json +--- a/dagger.json (revision 96fa3fa98fb6ceb760e89eb10f1704e753b888ed) ++++ b/dagger.json (date 1724762958963) +@@ -8,5 +8,5 @@ + } + ], + "source": ".", +- "engineVersion": "v0.12.3" ++ "engineVersion": "v0.12.5" + } +Index: README.md +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/README.md b/README.md +--- a/README.md (revision 96fa3fa98fb6ceb760e89eb10f1704e753b888ed) ++++ b/README.md (date 1724851803391) +@@ -102,11 +102,16 @@ + + ## Build From Source + ++For building the Harbor CLI locally, we recommend using [Dagger](https://docs.dagger.io/). ++Make sure you have also a container runtime like Docker or Podman installed on your system. ++ + ```bash + git clone https://github.com/goharbor/harbor-cli.git +-cd harbor-cli/cmd/harbor +-go build . +-sudo mv harbor /usr/local/bin/ ++dagger functions # to see all existing operations without looking into code ++dagger call build --source=. # to create a harbor cli binary ++ls -la build/ # to see the binary for your platform ++ ++ + ``` + + ## Linux and MacOS diff --git a/dagger.gen.go b/dagger.gen.go deleted file mode 100644 index be7ecf6e..00000000 --- a/dagger.gen.go +++ /dev/null @@ -1,297 +0,0 @@ -// Code generated by dagger. DO NOT EDIT. - -package main - -import ( - "context" - "encoding/json" - "fmt" - "log/slog" - "os" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.24.0" - "go.opentelemetry.io/otel/trace" - - "github.com/goharbor/harbor-cli/internal/dagger" - "github.com/goharbor/harbor-cli/internal/querybuilder" - "github.com/goharbor/harbor-cli/internal/telemetry" -) - -var dag = dagger.Connect() - -func Tracer() trace.Tracer { - return otel.Tracer("dagger.io/sdk.go") -} - -// used for local MarshalJSON implementations -var marshalCtx = context.Background() - -// called by main() -func setMarshalContext(ctx context.Context) { - marshalCtx = ctx - dagger.SetMarshalContext(ctx) -} - -type DaggerObject = querybuilder.GraphQLMarshaller - -type ExecError = dagger.ExecError - -// ptr returns a pointer to the given value. -func ptr[T any](v T) *T { - return &v -} - -// convertSlice converts a slice of one type to a slice of another type using a -// converter function -func convertSlice[I any, O any](in []I, f func(I) O) []O { - out := make([]O, len(in)) - for i, v := range in { - out[i] = f(v) - } - return out -} - -func (r HarborCli) MarshalJSON() ([]byte, error) { - var concrete struct{} - return json.Marshal(&concrete) -} - -func (r *HarborCli) UnmarshalJSON(bs []byte) error { - var concrete struct{} - err := json.Unmarshal(bs, &concrete) - if err != nil { - return err - } - return nil -} - -func main() { - ctx := context.Background() - - // Direct slog to the new stderr. This is only for dev time debugging, and - // runtime errors/warnings. - slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - Level: slog.LevelWarn, - }))) - - if err := dispatch(ctx); err != nil { - fmt.Println(err.Error()) - os.Exit(2) - } -} - -func dispatch(ctx context.Context) error { - ctx = telemetry.InitEmbedded(ctx, resource.NewWithAttributes( - semconv.SchemaURL, - semconv.ServiceNameKey.String("dagger-go-sdk"), - // TODO version? - )) - defer telemetry.Close() - - // A lot of the "work" actually happens when we're marshalling the return - // value, which entails getting object IDs, which happens in MarshalJSON, - // which has no ctx argument, so we use this lovely global variable. - setMarshalContext(ctx) - - fnCall := dag.CurrentFunctionCall() - parentName, err := fnCall.ParentName(ctx) - if err != nil { - return fmt.Errorf("get parent name: %w", err) - } - fnName, err := fnCall.Name(ctx) - if err != nil { - return fmt.Errorf("get fn name: %w", err) - } - parentJson, err := fnCall.Parent(ctx) - if err != nil { - return fmt.Errorf("get fn parent: %w", err) - } - fnArgs, err := fnCall.InputArgs(ctx) - if err != nil { - return fmt.Errorf("get fn args: %w", err) - } - - inputArgs := map[string][]byte{} - for _, fnArg := range fnArgs { - argName, err := fnArg.Name(ctx) - if err != nil { - return fmt.Errorf("get fn arg name: %w", err) - } - argValue, err := fnArg.Value(ctx) - if err != nil { - return fmt.Errorf("get fn arg value: %w", err) - } - inputArgs[argName] = []byte(argValue) - } - - result, err := invoke(ctx, []byte(parentJson), parentName, fnName, inputArgs) - if err != nil { - return fmt.Errorf("invoke: %w", err) - } - resultBytes, err := json.Marshal(result) - if err != nil { - return fmt.Errorf("marshal: %w", err) - } - if err = fnCall.ReturnValue(ctx, dagger.JSON(resultBytes)); err != nil { - return fmt.Errorf("store return value: %w", err) - } - return nil -} -func invoke(ctx context.Context, parentJSON []byte, parentName string, fnName string, inputArgs map[string][]byte) (_ any, err error) { - _ = inputArgs - switch parentName { - case "HarborCli": - switch fnName { - case "Build": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var source *dagger.Directory - if inputArgs["source"] != nil { - err = json.Unmarshal([]byte(inputArgs["source"]), &source) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg source", err)) - } - } - return (*HarborCli).Build(&parent, ctx, source), nil - case "LintCode": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var source *dagger.Directory - if inputArgs["source"] != nil { - err = json.Unmarshal([]byte(inputArgs["source"]), &source) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg source", err)) - } - } - return (*HarborCli).LintCode(&parent, ctx, source), nil - case "PullRequest": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var directoryArg *dagger.Directory - if inputArgs["directoryArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) - } - } - var githubToken string - if inputArgs["githubToken"] != nil { - err = json.Unmarshal([]byte(inputArgs["githubToken"]), &githubToken) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg githubToken", err)) - } - } - (*HarborCli).PullRequest(&parent, ctx, directoryArg, githubToken) - return nil, nil - case "Release": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var directoryArg *dagger.Directory - if inputArgs["directoryArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) - } - } - var githubToken string - if inputArgs["githubToken"] != nil { - err = json.Unmarshal([]byte(inputArgs["githubToken"]), &githubToken) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg githubToken", err)) - } - } - (*HarborCli).Release(&parent, ctx, directoryArg, githubToken) - return nil, nil - case "DockerPublish": - var parent HarborCli - err = json.Unmarshal(parentJSON, &parent) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal parent object", err)) - } - var directoryArg *dagger.Directory - if inputArgs["directoryArg"] != nil { - err = json.Unmarshal([]byte(inputArgs["directoryArg"]), &directoryArg) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg directoryArg", err)) - } - } - var cosignKey *dagger.Secret - if inputArgs["cosignKey"] != nil { - err = json.Unmarshal([]byte(inputArgs["cosignKey"]), &cosignKey) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg cosignKey", err)) - } - } - var cosignPassword string - if inputArgs["cosignPassword"] != nil { - err = json.Unmarshal([]byte(inputArgs["cosignPassword"]), &cosignPassword) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg cosignPassword", err)) - } - } - var regUsername string - if inputArgs["regUsername"] != nil { - err = json.Unmarshal([]byte(inputArgs["regUsername"]), ®Username) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg regUsername", err)) - } - } - var regPassword string - if inputArgs["regPassword"] != nil { - err = json.Unmarshal([]byte(inputArgs["regPassword"]), ®Password) - if err != nil { - panic(fmt.Errorf("%s: %w", "failed to unmarshal input arg regPassword", err)) - } - } - return (*HarborCli).DockerPublish(&parent, ctx, directoryArg, cosignKey, cosignPassword, regUsername, regPassword), nil - default: - return nil, fmt.Errorf("unknown function %s", fnName) - } - case "": - return dag.Module(). - WithObject( - dag.TypeDef().WithObject("HarborCli"). - WithFunction( - dag.Function("Build", - dag.TypeDef().WithObject("Directory")). - WithArg("source", dag.TypeDef().WithObject("Directory").WithOptional(true), dagger.FunctionWithArgOpts{DefaultPath: "./"})). - WithFunction( - dag.Function("LintCode", - dag.TypeDef().WithObject("Container")). - WithArg("source", dag.TypeDef().WithObject("Directory").WithOptional(true), dagger.FunctionWithArgOpts{DefaultPath: "./"})). - WithFunction( - dag.Function("PullRequest", - dag.TypeDef().WithKind(dagger.VoidKind).WithOptional(true)). - WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). - WithArg("githubToken", dag.TypeDef().WithKind(dagger.StringKind))). - WithFunction( - dag.Function("Release", - dag.TypeDef().WithKind(dagger.VoidKind).WithOptional(true)). - WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). - WithArg("githubToken", dag.TypeDef().WithKind(dagger.StringKind))). - WithFunction( - dag.Function("DockerPublish", - dag.TypeDef().WithKind(dagger.StringKind)). - WithArg("directoryArg", dag.TypeDef().WithObject("Directory")). - WithArg("cosignKey", dag.TypeDef().WithObject("Secret")). - WithArg("cosignPassword", dag.TypeDef().WithKind(dagger.StringKind)). - WithArg("regUsername", dag.TypeDef().WithKind(dagger.StringKind)). - WithArg("regPassword", dag.TypeDef().WithKind(dagger.StringKind)))), nil - default: - return nil, fmt.Errorf("unknown object %s", parentName) - } -} diff --git a/main.go b/main.go index ae3a0908..1a8a7677 100644 --- a/main.go +++ b/main.go @@ -3,10 +3,8 @@ package main import ( "context" "fmt" - "log" - "strings" - "github.com/goharbor/harbor-cli/internal/dagger" + "log" ) const ( @@ -29,26 +27,28 @@ func (m *HarborCli) Build( oses := []string{"linux", "darwin", "windows"} arches := []string{"amd64", "arm64"} outputs := dag.Directory() - golangcont, main_go_path := fetchMainGoPath(ctx, source) - for _, goos := range oses { for _, goarch := range arches { - path := fmt.Sprintf("build/%s/%s/", goos, goarch) - build := golangcont.WithEnvVariable("GOOS", goos). + bin_path := fmt.Sprintf("build/%s/%s/", goos, goarch) + builder := dag.Container(). + From("golang:"+GO_VERSION+"-alpine"). + WithMountedDirectory("/src", source). + WithWorkdir("/src"). WithMountedCache("/go/pkg/mod", dag.CacheVolume("go-mod-"+GO_VERSION)). WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). WithMountedCache("/go/build-cache", dag.CacheVolume("go-build-"+GO_VERSION)). WithEnvVariable("GOCACHE", "/go/build-cache"). + WithEnvVariable("GOOS", goos). WithEnvVariable("GOARCH", goarch). - WithExec([]string{"go", "build", "-o", path + "harbor", main_go_path}) + WithExec([]string{"go", "build", "-o", bin_path + "harbor", "/src/cmd/harbor/main.go"}) // Get reference to build output directory in container - outputs = outputs.WithDirectory(path, build.Directory(path)) + outputs = outputs.WithDirectory(bin_path, builder.Directory(bin_path)) } } return outputs } -func (m *HarborCli) LintCode( +func (m *HarborCli) Lint( ctx context.Context, // +optional // +defaultPath="./" @@ -87,19 +87,26 @@ func (m *HarborCli) Release(ctx context.Context, directoryArg *dagger.Directory, log.Println("Release tasks completed successfully 🎉") } -func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, cosignKey *dagger.Secret, cosignPassword string, regUsername string, regPassword string) string { - - builder, main_go_path := fetchMainGoPath(ctx, directoryArg) - builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) - - // Create a minimal runtime container - runtime := dag.Container(). +func (m *HarborCli) PublishImage( + ctx context.Context, + // +optional + // +defaultPath="./" + source *dagger.Directory, + cosignKey *dagger.Secret, + cosignPassword string, + regUsername string, + regPassword string, +) string { + + builder := m.Build(ctx, source) + // Create a minimal cli_runtime container + cli_runtime := dag.Container(). From("alpine:latest"). WithWorkdir("/root/"). - WithFile("/root/harbor", builder.File("/src/harbor")). + WithFile("/root/harbor", builder.File("/")). WithEntrypoint([]string{"./harbor"}) - addr, _ := runtime.Publish(ctx, PUBLISH_ADDRESS) + addr, _ := cli_runtime.Publish(ctx, PUBLISH_ADDRESS) cosign_password := dag.SetSecret("cosign_password", cosignPassword) regpassword := dag.SetSecret("reg_password", regPassword) _, err := dag.Cosign().Sign(ctx, cosignKey, cosign_password, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regpassword}) @@ -110,23 +117,6 @@ func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Dire return addr } -func fetchMainGoPath(ctx context.Context, directoryArg *dagger.Directory) (*dagger.Container, string) { - - container := dag.Container(). - From("golang:1.22-alpine"). - WithMountedDirectory("/src", directoryArg). - WithWorkdir("/src"). - WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find ./cmd -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, _ := container.File("main_go_path.txt").Contents(ctx) - trimmedPath := strings.TrimPrefix(main_go_txt_file, "./") - result := "/src/" + trimmedPath - main_go_path := strings.TrimRight(result, "\n") - - return container, main_go_path -} - func goreleaserContainer(directoryArg *dagger.Directory, githubToken string) *dagger.Container { token := dag.SetSecret("github_token", githubToken) From 37a6ab49ef883e5a49ad3f8f4d2ade499b4df742 Mon Sep 17 00:00:00 2001 From: Vadim Bauer Date: Tue, 24 Sep 2024 16:13:52 +0200 Subject: [PATCH 17/17] cleanup --- .gitignore | 4 +- comments.patch | 177 ------------------------------------------------- 2 files changed, 1 insertion(+), 180 deletions(-) delete mode 100644 comments.patch diff --git a/.gitignore b/.gitignore index bba0048c..4e00192e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,4 @@ go.work /harbor dist/ /dagger.gen.go -/internal/dagger -/internal/querybuilder -/internal/telemetry +/internal/* diff --git a/comments.patch b/comments.patch deleted file mode 100644 index 413ed6a2..00000000 --- a/comments.patch +++ /dev/null @@ -1,177 +0,0 @@ -Subject: [PATCH] comments ---- -Index: main.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/main.go b/main.go ---- a/main.go (revision 96fa3fa98fb6ceb760e89eb10f1704e753b888ed) -+++ b/main.go (date 1724857880146) -@@ -3,17 +3,16 @@ - import ( - "context" - "fmt" -- "log" -- "strings" - "github.com/goharbor/harbor-cli/internal/dagger" -+ "log" - ) - - const ( -- GO_VERSION = "1.22.5" -- SYFT_VERSION = "v1.9.0" -+ GO_VERSION = "1.22.5" -+ SYFT_VERSION = "v1.9.0" - GORELEASER_VERSION = "v2.1.0" -- APP_NAME = "dagger-harbor-cli" -- PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" -+ APP_NAME = "dagger-harbor-cli" -+ PUBLISH_ADDRESS = "demo.goharbor.io/library/harbor-cli:0.0.3" - ) - - type HarborCli struct{} -@@ -46,25 +45,31 @@ - WithMountedDirectory("/src", directoryArg). - WithWorkdir("/src"). - WithExec([]string{"golangci-lint", "run", "--timeout", "5m"}) -- - } - --func (m *HarborCli) BuildHarbor(ctx context.Context, directoryArg *dagger.Directory) *dagger.Directory { -+// Builds the Harbor CLI for multiple OS and architectures -+func (m *HarborCli) Build(ctx context.Context, source *dagger.Directory) *dagger.Directory { -+ - fmt.Println("🛠️ Building with Dagger...") - oses := []string{"linux", "darwin", "windows"} - arches := []string{"amd64", "arm64"} - outputs := dag.Directory() -- golangcont, main_go_path := fetchMainGoPath(ctx, directoryArg) -- - for _, goos := range oses { - for _, goarch := range arches { -- path := fmt.Sprintf("build/%s/%s/", goos, goarch) -- build := golangcont.WithEnvVariable("GOOS", goos). -+ bin_path := fmt.Sprintf("build/%s/%s/", goos, goarch) -+ builder := dag.Container(). -+ From("golang:1.22-alpine"). -+ WithMountedDirectory("/src", source). -+ WithWorkdir("/src"). -+ WithMountedCache("/go/pkg/mod", dag.CacheVolume("go-mod-122-5")). -+ WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). -+ WithMountedCache("/go/build-cache", dag.CacheVolume("go-build-122-5")). -+ WithEnvVariable("GOCACHE", "/go/build-cache"). -+ WithEnvVariable("GOOS", goos). - WithEnvVariable("GOARCH", goarch). -- WithExec([]string{"go", "build", "-o", path + "harbor", main_go_path}) -- -+ WithExec([]string{"go", "build", "-o", bin_path + "harbor", "/src/cmd/harbor/main.go"}) - // Get reference to build output directory in container -- outputs = outputs.WithDirectory(path, build.Directory(path)) -+ outputs = outputs.WithDirectory(bin_path, builder.Directory(bin_path)) - } - } - return outputs -@@ -90,44 +95,26 @@ - log.Println("Release tasks completed successfully 🎉") - } - --func (m *HarborCli) DockerPublish(ctx context.Context, directoryArg *dagger.Directory, regUsername string, regPassword *dagger.Secret, privateKey *dagger.Secret, cosignPassword *dagger.Secret) string { -+func (m *HarborCli) DockerPublish(ctx context.Context, source *dagger.Directory) string { - --builder, main_go_path := fetchMainGoPath(ctx, directoryArg) --builder = builder.WithWorkdir("/src").WithExec([]string{"go", "build", "-o", "harbor", main_go_path}) -+ builder := m.Build(ctx, source) - --// Create a minimal runtime container --runtime := dag.Container(). --From("alpine:latest"). --WithWorkdir("/root/"). --WithFile("/root/harbor", builder.File("/src/harbor")). --WithEntrypoint([]string{"./harbor"}) -+ // Create a minimal runtime container -+ runtime := dag.Container(). -+ From("alpine:latest"). -+ WithWorkdir("/root/"). -+ WithFile("/root/harbor", builder.File("/src/harbor")). -+ WithEntrypoint([]string{"./harbor"}) - --addr, _ := runtime.Publish(ctx,PUBLISH_ADDRESS) --_ , err := dag.Cosign().Sign(ctx,privateKey,cosignPassword,[]string{addr},dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) --if err != nil { -- panic(err) --} --fmt.Printf("Published to %s 🎉\n", addr) --return addr -+ addr, _ := runtime.Publish(ctx, PUBLISH_ADDRESS) -+ //_, err := dag.Cosign().Sign(ctx, privateKey, cosignPassword, []string{addr}, dagger.CosignSignOpts{RegistryUsername: regUsername, RegistryPassword: regPassword}) -+ //if err != nil { -+ // panic(err) -+ //} -+ fmt.Printf("Published to %s 🎉\n", addr) -+ return addr - } - --func fetchMainGoPath(ctx context.Context, directoryArg *dagger.Directory) (*dagger.Container, string) { -- -- container := dag.Container(). -- From("golang:1.22-alpine"). -- WithMountedDirectory("/src", directoryArg). -- WithWorkdir("/src"). -- WithExec([]string{"sh", "-c", "export MAIN_GO_PATH=$(find ./cmd -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, _ := container.File("main_go_path.txt").Contents(ctx) -- trimmedPath := strings.TrimPrefix(main_go_txt_file, "./") -- result := "/src/" + trimmedPath -- main_go_path := strings.TrimRight(result, "\n") -- -- return container, main_go_path --} -- - func goreleaserContainer(directoryArg *dagger.Directory, githubToken string) *dagger.Container { - token := dag.SetSecret("github_token", githubToken) - -Index: dagger.json -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/dagger.json b/dagger.json ---- a/dagger.json (revision 96fa3fa98fb6ceb760e89eb10f1704e753b888ed) -+++ b/dagger.json (date 1724762958963) -@@ -8,5 +8,5 @@ - } - ], - "source": ".", -- "engineVersion": "v0.12.3" -+ "engineVersion": "v0.12.5" - } -Index: README.md -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/README.md b/README.md ---- a/README.md (revision 96fa3fa98fb6ceb760e89eb10f1704e753b888ed) -+++ b/README.md (date 1724851803391) -@@ -102,11 +102,16 @@ - - ## Build From Source - -+For building the Harbor CLI locally, we recommend using [Dagger](https://docs.dagger.io/). -+Make sure you have also a container runtime like Docker or Podman installed on your system. -+ - ```bash - git clone https://github.com/goharbor/harbor-cli.git --cd harbor-cli/cmd/harbor --go build . --sudo mv harbor /usr/local/bin/ -+dagger functions # to see all existing operations without looking into code -+dagger call build --source=. # to create a harbor cli binary -+ls -la build/ # to see the binary for your platform -+ -+ - ``` - - ## Linux and MacOS