Skip to content

Commit 4c6eba5

Browse files
committed
bake: support += operator to append with overrides
Signed-off-by: CrazyMax <[email protected]>
1 parent e2d52a8 commit 4c6eba5

File tree

3 files changed

+149
-21
lines changed

3 files changed

+149
-21
lines changed

bake/bake.go

+58-18
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type File struct {
4545
type Override struct {
4646
Value string
4747
ArrValue []string
48+
Append bool
4849
}
4950

5051
func defaultFilenames() []string {
@@ -528,9 +529,12 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
528529
m := map[string]map[string]Override{}
529530
for _, v := range v {
530531
parts := strings.SplitN(v, "=", 2)
531-
keys := strings.SplitN(parts[0], ".", 3)
532+
533+
skey := strings.TrimSuffix(parts[0], "+")
534+
appendTo := strings.HasSuffix(parts[0], "+")
535+
keys := strings.SplitN(skey, ".", 3)
532536
if len(keys) < 2 {
533-
return nil, errors.Errorf("invalid override key %s, expected target.name", parts[0])
537+
return nil, errors.Errorf("invalid override key %s, expected target.name", skey)
534538
}
535539

536540
pattern := keys[0]
@@ -543,23 +547,23 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
543547
return nil, err
544548
}
545549

546-
kk := strings.SplitN(parts[0], ".", 2)
547-
550+
okey := strings.Join(keys[1:], ".")
548551
for _, name := range names {
549552
t, ok := m[name]
550553
if !ok {
551554
t = map[string]Override{}
552555
m[name] = t
553556
}
554557

555-
o := t[kk[1]]
558+
override := t[okey]
556559

557560
// IMPORTANT: if you add more fields here, do not forget to update
558561
// docs/reference/buildx_bake.md (--set) and https://docs.docker.com/build/bake/overrides/
559562
switch keys[1] {
560563
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network", "annotations":
561564
if len(parts) == 2 {
562-
o.ArrValue = append(o.ArrValue, parts[1])
565+
override.Append = appendTo
566+
override.ArrValue = append(override.ArrValue, parts[1])
563567
}
564568
case "args":
565569
if len(keys) != 3 {
@@ -570,7 +574,7 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
570574
if !ok {
571575
continue
572576
}
573-
o.Value = v
577+
override.Value = v
574578
}
575579
fallthrough
576580
case "contexts":
@@ -580,11 +584,11 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
580584
fallthrough
581585
default:
582586
if len(parts) == 2 {
583-
o.Value = parts[1]
587+
override.Value = parts[1]
584588
}
585589
}
586590

587-
t[kk[1]] = o
591+
t[okey] = override
588592
}
589593
}
590594
return m, nil
@@ -896,13 +900,21 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
896900
}
897901
t.Labels[keys[1]] = &value
898902
case "tags":
899-
t.Tags = o.ArrValue
903+
if o.Append {
904+
t.Tags = append(t.Tags, o.ArrValue...)
905+
} else {
906+
t.Tags = o.ArrValue
907+
}
900908
case "cache-from":
901909
cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
902910
if err != nil {
903911
return err
904912
}
905-
t.CacheFrom = cacheFrom
913+
if o.Append {
914+
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
915+
} else {
916+
t.CacheFrom = cacheFrom
917+
}
906918
for _, c := range t.CacheFrom {
907919
if c.Type == "local" {
908920
if v, ok := c.Attrs["src"]; ok {
@@ -915,7 +927,11 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
915927
if err != nil {
916928
return err
917929
}
918-
t.CacheTo = cacheTo
930+
if o.Append {
931+
t.CacheTo = t.CacheTo.Merge(cacheTo)
932+
} else {
933+
t.CacheTo = cacheTo
934+
}
919935
for _, c := range t.CacheTo {
920936
if c.Type == "local" {
921937
if v, ok := c.Attrs["dest"]; ok {
@@ -932,7 +948,11 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
932948
if err != nil {
933949
return errors.Wrap(err, "invalid value for outputs")
934950
}
935-
t.Secrets = secrets
951+
if o.Append {
952+
t.Secrets = t.Secrets.Merge(secrets)
953+
} else {
954+
t.Secrets = secrets
955+
}
936956
for _, s := range t.Secrets {
937957
if s.FilePath != "" {
938958
ent.FSRead = append(ent.FSRead, s.FilePath)
@@ -943,18 +963,30 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
943963
if err != nil {
944964
return errors.Wrap(err, "invalid value for outputs")
945965
}
946-
t.SSH = ssh
966+
if o.Append {
967+
t.SSH = t.SSH.Merge(ssh)
968+
} else {
969+
t.SSH = ssh
970+
}
947971
for _, s := range t.SSH {
948972
ent.FSRead = append(ent.FSRead, s.Paths...)
949973
}
950974
case "platform":
951-
t.Platforms = o.ArrValue
975+
if o.Append {
976+
t.Platforms = append(t.Platforms, o.ArrValue...)
977+
} else {
978+
t.Platforms = o.ArrValue
979+
}
952980
case "output":
953981
outputs, err := parseArrValue[buildflags.ExportEntry](o.ArrValue)
954982
if err != nil {
955983
return errors.Wrap(err, "invalid value for outputs")
956984
}
957-
t.Outputs = outputs
985+
if o.Append {
986+
t.Outputs = t.Outputs.Merge(outputs)
987+
} else {
988+
t.Outputs = outputs
989+
}
958990
for _, o := range t.Outputs {
959991
if o.Destination != "" {
960992
ent.FSWrite = append(ent.FSWrite, o.Destination)
@@ -984,11 +1016,19 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
9841016
}
9851017
t.NoCache = &noCache
9861018
case "no-cache-filter":
987-
t.NoCacheFilter = o.ArrValue
1019+
if o.Append {
1020+
t.NoCacheFilter = append(t.NoCacheFilter, o.ArrValue...)
1021+
} else {
1022+
t.NoCacheFilter = o.ArrValue
1023+
}
9881024
case "shm-size":
9891025
t.ShmSize = &value
9901026
case "ulimits":
991-
t.Ulimits = o.ArrValue
1027+
if o.Append {
1028+
t.Ulimits = append(t.Ulimits, o.ArrValue...)
1029+
} else {
1030+
t.Ulimits = o.ArrValue
1031+
}
9921032
case "network":
9931033
t.NetworkMode = &value
9941034
case "pull":

bake/bake_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ target "webapp" {
3737
annotations = [
3838
"index,manifest:org.opencontainers.image.authors=dvdksn"
3939
]
40+
attest = [
41+
"type=provenance,mode=max"
42+
]
43+
platforms = [
44+
"linux/amd64"
45+
]
46+
secret = [
47+
"id=FOO,env=FOO"
48+
]
4049
inherits = ["webDEP"]
4150
}`),
4251
}
@@ -127,6 +136,22 @@ target "webapp" {
127136
require.Equal(t, []string{"webapp"}, g["default"].Targets)
128137
})
129138

139+
t.Run("AttestOverride", func(t *testing.T) {
140+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.attest=type=sbom"}, nil, &EntitlementConf{})
141+
require.NoError(t, err)
142+
require.Len(t, m["webapp"].Attest, 2)
143+
require.Equal(t, "provenance", m["webapp"].Attest[0].Type)
144+
require.Equal(t, "sbom", m["webapp"].Attest[1].Type)
145+
})
146+
147+
t.Run("AttestAppend", func(t *testing.T) {
148+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.attest+=type=sbom"}, nil, &EntitlementConf{})
149+
require.NoError(t, err)
150+
require.Len(t, m["webapp"].Attest, 2)
151+
require.Equal(t, "provenance", m["webapp"].Attest[0].Type)
152+
require.Equal(t, "sbom", m["webapp"].Attest[1].Type)
153+
})
154+
130155
t.Run("ContextOverride", func(t *testing.T) {
131156
t.Parallel()
132157
_, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil, &EntitlementConf{})
@@ -148,6 +173,49 @@ target "webapp" {
148173
require.Equal(t, []string{"webapp"}, g["default"].Targets)
149174
})
150175

176+
t.Run("PlatformOverride", func(t *testing.T) {
177+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform=linux/arm64"}, nil, &EntitlementConf{})
178+
require.NoError(t, err)
179+
require.Equal(t, []string{"linux/arm64"}, m["webapp"].Platforms)
180+
})
181+
182+
t.Run("PlatformAppend", func(t *testing.T) {
183+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64"}, nil, &EntitlementConf{})
184+
require.NoError(t, err)
185+
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, m["webapp"].Platforms)
186+
})
187+
188+
t.Run("PlatformAppendMulti", func(t *testing.T) {
189+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64", "webapp.platform+=linux/riscv64"}, nil, &EntitlementConf{})
190+
require.NoError(t, err)
191+
require.Equal(t, []string{"linux/amd64", "linux/arm64", "linux/riscv64"}, m["webapp"].Platforms)
192+
})
193+
194+
t.Run("PlatformAppendMultiLastOverride", func(t *testing.T) {
195+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64", "webapp.platform=linux/riscv64"}, nil, &EntitlementConf{})
196+
require.NoError(t, err)
197+
require.Equal(t, []string{"linux/arm64", "linux/riscv64"}, m["webapp"].Platforms)
198+
})
199+
200+
t.Run("SecretsOverride", func(t *testing.T) {
201+
t.Setenv("FOO", "foo")
202+
t.Setenv("BAR", "bar")
203+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets=id=BAR,env=BAR"}, nil, &EntitlementConf{})
204+
require.NoError(t, err)
205+
require.Len(t, m["webapp"].Secrets, 1)
206+
require.Equal(t, "BAR", m["webapp"].Secrets[0].ID)
207+
})
208+
209+
t.Run("SecretsAppend", func(t *testing.T) {
210+
t.Setenv("FOO", "foo")
211+
t.Setenv("BAR", "bar")
212+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets+=id=BAR,env=BAR"}, nil, &EntitlementConf{})
213+
require.NoError(t, err)
214+
require.Len(t, m["webapp"].Secrets, 2)
215+
require.Equal(t, "FOO", m["webapp"].Secrets[0].ID)
216+
require.Equal(t, "BAR", m["webapp"].Secrets[1].ID)
217+
})
218+
151219
t.Run("ShmSizeOverride", func(t *testing.T) {
152220
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil, &EntitlementConf{})
153221
require.NoError(t, err)

docs/reference/buildx_bake.md

+23-3
Original file line numberDiff line numberDiff line change
@@ -347,19 +347,22 @@ is defined in https://golang.org/pkg/path/#Match.
347347
```console
348348
$ docker buildx bake --set target.args.mybuildarg=value
349349
$ docker buildx bake --set target.platform=linux/arm64
350-
$ docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo'
351-
$ docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets
352-
$ docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo'
350+
$ docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo'
351+
$ docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets
352+
$ docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo'
353+
$ docker buildx bake --set target.platform+=linux/arm64 # appends 'linux/arm64' to the platform list
353354
```
354355

355356
You can override the following fields:
356357

357358
* `annotations`
359+
* `attest`
358360
* `args`
359361
* `cache-from`
360362
* `cache-to`
361363
* `context`
362364
* `dockerfile`
365+
* `entitlements`
363366
* `labels`
364367
* `load`
365368
* `no-cache`
@@ -372,3 +375,20 @@ You can override the following fields:
372375
* `ssh`
373376
* `tags`
374377
* `target`
378+
379+
You can append using `+=` operator for the following fields:
380+
381+
* `annotations`¹
382+
* `attest`¹
383+
* `cache-from`
384+
* `cache-to`
385+
* `entitlements`¹
386+
* `no-cache-filter`
387+
* `output`
388+
* `platform`
389+
* `secrets`
390+
* `ssh`
391+
* `tags`
392+
393+
> [!NOTE]
394+
> ¹ These fields already append by default.

0 commit comments

Comments
 (0)