diff --git a/tests/bake.go b/tests/bake.go index 8f92cb8e668..1fb9d8a1866 100644 --- a/tests/bake.go +++ b/tests/bake.go @@ -11,6 +11,7 @@ import ( "github.com/containerd/continuity/fs/fstest" "github.com/docker/buildx/bake" + "github.com/docker/buildx/util/buildflags" "github.com/docker/buildx/util/gitutil" "github.com/moby/buildkit/client" "github.com/moby/buildkit/frontend/subrequests/lint" @@ -32,6 +33,7 @@ func bakeCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) { var bakeTests = []func(t *testing.T, sb integration.Sandbox){ testBakePrint, + testBakePrintConsistency, testBakeLocal, testBakeLocalMulti, testBakeRemote, @@ -63,6 +65,139 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){ } func testBakePrint(t *testing.T, sb integration.Sandbox) { + dockerfile := []byte(` +FROM busybox +ARG HELLO +RUN echo "Hello ${HELLO}" + `) + bakefile := []byte(` +target "build" { + args = { + HELLO = "foo" + } + cache-from = [ + "type=gha,scope=build", + "type=s3,region=eu-west-1,bucket=mybucket", + "user/repo:cache", + ] + cache-to = [ + "type=gha,scope=build,token=foo,mode=max", + "type=s3,region=eu-west-1,bucket=mybucket", + "type=inline" + ] + output = [ + "./release-out", + "type=registry,ref=user/app" + ] +} +`) + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + + cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--print", "build")) + cmd.Env = append(cmd.Env, "ACTIONS_RUNTIME_TOKEN=baz", "AWS_ACCESS_KEY_ID=foo", "AWS_SECRET_ACCESS_KEY=bar") + stdout := bytes.Buffer{} + stderr := bytes.Buffer{} + cmd.Stdout = &stdout + cmd.Stderr = &stderr + require.NoError(t, cmd.Run(), stdout.String(), stderr.String()) + + var def struct { + Group map[string]*bake.Group `json:"group,omitempty"` + Target map[string]*bake.Target `json:"target"` + } + require.NoError(t, json.Unmarshal(stdout.Bytes(), &def)) + + require.Len(t, def.Group, 1) + require.Contains(t, def.Group, "default") + + require.Equal(t, []string{"build"}, def.Group["default"].Targets) + require.Len(t, def.Target, 1) + require.Contains(t, def.Target, "build") + require.Equal(t, ".", *def.Target["build"].Context) + require.Equal(t, "Dockerfile", *def.Target["build"].Dockerfile) + require.Equal(t, map[string]*string{"HELLO": ptrstr("foo")}, def.Target["build"].Args) + require.Equal(t, []*buildflags.CacheOptionsEntry{ + {Type: "gha", Attrs: map[string]string{"scope": "build"}}, + {Type: "s3", Attrs: map[string]string{"region": "eu-west-1", "bucket": "mybucket"}}, + {Type: "registry", Attrs: map[string]string{"ref": "user/repo:cache"}}, + }, def.Target["build"].CacheFrom) + require.Equal(t, []*buildflags.CacheOptionsEntry{ + {Type: "gha", Attrs: map[string]string{"scope": "build", "token": "foo", "mode": "max"}}, + {Type: "s3", Attrs: map[string]string{"region": "eu-west-1", "bucket": "mybucket"}}, + {Type: "inline", Attrs: map[string]string{}}, + }, def.Target["build"].CacheTo) + require.Equal(t, []*buildflags.ExportEntry{ + {Type: "local", Destination: "./release-out"}, + {Type: "registry", Attrs: map[string]string{"ref": "user/app"}}, + }, def.Target["build"].Outputs) + + require.JSONEq(t, `{ + "group": { + "default": { + "targets": [ + "build" + ] + } + }, + "target": { + "build": { + "context": ".", + "dockerfile": "Dockerfile", + "args": { + "HELLO": "foo" + }, + "cache-from": [ + { + "scope": "build", + "type": "gha" + }, + { + "bucket": "mybucket", + "region": "eu-west-1", + "type": "s3" + }, + { + "ref": "user/repo:cache", + "type": "registry" + } + ], + "cache-to": [ + { + "mode": "max", + "scope": "build", + "token": "foo", + "type": "gha" + }, + { + "bucket": "mybucket", + "region": "eu-west-1", + "type": "s3" + }, + { + "type": "inline" + } + ], + "output": [ + { + "dest": "./release-out", + "type": "local" + }, + { + "ref": "user/app", + "type": "registry" + } + ] + } + } +} +`, stdout.String()) +} + +func testBakePrintConsistency(t *testing.T, sb integration.Sandbox) { testCases := []struct { name string f string diff --git a/util/buildflags/cache.go b/util/buildflags/cache.go index fcd36ff5e04..ab958f4ed2c 100644 --- a/util/buildflags/cache.go +++ b/util/buildflags/cache.go @@ -16,8 +16,9 @@ import ( ) type CacheOptionsEntry struct { - Type string `json:"type"` - Attrs map[string]string `json:"attrs,omitempty"` + Type string `json:"type"` + Attrs map[string]string `json:"attrs,omitempty"` + ImplicitAttrs map[string]string `json:"-"` } func (e *CacheOptionsEntry) Equal(other *CacheOptionsEntry) bool { @@ -46,9 +47,11 @@ func (e *CacheOptionsEntry) String() string { } func (e *CacheOptionsEntry) ToPB() *controllerapi.CacheOptionsEntry { + attrs := maps.Clone(e.Attrs) + maps.Copy(attrs, e.ImplicitAttrs) return &controllerapi.CacheOptionsEntry{ Type: e.Type, - Attrs: maps.Clone(e.Attrs), + Attrs: attrs, } } @@ -79,7 +82,7 @@ func (e *CacheOptionsEntry) IsActive() bool { if e.Type != "gha" { return true } - return e.Attrs["token"] != "" && e.Attrs["url"] != "" + return (e.Attrs["token"] != "" || e.ImplicitAttrs["token"] != "") && (e.Attrs["url"] != "" || e.ImplicitAttrs["url"] != "") } func (e *CacheOptionsEntry) UnmarshalText(text []byte) error { @@ -97,6 +100,7 @@ func (e *CacheOptionsEntry) UnmarshalText(text []byte) error { e.Type = "" e.Attrs = map[string]string{} + e.ImplicitAttrs = map[string]string{} for _, field := range fields { parts := strings.SplitN(field, "=", 2) @@ -162,12 +166,12 @@ func addGithubToken(ci *CacheOptionsEntry) { } if _, ok := ci.Attrs["token"]; !ok { if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok { - ci.Attrs["token"] = v + ci.ImplicitAttrs["token"] = v } } if _, ok := ci.Attrs["url"]; !ok { if v, ok := os.LookupEnv("ACTIONS_CACHE_URL"); ok { - ci.Attrs["url"] = v + ci.ImplicitAttrs["url"] = v } } } @@ -192,12 +196,12 @@ func addAwsCredentials(ci *CacheOptionsEntry) { return } if !okAccessKeyID && credentials.AccessKeyID != "" { - ci.Attrs["access_key_id"] = credentials.AccessKeyID + ci.ImplicitAttrs["access_key_id"] = credentials.AccessKeyID } if !okSecretAccessKey && credentials.SecretAccessKey != "" { - ci.Attrs["secret_access_key"] = credentials.SecretAccessKey + ci.ImplicitAttrs["secret_access_key"] = credentials.SecretAccessKey } if _, ok := ci.Attrs["session_token"]; !ok && credentials.SessionToken != "" { - ci.Attrs["session_token"] = credentials.SessionToken + ci.ImplicitAttrs["session_token"] = credentials.SessionToken } }