From 316ca972b6a6ce4e2fe4b422097579b7e6ae0f8e Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Wed, 24 Nov 2021 21:54:51 +0100 Subject: [PATCH] bake: fix print output Signed-off-by: CrazyMax --- bake/bake.go | 15 ++- bake/bake_test.go | 232 +++++++++++++++++++++++++++++++--- commands/bake.go | 28 ++-- docs/reference/buildx_bake.md | 134 ++++++++++++-------- 4 files changed, 319 insertions(+), 90 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index 199259de500..b6d4c6eef5b 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -89,7 +89,20 @@ func ReadTargets(ctx context.Context, files []File, targets, overrides []string, } } } - return m, c.Groups, nil + + var g []*Group + if len(targets) == 0 || (len(targets) == 1 && targets[0] == "default") { + for _, group := range c.Groups { + if group.Name != "default" { + continue + } + g = []*Group{{Targets: group.Targets}} + } + } else { + g = []*Group{{Targets: targets}} + } + + return m, g, nil } func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) { diff --git a/bake/bake_test.go b/bake/bake_test.go index caf43cf4708..46798f294fc 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -3,6 +3,7 @@ package bake import ( "context" "os" + "sort" "testing" "github.com/stretchr/testify/require" @@ -34,7 +35,7 @@ target "webapp" { ctx := context.TODO() t.Run("NoOverrides", func(t *testing.T) { - m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, nil, nil) + m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(m)) @@ -43,6 +44,9 @@ target "webapp" { require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"]) require.Equal(t, true, *m["webapp"].NoCache) require.Nil(t, m["webapp"].Pull) + + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"webapp"}, g[0].Targets) }) t.Run("InvalidTargetOverrides", func(t *testing.T) { @@ -56,7 +60,7 @@ target "webapp" { os.Setenv("VAR_FROMENV"+t.Name(), "fromEnv") defer os.Unsetenv("VAR_FROM_ENV" + t.Name()) - m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{ + m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{ "webapp.args.VAR_UNSET", "webapp.args.VAR_EMPTY=", "webapp.args.VAR_SET=bananas", @@ -81,17 +85,23 @@ target "webapp" { require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp") require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override") + + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"webapp"}, g[0].Targets) }) // building leaf but overriding parent fields t.Run("parent", func(t *testing.T) { - m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{ + m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{ "webDEP.args.VAR_INHERITED=override", "webDEP.args.VAR_BOTH=override", }, nil) + require.NoError(t, err) require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override") require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp") + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"webapp"}, g[0].Targets) }) }) @@ -99,40 +109,48 @@ target "webapp" { _, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil) require.NotNil(t, err) - m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context=foo"}, nil) + m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context=foo"}, nil) require.NoError(t, err) - require.Equal(t, "foo", *m["webapp"].Context) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"webapp"}, g[0].Targets) }) t.Run("NoCacheOverride", func(t *testing.T) { - m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.no-cache=false"}, nil) + m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.no-cache=false"}, nil) require.NoError(t, err) require.Equal(t, false, *m["webapp"].NoCache) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"webapp"}, g[0].Targets) }) t.Run("PullOverride", func(t *testing.T) { - m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil) + m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil) require.NoError(t, err) require.Equal(t, false, *m["webapp"].Pull) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"webapp"}, g[0].Targets) }) t.Run("PatternOverride", func(t *testing.T) { // same check for two cases - multiTargetCheck := func(t *testing.T, m map[string]*Target, err error) { + multiTargetCheck := func(t *testing.T, m map[string]*Target, g []*Group, err error) { require.NoError(t, err) require.Equal(t, 2, len(m)) require.Equal(t, "foo", *m["webapp"].Dockerfile) require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"]) require.Equal(t, "foo", *m["webDEP"].Dockerfile) require.Equal(t, "webDEP", m["webDEP"].Args["VAR_INHERITED"]) + require.Equal(t, 1, len(g)) + sort.Strings(g[0].Targets) + require.Equal(t, []string{"webDEP", "webapp"}, g[0].Targets) } cases := []struct { name string targets []string overrides []string - check func(*testing.T, map[string]*Target, error) + check func(*testing.T, map[string]*Target, []*Group, error) }{ { name: "multi target single pattern", @@ -150,18 +168,20 @@ target "webapp" { name: "single target", targets: []string{"webapp"}, overrides: []string{"web*.dockerfile=foo"}, - check: func(t *testing.T, m map[string]*Target, err error) { + check: func(t *testing.T, m map[string]*Target, g []*Group, err error) { require.NoError(t, err) require.Equal(t, 1, len(m)) require.Equal(t, "foo", *m["webapp"].Dockerfile) require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"]) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"webapp"}, g[0].Targets) }, }, { name: "nomatch", targets: []string{"webapp"}, overrides: []string{"nomatch*.dockerfile=foo"}, - check: func(t *testing.T, m map[string]*Target, err error) { + check: func(t *testing.T, m map[string]*Target, g []*Group, err error) { // NOTE: I am unsure whether failing to match should always error out // instead of simply skipping that override. // Let's enforce the error and we can relax it later if users complain. @@ -172,8 +192,8 @@ target "webapp" { } for _, test := range cases { t.Run(test.name, func(t *testing.T) { - m, _, err := ReadTargets(ctx, []File{fp}, test.targets, test.overrides, nil) - test.check(t, m, err) + m, g, err := ReadTargets(ctx, []File{fp}, test.targets, test.overrides, nil) + test.check(t, m, g, err) }) } }) @@ -260,16 +280,21 @@ services: ctx := context.TODO() - m, _, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil) + m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil) require.NoError(t, err) require.Equal(t, 3, len(m)) _, ok := m["newservice"] + require.True(t, ok) require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile) require.Equal(t, ".", *m["webapp"].Context) require.Equal(t, "1", m["webapp"].Args["buildno"]) require.Equal(t, "12", m["webapp"].Args["buildno2"]) + + require.Equal(t, 1, len(g)) + sort.Strings(g[0].Targets) + require.Equal(t, []string{"db", "newservice", "webapp"}, g[0].Targets) } func TestHCLCwdPrefix(t *testing.T) { @@ -282,7 +307,7 @@ func TestHCLCwdPrefix(t *testing.T) { }`), } ctx := context.TODO() - m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) + m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(m)) @@ -294,6 +319,9 @@ func TestHCLCwdPrefix(t *testing.T) { require.Equal(t, "test", *m["app"].Dockerfile) require.Equal(t, "foo", *m["app"].Context) + + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"app"}, g[0].Targets) } func TestOverrideMerge(t *testing.T) { @@ -324,3 +352,177 @@ func TestOverrideMerge(t *testing.T) { require.Equal(t, 1, len(m["app"].Outputs)) require.Equal(t, "type=registry", m["app"].Outputs[0]) } + +func TestReadTargetsMixed(t *testing.T) { + t.Parallel() + + fTargetDefault := File{ + Name: "docker-bake2.hcl", + Data: []byte(` +target "default" { + dockerfile = "test" +}`)} + + fTargetImage := File{ + Name: "docker-bake3.hcl", + Data: []byte(` +target "image" { + dockerfile = "test" +}`)} + + fpHCL := File{ + Name: "docker-bake.hcl", + Data: []byte(` +group "default" { + targets = ["image"] +} + +target "nocache" { + no-cache = true +} + +group "release" { + targets = ["image-release"] +} + +target "image" { + inherits = ["nocache"] + output = ["type=docker"] +} + +target "image-release" { + inherits = ["image"] + output = ["type=image,push=true"] + tags = ["user/app:latest"] +}`)} + + fpYML := File{ + Name: "docker-compose.yml", + Data: []byte(` +services: + addon: + build: + context: . + dockerfile: ./Dockerfile + args: + CT_ECR: foo + CT_TAG: bar + image: ct-addon:bar + environment: + - NODE_ENV=test + - AWS_ACCESS_KEY_ID=dummy + - AWS_SECRET_ACCESS_KEY=dummy + + aws: + build: + dockerfile: ./aws.Dockerfile + args: + CT_ECR: foo + CT_TAG: bar + image: ct-fake-aws:bar`)} + + fpJSON := File{ + Name: "docker-bake.json", + Data: []byte(`{ + "group": { + "default": { + "targets": [ + "image" + ] + } + }, + "target": { + "image": { + "context": ".", + "dockerfile": "Dockerfile", + "output": [ + "type=docker" + ] + } + } + }`)} + + ctx := context.TODO() + + m, g, err := ReadTargets(ctx, []File{fTargetDefault}, []string{"default"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 0, len(g)) + require.Equal(t, 1, len(m)) + require.Equal(t, "test", *m["default"].Dockerfile) + + _, _, err = ReadTargets(ctx, []File{fTargetImage}, []string{"default"}, nil, nil) + require.Error(t, err) + + m, g, err = ReadTargets(ctx, []File{fTargetImage}, []string{"image"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"image"}, g[0].Targets) + require.Equal(t, 1, len(m)) + require.Equal(t, "test", *m["image"].Dockerfile) + + m, g, err = ReadTargets(ctx, []File{fTargetImage}, []string{"image"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"image"}, g[0].Targets) + require.Equal(t, 1, len(m)) + require.Equal(t, "test", *m["image"].Dockerfile) + + m, g, err = ReadTargets(ctx, []File{fpHCL}, []string{"image-release"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"image-release"}, g[0].Targets) + require.Equal(t, 1, len(m)) + require.Equal(t, 1, len(m["image-release"].Outputs)) + require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0]) + + m, g, err = ReadTargets(ctx, []File{fpHCL}, []string{"image", "image-release"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"image", "image-release"}, g[0].Targets) + require.Equal(t, 2, len(m)) + require.Equal(t, ".", *m["image"].Context) + require.Equal(t, 1, len(m["image-release"].Outputs)) + require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0]) + + m, g, err = ReadTargets(ctx, []File{fpYML, fpHCL}, []string{"default"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"image"}, g[0].Targets) + require.Equal(t, 1, len(m)) + require.Equal(t, ".", *m["image"].Context) + + m, g, err = ReadTargets(ctx, []File{fpJSON}, []string{"default"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + require.Equal(t, []string{"image"}, g[0].Targets) + require.Equal(t, 1, len(m)) + require.Equal(t, ".", *m["image"].Context) + + m, g, err = ReadTargets(ctx, []File{fpYML}, []string{"default"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + sort.Strings(g[0].Targets) + require.Equal(t, []string{"addon", "aws"}, g[0].Targets) + require.Equal(t, 2, len(m)) + require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile) + require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile) + + m, g, err = ReadTargets(ctx, []File{fpYML, fpHCL}, []string{"addon", "aws"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + sort.Strings(g[0].Targets) + require.Equal(t, []string{"addon", "aws"}, g[0].Targets) + require.Equal(t, 2, len(m)) + require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile) + require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile) + + m, g, err = ReadTargets(ctx, []File{fpYML, fpHCL}, []string{"addon", "aws", "image"}, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(g)) + sort.Strings(g[0].Targets) + require.Equal(t, []string{"addon", "aws", "image"}, g[0].Targets) + require.Equal(t, 3, len(m)) + require.Equal(t, ".", *m["image"].Context) + require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile) + require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile) +} diff --git a/commands/bake.go b/commands/bake.go index 011909ce328..c6bc9ba8b3a 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -38,7 +38,6 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error }() var url string - var noTarget bool cmdContext := "cwd://" if len(targets) > 0 { @@ -49,7 +48,6 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error if bake.IsRemoteURL(targets[0]) { cmdContext = targets[0] targets = targets[1:] - } } } @@ -57,7 +55,6 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error if len(targets) == 0 { targets = []string{"default"} - noTarget = true } overrides := in.overrides @@ -107,7 +104,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error return err } - t, g, err := bake.ReadTargets(ctx, files, targets, overrides, map[string]string{ + tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, map[string]string{ // Don't forget to update documentation if you add a new // built-in variable: docs/reference/buildx_bake.md#built-in-variables "BAKE_CMD_CONTEXT": cmdContext, @@ -118,31 +115,24 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error } // this function can update target context string from the input so call before printOnly check - bo, err := bake.TargetsToBuildOpt(t, inp) + bo, err := bake.TargetsToBuildOpt(tgts, inp) if err != nil { return err } if in.printOnly { - defGroup := map[string][]string{ - "default": targets, - } - if noTarget { - for _, group := range g { - if group.Name != "default" { - continue - } - defGroup = map[string][]string{ - "default": group.Targets, - } + var defg map[string]*bake.Group + if len(grps) == 1 { + defg = map[string]*bake.Group{ + "default": grps[0], } } dt, err := json.MarshalIndent(struct { - Group map[string][]string `json:"group,omitempty"` + Group map[string]*bake.Group `json:"group,omitempty"` Target map[string]*bake.Target `json:"target"` }{ - defGroup, - t, + defg, + tgts, }, "", " ") if err != nil { return err diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md index 8c62c42ba5f..16073ec5788 100644 --- a/docs/reference/buildx_bake.md +++ b/docs/reference/buildx_bake.md @@ -99,19 +99,23 @@ $ docker buildx bake -f docker-compose.dev.yaml backend database You can also use a remote `git` bake definition: ```console -$ docker buildx bake "git://github.com/docker/cli#master" --print -#1 [internal] load git source git://github.com/docker/cli#master -#1 0.686 2776a6d694f988c0c1df61cad4bfac0f54e481c8 refs/heads/master -#1 CACHED +$ docker buildx bake "git://github.com/docker/cli#v20.10.11" --print +#1 [internal] load git source git://github.com/docker/cli#v20.10.11 +#1 0.745 e8f1871b077b64bcb4a13334b7146492773769f7 refs/tags/v20.10.11 +#1 2.022 From git://github.com/docker/cli +#1 2.022 * [new tag] v20.10.11 -> v20.10.11 +#1 DONE 2.9s { "group": { - "default": [ - "binary" - ] + "default": { + "targets": [ + "binary" + ] + } }, "target": { "binary": { - "context": "git://github.com/docker/cli#master", + "context": "git://github.com/docker/cli#v20.10.11", "dockerfile": "Dockerfile", "args": { "BASE_VARIANT": "alpine", @@ -153,11 +157,6 @@ EOT ```console $ docker buildx bake "git://github.com/tonistiigi/buildx#remote-test" --print { - "group": { - "default": [ - "default" - ] - }, "target": { "default": { "context": ".", @@ -180,19 +179,14 @@ $ docker buildx bake "git://github.com/tonistiigi/buildx#remote-test" ``` ```console -$ docker buildx bake "git://github.com/tonistiigi/buildx#remote-test" "git://github.com/docker/cli#master" --print +$ docker buildx bake "git://github.com/tonistiigi/buildx#remote-test" "git://github.com/docker/cli#v20.10.11" --print #1 [internal] load git source git://github.com/tonistiigi/buildx#remote-test -#1 0.401 577303add004dd7efeb13434d69ea030d35f7888 refs/heads/remote-test +#1 0.429 577303add004dd7efeb13434d69ea030d35f7888 refs/heads/remote-test #1 CACHED { - "group": { - "default": [ - "default" - ] - }, "target": { "default": { - "context": "git://github.com/docker/cli#master", + "context": "git://github.com/docker/cli#v20.10.11", "dockerfile": "Dockerfile", "dockerfile-inline": "FROM alpine\nWORKDIR /src\nCOPY . .\nRUN ls -l \u0026\u0026 stop\n" } @@ -201,7 +195,7 @@ $ docker buildx bake "git://github.com/tonistiigi/buildx#remote-test" "git://git ``` ```console -$ docker buildx bake "git://github.com/tonistiigi/buildx#remote-test" "git://github.com/docker/cli#master" +$ docker buildx bake "git://github.com/tonistiigi/buildx#remote-test" "git://github.com/docker/cli#v20.10.11" ... > [4/4] RUN ls -l && stop: #8 0.136 drwxrwxrwx 5 root root 4096 Jul 27 18:31 kubernetes @@ -229,9 +223,11 @@ format, without starting a build. $ docker buildx bake -f docker-bake.hcl --print db { "group": { - "default": [ - "db" - ] + "default": { + "targets": [ + "db" + ] + } }, "target": { "db": { @@ -372,9 +368,11 @@ You can use this file directly: $ docker buildx bake --print app { "group": { - "default": [ - "app" - ] + "default": { + "targets": [ + "app" + ] + } }, "target": { "app": { @@ -402,9 +400,11 @@ And invoke bake together with both of the files: $ docker buildx bake -f docker-bake.hcl -f env.hcl --print app { "group": { - "default": [ - "app" - ] + "default": { + "targets": [ + "app" + ] + } }, "target": { "app": { @@ -454,9 +454,11 @@ target "webapp" { $ docker buildx bake --print webapp { "group": { - "default": [ - "webapp" - ] + "default": { + "targets": [ + "webapp" + ] + } }, "target": { "webapp": { @@ -474,9 +476,11 @@ $ docker buildx bake --print webapp $ TAG=$(git rev-parse --short HEAD) docker buildx bake --print webapp { "group": { - "default": [ - "webapp" - ] + "default": { + "targets": [ + "webapp" + ] + } }, "target": { "webapp": { @@ -516,9 +520,11 @@ target "webapp" { $ docker buildx bake --print webapp { "group": { - "default": [ - "webapp" - ] + "default": { + "targets": [ + "webapp" + ] + } }, "target": { "webapp": { @@ -559,9 +565,11 @@ target "webapp" { $ docker buildx bake --print webapp { "group": { - "default": [ - "webapp" - ] + "default": { + "targets": [ + "webapp" + ] + } }, "target": { "webapp": { @@ -604,9 +612,11 @@ target "webapp" { $ docker buildx bake --print webapp { "group": { - "default": [ - "webapp" - ] + "default": { + "targets": [ + "webapp" + ] + } }, "target": { "webapp": { @@ -645,9 +655,11 @@ target "webapp" { $ docker buildx bake --print webapp { "group": { - "default": [ - "webapp" - ] + "default": { + "targets": [ + "webapp" + ] + } }, "target": { "webapp": { @@ -700,9 +712,11 @@ target "app" { $ docker buildx bake -f docker-bake1.hcl -f docker-bake2.hcl --print app { "group": { - "default": [ - "app" - ] + "default": { + "targets": [ + "app" + ] + } }, "target": { "app": { @@ -744,9 +758,11 @@ target "app" { $ docker buildx bake --print app { "group": { - "default": [ - "app" - ] + "default": { + "targets": [ + "app" + ] + } }, "target": { "app": { @@ -810,6 +826,14 @@ services: ```console $ docker buildx bake --print { + "group": { + "default": { + "targets": [ + "aws", + "addon" + ] + } + }, "target": { "addon": { "context": ".",