Skip to content

Commit cb185f0

Browse files
authored
Merge pull request #721 from crazy-max/compose-ext
bake: `x-bake` extension field with compose
2 parents 04bac63 + 89e126f commit cb185f0

File tree

3 files changed

+255
-2
lines changed

3 files changed

+255
-2
lines changed

bake/compose.go

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@ func ParseCompose(dt []byte) (*Config, error) {
8080
return val, ok
8181
})),
8282
CacheFrom: s.Build.CacheFrom,
83-
// TODO: add platforms
83+
}
84+
if err = t.composeExtTarget(s.Build.Extensions); err != nil {
85+
return nil, err
8486
}
8587
if s.Build.Target != "" {
8688
target := s.Build.Target
8789
t.Target = &target
8890
}
89-
if s.Image != "" {
91+
if len(t.Tags) == 0 && s.Image != "" {
9092
t.Tags = []string{s.Image}
9193
}
9294
c.Targets = append(c.Targets, t)
@@ -111,3 +113,82 @@ func flatten(in compose.MappingWithEquals) compose.Mapping {
111113
}
112114
return out
113115
}
116+
117+
// composeExtTarget converts Compose build extension x-bake to bake Target
118+
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension
119+
func (t *Target) composeExtTarget(exts map[string]interface{}) error {
120+
if ext, ok := exts["x-bake"]; ok {
121+
for key, val := range ext.(map[string]interface{}) {
122+
switch key {
123+
case "tags":
124+
if res, k := val.(string); k {
125+
t.Tags = append(t.Tags, res)
126+
} else {
127+
for _, res := range val.([]interface{}) {
128+
t.Tags = append(t.Tags, res.(string))
129+
}
130+
}
131+
case "cache-from":
132+
t.CacheFrom = []string{} // Needed to override the main field
133+
if res, k := val.(string); k {
134+
t.CacheFrom = append(t.CacheFrom, res)
135+
} else {
136+
for _, res := range val.([]interface{}) {
137+
t.CacheFrom = append(t.CacheFrom, res.(string))
138+
}
139+
}
140+
case "cache-to":
141+
if res, k := val.(string); k {
142+
t.CacheTo = append(t.CacheTo, res)
143+
} else {
144+
for _, res := range val.([]interface{}) {
145+
t.CacheTo = append(t.CacheTo, res.(string))
146+
}
147+
}
148+
case "secret":
149+
if res, k := val.(string); k {
150+
t.Secrets = append(t.Secrets, res)
151+
} else {
152+
for _, res := range val.([]interface{}) {
153+
t.Secrets = append(t.Secrets, res.(string))
154+
}
155+
}
156+
case "ssh":
157+
if res, k := val.(string); k {
158+
t.SSH = append(t.SSH, res)
159+
} else {
160+
for _, res := range val.([]interface{}) {
161+
t.SSH = append(t.SSH, res.(string))
162+
}
163+
}
164+
case "platforms":
165+
if res, k := val.(string); k {
166+
t.Platforms = append(t.Platforms, res)
167+
} else {
168+
for _, res := range val.([]interface{}) {
169+
t.Platforms = append(t.Platforms, res.(string))
170+
}
171+
}
172+
case "output":
173+
if res, k := val.(string); k {
174+
t.Outputs = append(t.Outputs, res)
175+
} else {
176+
for _, res := range val.([]interface{}) {
177+
t.Outputs = append(t.Outputs, res.(string))
178+
}
179+
}
180+
case "pull":
181+
if res, ok := val.(bool); ok {
182+
t.Pull = &res
183+
}
184+
case "no-cache":
185+
if res, ok := val.(bool); ok {
186+
t.NoCache = &res
187+
}
188+
default:
189+
return fmt.Errorf("compose file invalid: unkwown %s field for x-bake", key)
190+
}
191+
}
192+
}
193+
return nil
194+
}

bake/compose_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,70 @@ networks:
214214
_, err := ParseCompose(dt)
215215
require.NoError(t, err)
216216
}
217+
218+
func TestComposeExt(t *testing.T) {
219+
var dt = []byte(`
220+
services:
221+
addon:
222+
image: ct-addon:bar
223+
build:
224+
context: .
225+
dockerfile: ./Dockerfile
226+
cache_from:
227+
- user/app:cache
228+
args:
229+
CT_ECR: foo
230+
CT_TAG: bar
231+
x-bake:
232+
tags:
233+
- ct-addon:foo
234+
- ct-addon:alp
235+
platforms:
236+
- linux/amd64
237+
- linux/arm64
238+
cache-from:
239+
- type=local,src=path/to/cache
240+
cache-to: local,dest=path/to/cache
241+
pull: true
242+
243+
aws:
244+
image: ct-fake-aws:bar
245+
build:
246+
dockerfile: ./aws.Dockerfile
247+
args:
248+
CT_ECR: foo
249+
CT_TAG: bar
250+
x-bake:
251+
secret:
252+
- id=mysecret,src=/local/secret
253+
- id=mysecret2,src=/local/secret2
254+
ssh: default
255+
platforms: linux/arm64
256+
output: type=docker
257+
no-cache: true
258+
`)
259+
260+
c, err := ParseCompose(dt)
261+
require.NoError(t, err)
262+
require.Equal(t, 2, len(c.Targets))
263+
sort.Slice(c.Targets, func(i, j int) bool {
264+
return c.Targets[i].Name < c.Targets[j].Name
265+
})
266+
require.Equal(t, c.Targets[0].Args, map[string]string{"CT_ECR": "foo", "CT_TAG": "bar"})
267+
require.Equal(t, c.Targets[0].Tags, []string{"ct-addon:foo", "ct-addon:alp"})
268+
require.Equal(t, c.Targets[0].Platforms, []string{"linux/amd64", "linux/arm64"})
269+
require.Equal(t, c.Targets[0].CacheFrom, []string{"type=local,src=path/to/cache"})
270+
require.Equal(t, c.Targets[0].CacheTo, []string{"local,dest=path/to/cache"})
271+
require.Equal(t, c.Targets[0].Pull, newBool(true))
272+
require.Equal(t, c.Targets[1].Tags, []string{"ct-fake-aws:bar"})
273+
require.Equal(t, c.Targets[1].Secrets, []string{"id=mysecret,src=/local/secret", "id=mysecret2,src=/local/secret2"})
274+
require.Equal(t, c.Targets[1].SSH, []string{"default"})
275+
require.Equal(t, c.Targets[1].Platforms, []string{"linux/arm64"})
276+
require.Equal(t, c.Targets[1].Outputs, []string{"type=docker"})
277+
require.Equal(t, c.Targets[1].NoCache, newBool(true))
278+
}
279+
280+
func newBool(val bool) *bool {
281+
b := val
282+
return &b
283+
}

docs/reference/buildx_bake.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,3 +756,108 @@ $ docker buildx bake --print app
756756
}
757757
}
758758
```
759+
760+
### Extension field with Compose
761+
762+
[Special extension](https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension)
763+
field `x-bake` can be used in your compose file to evaluate fields that are not
764+
(yet) available in the [build definition](https://github.com/compose-spec/compose-spec/blob/master/build.md#build-definition).
765+
766+
```yaml
767+
# docker-compose.yml
768+
services:
769+
addon:
770+
image: ct-addon:bar
771+
build:
772+
context: .
773+
dockerfile: ./Dockerfile
774+
args:
775+
CT_ECR: foo
776+
CT_TAG: bar
777+
x-bake:
778+
tags:
779+
- ct-addon:foo
780+
- ct-addon:alp
781+
platforms:
782+
- linux/amd64
783+
- linux/arm64
784+
cache-from:
785+
- user/app:cache
786+
- type=local,src=path/to/cache
787+
cache-to: type=local,dest=path/to/cache
788+
pull: true
789+
790+
aws:
791+
image: ct-fake-aws:bar
792+
build:
793+
dockerfile: ./aws.Dockerfile
794+
args:
795+
CT_ECR: foo
796+
CT_TAG: bar
797+
x-bake:
798+
secret:
799+
- id=mysecret,src=./secret
800+
- id=mysecret2,src=./secret2
801+
platforms: linux/arm64
802+
output: type=docker
803+
no-cache: true
804+
```
805+
806+
```console
807+
$ docker buildx bake --print
808+
{
809+
"target": {
810+
"addon": {
811+
"context": ".",
812+
"dockerfile": "./Dockerfile",
813+
"args": {
814+
"CT_ECR": "foo",
815+
"CT_TAG": "bar"
816+
},
817+
"tags": [
818+
"ct-addon:foo",
819+
"ct-addon:alp"
820+
],
821+
"cache-from": [
822+
"user/app:cache",
823+
"type=local,src=path/to/cache"
824+
],
825+
"cache-to": [
826+
"type=local,dest=path/to/cache"
827+
],
828+
"platforms": [
829+
"linux/amd64",
830+
"linux/arm64"
831+
],
832+
"pull": true
833+
},
834+
"aws": {
835+
"context": ".",
836+
"dockerfile": "./aws.Dockerfile",
837+
"args": {
838+
"CT_ECR": "foo",
839+
"CT_TAG": "bar"
840+
},
841+
"tags": [
842+
"ct-fake-aws:bar"
843+
],
844+
"secret": [
845+
"id=mysecret,src=./secret",
846+
"id=mysecret2,src=./secret2"
847+
],
848+
"platforms": [
849+
"linux/arm64"
850+
],
851+
"output": [
852+
"type=docker"
853+
],
854+
"no-cache": true
855+
}
856+
}
857+
}
858+
```
859+
860+
Complete list of valid fields for `x-bake`:
861+
862+
`tags`, `cache-from`, `cache-to`, `secret`, `ssh`, `platforms`, `output`,
863+
`pull`, `no-cache`

0 commit comments

Comments
 (0)