Skip to content

Commit 528b1be

Browse files
committed
bake: support null args
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
1 parent e91d532 commit 528b1be

7 files changed

Lines changed: 265 additions & 133 deletions

File tree

bake/bake.go

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -558,24 +558,24 @@ type Target struct {
558558
// Inherits is the only field that cannot be overridden with --set
559559
Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional"`
560560

561-
Context *string `json:"context,omitempty" hcl:"context,optional"`
562-
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional"`
563-
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional"`
564-
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional"`
565-
Args map[string]string `json:"args,omitempty" hcl:"args,optional"`
566-
Labels map[string]string `json:"labels,omitempty" hcl:"labels,optional"`
567-
Tags []string `json:"tags,omitempty" hcl:"tags,optional"`
568-
CacheFrom []string `json:"cache-from,omitempty" hcl:"cache-from,optional"`
569-
CacheTo []string `json:"cache-to,omitempty" hcl:"cache-to,optional"`
570-
Target *string `json:"target,omitempty" hcl:"target,optional"`
571-
Secrets []string `json:"secret,omitempty" hcl:"secret,optional"`
572-
SSH []string `json:"ssh,omitempty" hcl:"ssh,optional"`
573-
Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional"`
574-
Outputs []string `json:"output,omitempty" hcl:"output,optional"`
575-
Pull *bool `json:"pull,omitempty" hcl:"pull,optional"`
576-
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional"`
577-
NetworkMode *string `json:"-" hcl:"-"`
578-
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional"`
561+
Context *string `json:"context,omitempty" hcl:"context,optional"`
562+
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional"`
563+
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional"`
564+
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional"`
565+
Args map[string]*string `json:"args,omitempty" hcl:"args,optional"`
566+
Labels map[string]string `json:"labels,omitempty" hcl:"labels,optional"`
567+
Tags []string `json:"tags,omitempty" hcl:"tags,optional"`
568+
CacheFrom []string `json:"cache-from,omitempty" hcl:"cache-from,optional"`
569+
CacheTo []string `json:"cache-to,omitempty" hcl:"cache-to,optional"`
570+
Target *string `json:"target,omitempty" hcl:"target,optional"`
571+
Secrets []string `json:"secret,omitempty" hcl:"secret,optional"`
572+
SSH []string `json:"ssh,omitempty" hcl:"ssh,optional"`
573+
Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional"`
574+
Outputs []string `json:"output,omitempty" hcl:"output,optional"`
575+
Pull *bool `json:"pull,omitempty" hcl:"pull,optional"`
576+
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional"`
577+
NetworkMode *string `json:"-" hcl:"-"`
578+
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional"`
579579
// IMPORTANT: if you add more fields here, do not forget to update newOverrides and docs/manuals/bake/file-definition.md.
580580

581581
// linked is a private field to mark a target used as a linked one
@@ -613,8 +613,11 @@ func (t *Target) Merge(t2 *Target) {
613613
t.DockerfileInline = t2.DockerfileInline
614614
}
615615
for k, v := range t2.Args {
616+
if v == nil {
617+
continue
618+
}
616619
if t.Args == nil {
617-
t.Args = map[string]string{}
620+
t.Args = map[string]*string{}
618621
}
619622
t.Args[k] = v
620623
}
@@ -683,9 +686,9 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
683686
return errors.Errorf("args require name")
684687
}
685688
if t.Args == nil {
686-
t.Args = map[string]string{}
689+
t.Args = map[string]*string{}
687690
}
688-
t.Args[keys[1]] = value
691+
t.Args[keys[1]] = &value
689692
case "contexts":
690693
if len(keys) != 2 {
691694
return errors.Errorf("contexts require name")
@@ -875,6 +878,14 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
875878
dockerfilePath = path.Join(contextPath, dockerfilePath)
876879
}
877880

881+
args := map[string]string{}
882+
for k, v := range t.Args {
883+
if v == nil {
884+
continue
885+
}
886+
args[k] = *v
887+
}
888+
878889
noCache := false
879890
if t.NoCache != nil {
880891
noCache = *t.NoCache
@@ -915,7 +926,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
915926
bo := &build.Options{
916927
Inputs: bi,
917928
Tags: t.Tags,
918-
BuildArgs: t.Args,
929+
BuildArgs: args,
919930
Labels: t.Labels,
920931
NoCache: noCache,
921932
NoCacheFilter: t.NoCacheFilter,

bake/bake_test.go

Lines changed: 93 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package bake
22

33
import (
44
"context"
5-
"os"
65
"sort"
76
"strings"
87
"testing"
@@ -42,7 +41,7 @@ target "webapp" {
4241

4342
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
4443
require.Equal(t, ".", *m["webapp"].Context)
45-
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
44+
require.Equal(t, ptrstr("webDEP"), m["webapp"].Args["VAR_INHERITED"])
4645
require.Equal(t, true, *m["webapp"].NoCache)
4746
require.Nil(t, m["webapp"].Pull)
4847

@@ -58,8 +57,7 @@ target "webapp" {
5857

5958
t.Run("ArgsOverrides", func(t *testing.T) {
6059
t.Run("leaf", func(t *testing.T) {
61-
os.Setenv("VAR_FROMENV"+t.Name(), "fromEnv")
62-
defer os.Unsetenv("VAR_FROM_ENV" + t.Name())
60+
t.Setenv("VAR_FROMENV"+t.Name(), "fromEnv")
6361

6462
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{
6563
"webapp.args.VAR_UNSET",
@@ -80,12 +78,12 @@ target "webapp" {
8078
_, isSet = m["webapp"].Args["VAR_EMPTY"]
8179
require.True(t, isSet, m["webapp"].Args["VAR_EMPTY"])
8280

83-
require.Equal(t, m["webapp"].Args["VAR_SET"], "bananas")
81+
require.Equal(t, ptrstr("bananas"), m["webapp"].Args["VAR_SET"])
8482

85-
require.Equal(t, m["webapp"].Args["VAR_FROMENV"+t.Name()], "fromEnv")
83+
require.Equal(t, ptrstr("fromEnv"), m["webapp"].Args["VAR_FROMENV"+t.Name()])
8684

87-
require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp")
88-
require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override")
85+
require.Equal(t, ptrstr("webapp"), m["webapp"].Args["VAR_BOTH"])
86+
require.Equal(t, ptrstr("override"), m["webapp"].Args["VAR_INHERITED"])
8987

9088
require.Equal(t, 1, len(g))
9189
require.Equal(t, []string{"webapp"}, g["default"].Targets)
@@ -99,8 +97,8 @@ target "webapp" {
9997
}, nil)
10098

10199
require.NoError(t, err)
102-
require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override")
103-
require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp")
100+
require.Equal(t, ptrstr("override"), m["webapp"].Args["VAR_INHERITED"])
101+
require.Equal(t, ptrstr("webapp"), m["webapp"].Args["VAR_BOTH"])
104102
require.Equal(t, 1, len(g))
105103
require.Equal(t, []string{"webapp"}, g["default"].Targets)
106104
})
@@ -139,9 +137,9 @@ target "webapp" {
139137
require.NoError(t, err)
140138
require.Equal(t, 2, len(m))
141139
require.Equal(t, "foo", *m["webapp"].Dockerfile)
142-
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
140+
require.Equal(t, ptrstr("webDEP"), m["webapp"].Args["VAR_INHERITED"])
143141
require.Equal(t, "foo", *m["webDEP"].Dockerfile)
144-
require.Equal(t, "webDEP", m["webDEP"].Args["VAR_INHERITED"])
142+
require.Equal(t, ptrstr("webDEP"), m["webDEP"].Args["VAR_INHERITED"])
145143
require.Equal(t, 1, len(g))
146144
sort.Strings(g["default"].Targets)
147145
require.Equal(t, []string{"webDEP", "webapp"}, g["default"].Targets)
@@ -173,7 +171,7 @@ target "webapp" {
173171
require.NoError(t, err)
174172
require.Equal(t, 1, len(m))
175173
require.Equal(t, "foo", *m["webapp"].Dockerfile)
176-
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
174+
require.Equal(t, ptrstr("webDEP"), m["webapp"].Args["VAR_INHERITED"])
177175
require.Equal(t, 1, len(g))
178176
require.Equal(t, []string{"webapp"}, g["default"].Targets)
179177
},
@@ -300,8 +298,8 @@ services:
300298
require.True(t, ok)
301299
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
302300
require.Equal(t, ".", *m["webapp"].Context)
303-
require.Equal(t, "1", m["webapp"].Args["buildno"])
304-
require.Equal(t, "12", m["webapp"].Args["buildno2"])
301+
require.Equal(t, ptrstr("1"), m["webapp"].Args["buildno"])
302+
require.Equal(t, ptrstr("12"), m["webapp"].Args["buildno2"])
305303

306304
require.Equal(t, 1, len(g))
307305
sort.Strings(g["default"].Targets)
@@ -344,15 +342,15 @@ services:
344342
_, ok := m["web_app"]
345343
require.True(t, ok)
346344
require.Equal(t, "Dockerfile.webapp", *m["web_app"].Dockerfile)
347-
require.Equal(t, "1", m["web_app"].Args["buildno"])
345+
require.Equal(t, ptrstr("1"), m["web_app"].Args["buildno"])
348346

349347
m, _, err = ReadTargets(ctx, []File{fp2}, []string{"web_app"}, nil, nil)
350348
require.NoError(t, err)
351349
require.Equal(t, 1, len(m))
352350
_, ok = m["web_app"]
353351
require.True(t, ok)
354352
require.Equal(t, "Dockerfile", *m["web_app"].Dockerfile)
355-
require.Equal(t, "12", m["web_app"].Args["buildno2"])
353+
require.Equal(t, ptrstr("12"), m["web_app"].Args["buildno2"])
356354

357355
m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil)
358356
require.NoError(t, err)
@@ -361,8 +359,8 @@ services:
361359
require.True(t, ok)
362360
require.Equal(t, "Dockerfile.webapp", *m["web_app"].Dockerfile)
363361
require.Equal(t, ".", *m["web_app"].Context)
364-
require.Equal(t, "1", m["web_app"].Args["buildno"])
365-
require.Equal(t, "12", m["web_app"].Args["buildno2"])
362+
require.Equal(t, ptrstr("1"), m["web_app"].Args["buildno"])
363+
require.Equal(t, ptrstr("12"), m["web_app"].Args["buildno2"])
366364

367365
require.Equal(t, 1, len(g))
368366
sort.Strings(g["default"].Targets)
@@ -999,22 +997,22 @@ target "d" {
999997
cases := []struct {
1000998
name string
1001999
overrides []string
1002-
want map[string]string
1000+
want map[string]*string
10031001
}{
10041002
{
10051003
name: "nested simple",
10061004
overrides: nil,
1007-
want: map[string]string{"bar": "234", "baz": "890", "foo": "123"},
1005+
want: map[string]*string{"bar": ptrstr("234"), "baz": ptrstr("890"), "foo": ptrstr("123")},
10081006
},
10091007
{
10101008
name: "nested with overrides first",
10111009
overrides: []string{"a.args.foo=321", "b.args.bar=432"},
1012-
want: map[string]string{"bar": "234", "baz": "890", "foo": "321"},
1010+
want: map[string]*string{"bar": ptrstr("234"), "baz": ptrstr("890"), "foo": ptrstr("321")},
10131011
},
10141012
{
10151013
name: "nested with overrides last",
10161014
overrides: []string{"a.args.foo=321", "c.args.bar=432"},
1017-
want: map[string]string{"bar": "432", "baz": "890", "foo": "321"},
1015+
want: map[string]*string{"bar": ptrstr("432"), "baz": ptrstr("890"), "foo": ptrstr("321")},
10181016
},
10191017
}
10201018
for _, tt := range cases {
@@ -1067,26 +1065,26 @@ group "default" {
10671065
cases := []struct {
10681066
name string
10691067
overrides []string
1070-
wantch1 map[string]string
1071-
wantch2 map[string]string
1068+
wantch1 map[string]*string
1069+
wantch2 map[string]*string
10721070
}{
10731071
{
10741072
name: "nested simple",
10751073
overrides: nil,
1076-
wantch1: map[string]string{"BAR": "fuu", "FOO": "bar"},
1077-
wantch2: map[string]string{"BAR": "fuu", "FOO": "bar", "FOO2": "bar2"},
1074+
wantch1: map[string]*string{"BAR": ptrstr("fuu"), "FOO": ptrstr("bar")},
1075+
wantch2: map[string]*string{"BAR": ptrstr("fuu"), "FOO": ptrstr("bar"), "FOO2": ptrstr("bar2")},
10781076
},
10791077
{
10801078
name: "nested with overrides first",
10811079
overrides: []string{"grandparent.args.BAR=fii", "child1.args.FOO=baaar"},
1082-
wantch1: map[string]string{"BAR": "fii", "FOO": "baaar"},
1083-
wantch2: map[string]string{"BAR": "fii", "FOO": "bar", "FOO2": "bar2"},
1080+
wantch1: map[string]*string{"BAR": ptrstr("fii"), "FOO": ptrstr("baaar")},
1081+
wantch2: map[string]*string{"BAR": ptrstr("fii"), "FOO": ptrstr("bar"), "FOO2": ptrstr("bar2")},
10841082
},
10851083
{
10861084
name: "nested with overrides last",
10871085
overrides: []string{"grandparent.args.BAR=fii", "child2.args.FOO=baaar"},
1088-
wantch1: map[string]string{"BAR": "fii", "FOO": "bar"},
1089-
wantch2: map[string]string{"BAR": "fii", "FOO": "baaar", "FOO2": "bar2"},
1086+
wantch1: map[string]*string{"BAR": ptrstr("fii"), "FOO": ptrstr("bar")},
1087+
wantch2: map[string]*string{"BAR": ptrstr("fii"), "FOO": ptrstr("baaar"), "FOO2": ptrstr("bar2")},
10901088
},
10911089
}
10921090
for _, tt := range cases {
@@ -1285,8 +1283,70 @@ services:
12851283

12861284
require.Equal(t, 1, len(c.Targets))
12871285
require.Equal(t, "app", c.Targets[0].Name)
1288-
require.Equal(t, "foo", c.Targets[0].Args["v1"])
1289-
require.Equal(t, "bar", c.Targets[0].Args["v2"])
1286+
require.Equal(t, ptrstr("foo"), c.Targets[0].Args["v1"])
1287+
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["v2"])
12901288
require.Equal(t, "dir", *c.Targets[0].Context)
12911289
require.Equal(t, "Dockerfile-alternate", *c.Targets[0].Dockerfile)
12921290
}
1291+
1292+
func TestHCLNullVars(t *testing.T) {
1293+
fp := File{
1294+
Name: "docker-bake.hcl",
1295+
Data: []byte(
1296+
`variable "FOO" {
1297+
default = null
1298+
}
1299+
target "default" {
1300+
args = {
1301+
foo = FOO
1302+
bar = "baz"
1303+
}
1304+
}`),
1305+
}
1306+
1307+
ctx := context.TODO()
1308+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil)
1309+
require.NoError(t, err)
1310+
1311+
require.Equal(t, 1, len(m))
1312+
_, ok := m["default"]
1313+
require.True(t, ok)
1314+
1315+
_, err = TargetsToBuildOpt(m, &Input{})
1316+
require.NoError(t, err)
1317+
require.Equal(t, map[string]*string{"bar": ptrstr("baz")}, m["default"].Args)
1318+
}
1319+
1320+
func TestJSONNullVars(t *testing.T) {
1321+
fp := File{
1322+
Name: "docker-bake.json",
1323+
Data: []byte(
1324+
`{
1325+
"variable": {
1326+
"FOO": {
1327+
"default": null
1328+
}
1329+
},
1330+
"target": {
1331+
"default": {
1332+
"args": {
1333+
"foo": "${FOO}",
1334+
"bar": "baz"
1335+
}
1336+
}
1337+
}
1338+
}`),
1339+
}
1340+
1341+
ctx := context.TODO()
1342+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil)
1343+
require.NoError(t, err)
1344+
1345+
require.Equal(t, 1, len(m))
1346+
_, ok := m["default"]
1347+
require.True(t, ok)
1348+
1349+
_, err = TargetsToBuildOpt(m, &Input{})
1350+
require.NoError(t, err)
1351+
require.Equal(t, map[string]*string{"bar": ptrstr("baz")}, m["default"].Args)
1352+
}

bake/compose.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,16 @@ func loadDotEnv(curenv map[string]string, workingDir string) (map[string]string,
193193
return curenv, nil
194194
}
195195

196-
func flatten(in compose.MappingWithEquals) compose.Mapping {
196+
func flatten(in compose.MappingWithEquals) map[string]*string {
197197
if len(in) == 0 {
198198
return nil
199199
}
200-
out := compose.Mapping{}
200+
out := map[string]*string{}
201201
for k, v := range in {
202202
if v == nil {
203203
continue
204204
}
205-
out[k] = *v
205+
out[k] = v
206206
}
207207
return out
208208
}

0 commit comments

Comments
 (0)