bake: generate implicit groups for matrixes

Signed-off-by: Justin Chadwell <me@jedevc.com>
pull/1690/head
Justin Chadwell 2 years ago
parent 77252f161c
commit 0806870261

@ -246,13 +246,28 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error)
} }
if len(hclFiles) > 0 { if len(hclFiles) > 0 {
if err := hclparser.Parse(hcl.MergeFiles(hclFiles), hclparser.Opt{ renamed, err := hclparser.Parse(hcl.MergeFiles(hclFiles), hclparser.Opt{
LookupVar: os.LookupEnv, LookupVar: os.LookupEnv,
Vars: defaults, Vars: defaults,
ValidateLabel: validateTargetName, ValidateLabel: validateTargetName,
}, &c); err.HasErrors() { }, &c)
if err.HasErrors() {
return nil, err return nil, err
} }
for _, renamed := range renamed {
for oldName, newNames := range renamed {
newNames = dedupSlice(newNames)
if len(newNames) == 1 && oldName == newNames[0] {
continue
}
c.Groups = append(c.Groups, &Group{
Name: oldName,
Targets: newNames,
})
}
}
c = dedupeConfig(c)
} }
return &c, nil return &c, nil

@ -666,6 +666,37 @@ func TestHCLRenameTarget(t *testing.T) {
require.Equal(t, 1, len(c.Targets)) require.Equal(t, 1, len(c.Targets))
require.Equal(t, "xyz", c.Targets[0].Name) require.Equal(t, "xyz", c.Targets[0].Name)
require.Equal(t, "foo", *c.Targets[0].Dockerfile) require.Equal(t, "foo", *c.Targets[0].Dockerfile)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "abc", c.Groups[0].Name)
require.Equal(t, []string{"xyz"}, c.Groups[0].Targets)
}
func TestHCLRenameGroup(t *testing.T) {
dt := []byte(`
group "foo" {
name = "bar"
targets = ["x", "y"]
}
target "x" {
}
target "y" {
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 2, len(c.Targets))
require.Equal(t, "x", c.Targets[0].Name)
require.Equal(t, "y", c.Targets[1].Name)
require.Equal(t, 2, len(c.Groups))
require.Equal(t, "bar", c.Groups[0].Name)
require.Equal(t, []string{"x", "y"}, c.Groups[0].Targets)
require.Equal(t, "foo", c.Groups[1].Name)
require.Equal(t, []string{"bar"}, c.Groups[1].Targets)
} }
func TestHCLRenameTargetAttrs(t *testing.T) { func TestHCLRenameTargetAttrs(t *testing.T) {
@ -736,7 +767,7 @@ func TestHCLRenameTargetAttrs(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
func TestHCLRenameMerge(t *testing.T) { func TestHCLRenameSplit(t *testing.T) {
dt := []byte(` dt := []byte(`
target "x" { target "x" {
name = "y" name = "y"
@ -757,6 +788,10 @@ func TestHCLRenameMerge(t *testing.T) {
require.Equal(t, "foo", *c.Targets[0].Dockerfile) require.Equal(t, "foo", *c.Targets[0].Dockerfile)
require.Equal(t, "z", c.Targets[1].Name) require.Equal(t, "z", c.Targets[1].Name)
require.Equal(t, "bar", *c.Targets[1].Dockerfile) require.Equal(t, "bar", *c.Targets[1].Dockerfile)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "x", c.Groups[0].Name)
require.Equal(t, []string{"y", "z"}, c.Groups[0].Targets)
} }
func TestHCLRenameMultiFile(t *testing.T) { func TestHCLRenameMultiFile(t *testing.T) {
@ -813,9 +848,13 @@ func TestHCLMatrixBasic(t *testing.T) {
require.Equal(t, c.Targets[1].Name, "y") require.Equal(t, c.Targets[1].Name, "y")
require.Equal(t, *c.Targets[0].Dockerfile, "x.Dockerfile") require.Equal(t, *c.Targets[0].Dockerfile, "x.Dockerfile")
require.Equal(t, *c.Targets[1].Dockerfile, "y.Dockerfile") require.Equal(t, *c.Targets[1].Dockerfile, "y.Dockerfile")
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"x", "y"}, c.Groups[0].Targets)
} }
func TestHCLMatrixMultiple(t *testing.T) { func TestHCLMatrixMultipleKeys(t *testing.T) {
dt := []byte(` dt := []byte(`
target "default" { target "default" {
matrix = { matrix = {
@ -835,7 +874,54 @@ func TestHCLMatrixMultiple(t *testing.T) {
for i, t := range c.Targets { for i, t := range c.Targets {
names[i] = t.Name names[i] = t.Name
} }
require.ElementsMatch(t, names, []string{"a-b-d", "a-b-e", "a-b-f", "a-c-d", "a-c-e", "a-c-f"}) require.ElementsMatch(t, []string{"a-b-d", "a-b-e", "a-b-f", "a-c-d", "a-c-e", "a-c-f"}, names)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.ElementsMatch(t, []string{"a-b-d", "a-b-e", "a-b-f", "a-c-d", "a-c-e", "a-c-f"}, c.Groups[0].Targets)
}
func TestHCLMatrixMultipleTargets(t *testing.T) {
dt := []byte(`
target "x" {
matrix = {
foo = ["a", "b"]
}
name = foo
}
target "y" {
matrix = {
bar = ["c", "d"]
}
name = bar
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 4, len(c.Targets))
names := make([]string, len(c.Targets))
for i, t := range c.Targets {
names[i] = t.Name
}
require.ElementsMatch(t, []string{"a", "b", "c", "d"}, names)
require.Equal(t, 2, len(c.Groups))
names = make([]string, len(c.Groups))
for i, c := range c.Groups {
names[i] = c.Name
}
require.ElementsMatch(t, []string{"x", "y"}, names)
for _, g := range c.Groups {
switch g.Name {
case "x":
require.Equal(t, []string{"a", "b"}, g.Targets)
case "y":
require.Equal(t, []string{"c", "d"}, g.Targets)
}
}
} }
func TestHCLMatrixArgs(t *testing.T) { func TestHCLMatrixArgs(t *testing.T) {

@ -531,7 +531,7 @@ func (p *parser) resolveBlockNames(block *hcl.Block) ([]string, error) {
return names, nil return names, nil
} }
func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics { func Parse(b hcl.Body, opt Opt, val interface{}) (map[string]map[string][]string, hcl.Diagnostics) {
reserved := map[string]struct{}{} reserved := map[string]struct{}{}
schema, _ := gohcl.ImpliedBodySchema(val) schema, _ := gohcl.ImpliedBodySchema(val)
@ -544,7 +544,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
var defs inputs var defs inputs
if err := gohcl.DecodeBody(b, nil, &defs); err != nil { if err := gohcl.DecodeBody(b, nil, &defs); err != nil {
return err return nil, err
} }
defsSchema, _ := gohcl.ImpliedBodySchema(defs) defsSchema, _ := gohcl.ImpliedBodySchema(defs)
@ -599,18 +599,18 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
content, b, diags := b.PartialContent(schema) content, b, diags := b.PartialContent(schema)
if diags.HasErrors() { if diags.HasErrors() {
return diags return nil, diags
} }
blocks, b, diags := b.PartialContent(defsSchema) blocks, b, diags := b.PartialContent(defsSchema)
if diags.HasErrors() { if diags.HasErrors() {
return diags return nil, diags
} }
attrs, diags := b.JustAttributes() attrs, diags := b.JustAttributes()
if diags.HasErrors() { if diags.HasErrors() {
if d := removeAttributesDiags(diags, reserved, p.vars); len(d) > 0 { if d := removeAttributesDiags(diags, reserved, p.vars); len(d) > 0 {
return d return nil, d
} }
} }
@ -627,7 +627,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
} }
for _, a := range content.Attributes { for _, a := range content.Attributes {
return hcl.Diagnostics{ return nil, hcl.Diagnostics{
&hcl.Diagnostic{ &hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,
Summary: "Invalid attribute", Summary: "Invalid attribute",
@ -641,17 +641,17 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
for k := range p.vars { for k := range p.vars {
if err := p.resolveValue(p.ectx, k); err != nil { if err := p.resolveValue(p.ectx, k); err != nil {
if diags, ok := err.(hcl.Diagnostics); ok { if diags, ok := err.(hcl.Diagnostics); ok {
return diags return nil, diags
} }
r := p.vars[k].Body.MissingItemRange() r := p.vars[k].Body.MissingItemRange()
return wrapErrorDiagnostic("Invalid value", err, &r, &r) return nil, wrapErrorDiagnostic("Invalid value", err, &r, &r)
} }
} }
for k := range p.funcs { for k := range p.funcs {
if err := p.resolveFunction(p.ectx, k); err != nil { if err := p.resolveFunction(p.ectx, k); err != nil {
if diags, ok := err.(hcl.Diagnostics); ok { if diags, ok := err.(hcl.Diagnostics); ok {
return diags return nil, diags
} }
var subject *hcl.Range var subject *hcl.Range
var context *hcl.Range var context *hcl.Range
@ -667,7 +667,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
} }
} }
} }
return wrapErrorDiagnostic("Invalid function", err, subject, context) return nil, wrapErrorDiagnostic("Invalid function", err, subject, context)
} }
} }
@ -681,6 +681,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
values map[string]value values map[string]value
} }
types := map[string]field{} types := map[string]field{}
renamed := map[string]map[string][]string{}
vt := reflect.ValueOf(val).Elem().Type() vt := reflect.ValueOf(val).Elem().Type()
for i := 0; i < vt.NumField(); i++ { for i := 0; i < vt.NumField(); i++ {
tags := strings.Split(vt.Field(i).Tag.Get("hcl"), ",") tags := strings.Split(vt.Field(i).Tag.Get("hcl"), ",")
@ -691,12 +692,13 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
typ: vt.Field(i).Type, typ: vt.Field(i).Type,
values: make(map[string]value), values: make(map[string]value),
} }
renamed[tags[0]] = map[string][]string{}
} }
tmpBlocks := map[string]map[string][]*hcl.Block{} tmpBlocks := map[string]map[string][]*hcl.Block{}
for _, b := range content.Blocks { for _, b := range content.Blocks {
if len(b.Labels) == 0 || len(b.Labels) > 1 { if len(b.Labels) == 0 || len(b.Labels) > 1 {
return hcl.Diagnostics{ return nil, hcl.Diagnostics{
&hcl.Diagnostic{ &hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,
Summary: "Invalid block", Summary: "Invalid block",
@ -715,10 +717,11 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
names, err := p.resolveBlockNames(b) names, err := p.resolveBlockNames(b)
if err != nil { if err != nil {
return wrapErrorDiagnostic("Invalid name", err, &b.LabelRanges[0], &b.LabelRanges[0]) return nil, wrapErrorDiagnostic("Invalid name", err, &b.LabelRanges[0], &b.LabelRanges[0])
} }
for _, name := range names { for _, name := range names {
bm[name] = append(bm[name], b) bm[name] = append(bm[name], b)
renamed[b.Type][b.Labels[0]] = append(renamed[b.Type][b.Labels[0]], name)
} }
} }
p.blocks = tmpBlocks p.blocks = tmpBlocks
@ -735,7 +738,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
continue continue
} }
} else { } else {
return wrapErrorDiagnostic("Invalid block", err, &b.LabelRanges[0], &b.DefRange) return nil, wrapErrorDiagnostic("Invalid block", err, &b.LabelRanges[0], &b.DefRange)
} }
} }
@ -773,19 +776,19 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
} }
} }
if diags.HasErrors() { if diags.HasErrors() {
return diags return nil, diags
} }
for k := range p.attrs { for k := range p.attrs {
if err := p.resolveValue(p.ectx, k); err != nil { if err := p.resolveValue(p.ectx, k); err != nil {
if diags, ok := err.(hcl.Diagnostics); ok { if diags, ok := err.(hcl.Diagnostics); ok {
return diags return nil, diags
} }
return wrapErrorDiagnostic("Invalid attribute", err, &p.attrs[k].Range, &p.attrs[k].Range) return nil, wrapErrorDiagnostic("Invalid attribute", err, &p.attrs[k].Range, &p.attrs[k].Range)
} }
} }
return nil return renamed, nil
} }
// wrapErrorDiagnostic wraps an error into a hcl.Diagnostics object. // wrapErrorDiagnostic wraps an error into a hcl.Diagnostics object.

Loading…
Cancel
Save