diff --git a/bake/hcl_test.go b/bake/hcl_test.go index 18d7a63ea5f0..745ac9b332e6 100644 --- a/bake/hcl_test.go +++ b/bake/hcl_test.go @@ -745,3 +745,50 @@ target "two" { require.Equal(t, c.Targets[1].Name, "two") require.Equal(t, map[string]string{"b": "pre-jkl"}, c.Targets[1].Args) } + +func TestCombineHCLAndJSONAttrs(t *testing.T) { + c, err := ParseFiles([]File{ + { + Name: "docker-bake.hcl", + Data: []byte(` +ABC = "foo" +variable "DEF" { + default = "" +} +group "default" { + targets = ["one"] +} +target "one" { + args = { + a = "pre-${ABC}" + } +} +target "two" { + args = { + b = "pre-${DEF}" + } +}`), + }, + { + Name: "foo.json", + Data: []byte(`{"ABC": "oof", "variable": {"DEF": {"default": "bar"}}, "target": { "one": { "args": {"a": "pre-${ABC}-${DEF}"}} } }`), + }, + { + Name: "bar.json", + Data: []byte(`{"ABC": "ghi", "DEF": "jkl"}`), + }, + }, nil) + require.NoError(t, err) + + require.Equal(t, 1, len(c.Groups)) + require.Equal(t, "default", c.Groups[0].Name) + require.Equal(t, []string{"one"}, c.Groups[0].Targets) + + require.Equal(t, 2, len(c.Targets)) + + require.Equal(t, c.Targets[0].Name, "one") + require.Equal(t, map[string]string{"a": "pre-ghi-jkl"}, c.Targets[0].Args) + + require.Equal(t, c.Targets[1].Name, "two") + require.Equal(t, map[string]string{"b": "pre-jkl"}, c.Targets[1].Args) +} diff --git a/bake/hclparser/hclparser.go b/bake/hclparser/hclparser.go index 233d6557d885..7b5dd351fe28 100644 --- a/bake/hclparser/hclparser.go +++ b/bake/hclparser/hclparser.go @@ -301,12 +301,6 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics { } attrs, diags := b.JustAttributes() - if diags.HasErrors() { - if d := removeAttributesDiags(diags, reserved, p.vars); len(d) > 0 { - return d - } - } - for _, v := range attrs { if _, ok := reserved[v.Name]; ok { continue @@ -315,6 +309,12 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics { } delete(p.attrs, "function") + if diags.HasErrors() { + if d := removeAttributesDiags(diags, reserved, p.vars, p.attrs); len(d) > 0 { + return d + } + } + for k := range p.opt.Vars { _ = p.resolveValue(k) } @@ -513,7 +513,7 @@ func setLabel(v reflect.Value, lbl string) int { return -1 } -func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}, vars map[string]*variable) hcl.Diagnostics { +func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}, vars map[string]*variable, attrs map[string]*hcl.Attribute) hcl.Diagnostics { var fdiags hcl.Diagnostics for _, d := range diags { if fout := func(d *hcl.Diagnostic) bool { @@ -535,6 +535,12 @@ func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}, return true } } + for a := range attrs { + // Do the same for global attributes + if strings.HasPrefix(d.Detail, fmt.Sprintf(`Argument "%s" was already set at `, a)) { + return true + } + } return false }(d); !fout { fdiags = append(fdiags, d)