From 06807d706a25e0fdb5255fa0754081ba0b5ea37d Mon Sep 17 00:00:00 2001 From: Vadim Bauer Date: Tue, 24 Sep 2024 15:46:15 +0200 Subject: [PATCH] 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)