Skip to content

Commit 8a035b5

Browse files
committed
bake: support += operator to append with overrides
Signed-off-by: CrazyMax <[email protected]>
1 parent 921b576 commit 8a035b5

File tree

3 files changed

+148
-19
lines changed

3 files changed

+148
-19
lines changed

bake/bake.go

+63-16
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 {
@@ -534,16 +535,23 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
534535
}
535536

536537
pattern := keys[0]
537-
if len(parts) != 2 && keys[1] != "args" {
538+
attrName := strings.TrimSuffix(keys[1], "+")
539+
if len(parts) != 2 && attrName != "args" {
538540
return nil, errors.Errorf("invalid override %s, expected target.name=value", v)
539541
}
540542

543+
appendTo := false
544+
if strings.HasSuffix(keys[1], "+") {
545+
appendTo = true
546+
}
547+
541548
names, err := c.expandTargets(pattern)
542549
if err != nil {
543550
return nil, err
544551
}
545552

546553
kk := strings.SplitN(parts[0], ".", 2)
554+
overrideKey := strings.TrimSuffix(kk[1], "+")
547555

548556
for _, name := range names {
549557
t, ok := m[name]
@@ -552,14 +560,17 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
552560
m[name] = t
553561
}
554562

555-
o := t[kk[1]]
563+
override := t[overrideKey]
556564

557565
// IMPORTANT: if you add more fields here, do not forget to update
558566
// docs/reference/buildx_bake.md (--set) and https://docs.docker.com/build/bake/overrides/
559-
switch keys[1] {
567+
switch attrName {
560568
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network", "annotations":
561569
if len(parts) == 2 {
562-
o.ArrValue = append(o.ArrValue, parts[1])
570+
if appendTo {
571+
override.Append = true
572+
}
573+
override.ArrValue = append(override.ArrValue, parts[1])
563574
}
564575
case "args":
565576
if len(keys) != 3 {
@@ -570,7 +581,7 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
570581
if !ok {
571582
continue
572583
}
573-
o.Value = v
584+
override.Value = v
574585
}
575586
fallthrough
576587
case "contexts":
@@ -580,11 +591,11 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
580591
fallthrough
581592
default:
582593
if len(parts) == 2 {
583-
o.Value = parts[1]
594+
override.Value = parts[1]
584595
}
585596
}
586597

587-
t[kk[1]] = o
598+
t[overrideKey] = override
588599
}
589600
}
590601
return m, nil
@@ -896,13 +907,21 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
896907
}
897908
t.Labels[keys[1]] = &value
898909
case "tags":
899-
t.Tags = o.ArrValue
910+
if o.Append {
911+
t.Tags = append(t.Tags, o.ArrValue...)
912+
} else {
913+
t.Tags = o.ArrValue
914+
}
900915
case "cache-from":
901916
cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
902917
if err != nil {
903918
return err
904919
}
905-
t.CacheFrom = cacheFrom
920+
if o.Append {
921+
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
922+
} else {
923+
t.CacheFrom = cacheFrom
924+
}
906925
for _, c := range t.CacheFrom {
907926
if c.Type == "local" {
908927
if v, ok := c.Attrs["src"]; ok {
@@ -915,7 +934,11 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
915934
if err != nil {
916935
return err
917936
}
918-
t.CacheTo = cacheTo
937+
if o.Append {
938+
t.CacheTo = t.CacheTo.Merge(cacheTo)
939+
} else {
940+
t.CacheTo = cacheTo
941+
}
919942
for _, c := range t.CacheTo {
920943
if c.Type == "local" {
921944
if v, ok := c.Attrs["dest"]; ok {
@@ -932,7 +955,11 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
932955
if err != nil {
933956
return errors.Wrap(err, "invalid value for outputs")
934957
}
935-
t.Secrets = secrets
958+
if o.Append {
959+
t.Secrets = t.Secrets.Merge(secrets)
960+
} else {
961+
t.Secrets = secrets
962+
}
936963
for _, s := range t.Secrets {
937964
if s.FilePath != "" {
938965
ent.FSRead = append(ent.FSRead, s.FilePath)
@@ -943,18 +970,30 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
943970
if err != nil {
944971
return errors.Wrap(err, "invalid value for outputs")
945972
}
946-
t.SSH = ssh
973+
if o.Append {
974+
t.SSH = t.SSH.Merge(ssh)
975+
} else {
976+
t.SSH = ssh
977+
}
947978
for _, s := range t.SSH {
948979
ent.FSRead = append(ent.FSRead, s.Paths...)
949980
}
950981
case "platform":
951-
t.Platforms = o.ArrValue
982+
if o.Append {
983+
t.Platforms = append(t.Platforms, o.ArrValue...)
984+
} else {
985+
t.Platforms = o.ArrValue
986+
}
952987
case "output":
953988
outputs, err := parseArrValue[buildflags.ExportEntry](o.ArrValue)
954989
if err != nil {
955990
return errors.Wrap(err, "invalid value for outputs")
956991
}
957-
t.Outputs = outputs
992+
if o.Append {
993+
t.Outputs = t.Outputs.Merge(outputs)
994+
} else {
995+
t.Outputs = outputs
996+
}
958997
for _, o := range t.Outputs {
959998
if o.Destination != "" {
960999
ent.FSWrite = append(ent.FSWrite, o.Destination)
@@ -984,11 +1023,19 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
9841023
}
9851024
t.NoCache = &noCache
9861025
case "no-cache-filter":
987-
t.NoCacheFilter = o.ArrValue
1026+
if o.Append {
1027+
t.NoCacheFilter = append(t.NoCacheFilter, o.ArrValue...)
1028+
} else {
1029+
t.NoCacheFilter = o.ArrValue
1030+
}
9881031
case "shm-size":
9891032
t.ShmSize = &value
9901033
case "ulimits":
991-
t.Ulimits = o.ArrValue
1034+
if o.Append {
1035+
t.Ulimits = append(t.Ulimits, o.ArrValue...)
1036+
} else {
1037+
t.Ulimits = o.ArrValue
1038+
}
9921039
case "network":
9931040
t.NetworkMode = &value
9941041
case "pull":

bake/bake_test.go

+62
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,43 @@ 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("SecretsOverride", func(t *testing.T) {
195+
t.Setenv("FOO", "foo")
196+
t.Setenv("BAR", "bar")
197+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets=id=BAR,env=BAR"}, nil, &EntitlementConf{})
198+
require.NoError(t, err)
199+
require.Len(t, m["webapp"].Secrets, 1)
200+
require.Equal(t, "BAR", m["webapp"].Secrets[0].ID)
201+
})
202+
203+
t.Run("SecretsAppend", func(t *testing.T) {
204+
t.Setenv("FOO", "foo")
205+
t.Setenv("BAR", "bar")
206+
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets+=id=BAR,env=BAR"}, nil, &EntitlementConf{})
207+
require.NoError(t, err)
208+
require.Len(t, m["webapp"].Secrets, 2)
209+
require.Equal(t, "FOO", m["webapp"].Secrets[0].ID)
210+
require.Equal(t, "BAR", m["webapp"].Secrets[1].ID)
211+
})
212+
151213
t.Run("ShmSizeOverride", func(t *testing.T) {
152214
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil, &EntitlementConf{})
153215
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)