Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

bake: fix print output #857

Merged
merged 1 commit into from
Nov 24, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion bake/bake.go
Original file line number Diff line number Diff line change
@@ -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) {
232 changes: 217 additions & 15 deletions bake/bake_test.go
Original file line number Diff line number Diff line change
@@ -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,58 +85,72 @@ 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)
})
})

t.Run("ContextOverride", func(t *testing.T) {
_, _, 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)
}
28 changes: 9 additions & 19 deletions commands/bake.go
Original file line number Diff line number Diff line change
@@ -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,15 +48,13 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
if bake.IsRemoteURL(targets[0]) {
cmdContext = targets[0]
targets = targets[1:]

}
}
}
}

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
134 changes: 79 additions & 55 deletions docs/reference/buildx_bake.md
Original file line number Diff line number Diff line change
@@ -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": ".",