diff --git a/bake/compose.go b/bake/compose.go index daad607a..08aa96df 100644 --- a/bake/compose.go +++ b/bake/compose.go @@ -9,6 +9,7 @@ import ( "github.com/compose-spec/compose-go/loader" compose "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" + "gopkg.in/yaml.v3" ) func parseCompose(dt []byte) (*compose.Project, error) { @@ -135,90 +136,86 @@ func flatten(in compose.MappingWithEquals) compose.Mapping { return out } +// xbake Compose build extension provides fields not (yet) available in +// Compose build specification: https://github.com/compose-spec/compose-spec/blob/master/build.md +type xbake struct { + Tags stringArray `yaml:"tags,omitempty"` + CacheFrom stringArray `yaml:"cache-from,omitempty"` + CacheTo stringArray `yaml:"cache-to,omitempty"` + Secrets stringArray `yaml:"secret,omitempty"` + SSH stringArray `yaml:"ssh,omitempty"` + Platforms stringArray `yaml:"platforms,omitempty"` + Outputs stringArray `yaml:"output,omitempty"` + Pull *bool `yaml:"pull,omitempty"` + NoCache *bool `yaml:"no-cache,omitempty"` + NoCacheFilter stringArray `yaml:"no-cache-filter,omitempty"` + // don't forget to update documentation if you add a new field: + // docs/guides/bake/compose-file.md#extension-field-with-x-bake +} + +type stringArray []string + +func (sa *stringArray) UnmarshalYAML(unmarshal func(interface{}) error) error { + var multi []string + err := unmarshal(&multi) + if err != nil { + var single string + if err := unmarshal(&single); err != nil { + return err + } + *sa = strings.Fields(single) + } else { + *sa = multi + } + return nil +} + // composeExtTarget converts Compose build extension x-bake to bake Target // https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension func (t *Target) composeExtTarget(exts map[string]interface{}) error { - if ext, ok := exts["x-bake"]; ok { - for key, val := range ext.(map[string]interface{}) { - switch key { - case "tags": - if res, k := val.(string); k { - t.Tags = append(t.Tags, res) - } else { - for _, res := range val.([]interface{}) { - t.Tags = append(t.Tags, res.(string)) - } - } - case "cache-from": - t.CacheFrom = []string{} // Needed to override the main field - if res, k := val.(string); k { - t.CacheFrom = append(t.CacheFrom, res) - } else { - for _, res := range val.([]interface{}) { - t.CacheFrom = append(t.CacheFrom, res.(string)) - } - } - case "cache-to": - if res, k := val.(string); k { - t.CacheTo = append(t.CacheTo, res) - } else { - for _, res := range val.([]interface{}) { - t.CacheTo = append(t.CacheTo, res.(string)) - } - } - case "secret": - if res, k := val.(string); k { - t.Secrets = append(t.Secrets, res) - } else { - for _, res := range val.([]interface{}) { - t.Secrets = append(t.Secrets, res.(string)) - } - } - case "ssh": - if res, k := val.(string); k { - t.SSH = append(t.SSH, res) - } else { - for _, res := range val.([]interface{}) { - t.SSH = append(t.SSH, res.(string)) - } - } - case "platforms": - if res, k := val.(string); k { - t.Platforms = append(t.Platforms, res) - } else { - for _, res := range val.([]interface{}) { - t.Platforms = append(t.Platforms, res.(string)) - } - } - case "output": - if res, k := val.(string); k { - t.Outputs = append(t.Outputs, res) - } else { - for _, res := range val.([]interface{}) { - t.Outputs = append(t.Outputs, res.(string)) - } - } - case "pull": - if res, ok := val.(bool); ok { - t.Pull = &res - } - case "no-cache": - if res, ok := val.(bool); ok { - t.NoCache = &res - } - case "no-cache-filter": - if res, k := val.(string); k { - t.NoCacheFilter = append(t.NoCacheFilter, res) - } else { - for _, res := range val.([]interface{}) { - t.NoCacheFilter = append(t.NoCacheFilter, res.(string)) - } - } - default: - return fmt.Errorf("compose file invalid: unkwown %s field for x-bake", key) - } - } + var xb xbake + + ext, ok := exts["x-bake"] + if !ok || ext == nil { + return nil } + + yb, _ := yaml.Marshal(ext) + if err := yaml.Unmarshal(yb, &xb); err != nil { + return err + } + + if len(xb.Tags) > 0 { + t.Tags = append(t.Tags, xb.Tags...) + } + if len(xb.CacheFrom) > 0 { + t.CacheFrom = xb.CacheFrom // override main field + } + if len(xb.CacheTo) > 0 { + t.CacheTo = append(t.CacheTo, xb.CacheTo...) + } + if len(xb.Secrets) > 0 { + t.Secrets = append(t.Secrets, xb.Secrets...) + } + if len(xb.SSH) > 0 { + t.SSH = append(t.SSH, xb.SSH...) + } + if len(xb.Platforms) > 0 { + t.Platforms = append(t.Platforms, xb.Platforms...) + } + if len(xb.Outputs) > 0 { + t.Outputs = append(t.Outputs, xb.Outputs...) + } + if xb.Pull != nil { + t.Pull = xb.Pull + } + if xb.NoCache != nil { + t.NoCache = xb.NoCache + } + if len(xb.NoCacheFilter) > 0 { + t.NoCacheFilter = append(t.NoCacheFilter, xb.NoCacheFilter...) + } + return nil } diff --git a/bake/compose_test.go b/bake/compose_test.go index 9b279d00..0735ee80 100644 --- a/bake/compose_test.go +++ b/bake/compose_test.go @@ -273,7 +273,7 @@ services: - linux/arm64 cache-from: - type=local,src=path/to/cache - cache-to: local,dest=path/to/cache + cache-to: type=local,dest=path/to/cache pull: true aws: @@ -303,7 +303,7 @@ services: require.Equal(t, c.Targets[0].Tags, []string{"ct-addon:baz", "ct-addon:foo", "ct-addon:alp"}) require.Equal(t, c.Targets[0].Platforms, []string{"linux/amd64", "linux/arm64"}) require.Equal(t, c.Targets[0].CacheFrom, []string{"type=local,src=path/to/cache"}) - require.Equal(t, c.Targets[0].CacheTo, []string{"local,dest=path/to/cache"}) + require.Equal(t, c.Targets[0].CacheTo, []string{"type=local,dest=path/to/cache"}) require.Equal(t, c.Targets[0].Pull, newBool(true)) require.Equal(t, c.Targets[1].Tags, []string{"ct-fake-aws:bar"}) require.Equal(t, c.Targets[1].Secrets, []string{"id=mysecret,src=/local/secret", "id=mysecret2,src=/local/secret2"}) diff --git a/go.mod b/go.mod index 33473ef5..36501fe2 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( go.opentelemetry.io/otel/trace v1.4.1 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c google.golang.org/grpc v1.45.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.23.4 k8s.io/apimachinery v0.23.4 k8s.io/client-go v0.23.4 @@ -139,7 +140,6 @@ require ( gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect k8s.io/klog/v2 v2.30.0 // indirect k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect diff --git a/go.sum b/go.sum index 1d818713..ad1eb6d2 100644 --- a/go.sum +++ b/go.sum @@ -1124,8 +1124,9 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= diff --git a/vendor/gopkg.in/yaml.v3/parserc.go b/vendor/gopkg.in/yaml.v3/parserc.go index ac66fccc..268558a0 100644 --- a/vendor/gopkg.in/yaml.v3/parserc.go +++ b/vendor/gopkg.in/yaml.v3/parserc.go @@ -687,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -786,7 +789,7 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { } token := peek_token(parser) - if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { + if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { return } @@ -813,6 +816,9 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -922,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } diff --git a/vendor/modules.txt b/vendor/modules.txt index d85fec68..69f69338 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -761,7 +761,7 @@ gopkg.in/inf.v0 # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2 -# gopkg.in/yaml.v3 v3.0.0 +# gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 # k8s.io/api v0.23.4 => k8s.io/api v0.22.4