From c317ca1e95d4861f0880179789e9dc1c4c6d115f Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Mon, 4 Apr 2022 17:43:45 +0200 Subject: [PATCH 1/2] bake: merge targets from multiple JSON files Signed-off-by: CrazyMax --- bake/hcl_test.go | 76 +++++++++++++++++++++++++++++++++++++ bake/hclparser/hclparser.go | 30 +++++++++++++-- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/bake/hcl_test.go b/bake/hcl_test.go index 892f6bdd..20131dee 100644 --- a/bake/hcl_test.go +++ b/bake/hcl_test.go @@ -620,3 +620,79 @@ func TestHCLBuiltinVars(t *testing.T) { require.Equal(t, "foo", *c.Targets[0].Context) require.Equal(t, "test", *c.Targets[0].Dockerfile) } + +func TestCombineHCLAndJSON(t *testing.T) { + c, err := ParseFiles([]File{ + { + Name: "docker-bake.hcl", + Data: []byte(` +group "default" { + targets = ["a"] +} + +target "metadata-a" {} +target "metadata-b" {} + +target "a" { + inherits = ["metadata-a"] + context = "." + target = "a" +} + +target "b" { + inherits = ["metadata-b"] + context = "." + target = "b" +}`), + }, + { + Name: "metadata-a.json", + Data: []byte(` +{ + "target": [{ + "metadata-a": [{ + "tags": [ + "app/a:1.0.0", + "app/a:latest" + ] + }] + }] +}`), + }, + { + Name: "metadata-b.json", + Data: []byte(` +{ + "target": [{ + "metadata-b": [{ + "tags": [ + "app/b:1.0.0", + "app/b:latest" + ] + }] + }] +}`), + }, + }, nil) + require.NoError(t, err) + + require.Equal(t, 1, len(c.Groups)) + require.Equal(t, "default", c.Groups[0].Name) + require.Equal(t, []string{"a"}, c.Groups[0].Targets) + + require.Equal(t, 4, len(c.Targets)) + + require.Equal(t, c.Targets[0].Name, "metadata-a") + require.Equal(t, []string{"app/a:1.0.0", "app/a:latest"}, c.Targets[0].Tags) + + require.Equal(t, c.Targets[1].Name, "metadata-b") + require.Equal(t, []string{"app/b:1.0.0", "app/b:latest"}, c.Targets[1].Tags) + + require.Equal(t, c.Targets[2].Name, "a") + require.Equal(t, ".", *c.Targets[2].Context) + require.Equal(t, "a", *c.Targets[2].Target) + + require.Equal(t, c.Targets[3].Name, "b") + require.Equal(t, ".", *c.Targets[3].Context) + require.Equal(t, "b", *c.Targets[3].Target) +} diff --git a/bake/hclparser/hclparser.go b/bake/hclparser/hclparser.go index bf23dce8..c4bbf1fb 100644 --- a/bake/hclparser/hclparser.go +++ b/bake/hclparser/hclparser.go @@ -302,10 +302,8 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics { attrs, diags := b.JustAttributes() if diags.HasErrors() { - for _, d := range diags { - if d.Detail != "Blocks are not allowed here." { - return diags - } + if d := removeAttributesDiags(diags, reserved); len(d) > 0 { + return d } } @@ -514,3 +512,27 @@ func setLabel(v reflect.Value, lbl string) int { } return -1 } + +func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}) hcl.Diagnostics { + var fdiags hcl.Diagnostics + for _, d := range diags { + if fout := func(d *hcl.Diagnostic) bool { + // https://github.com/docker/buildx/pull/541 + if d.Detail == "Blocks are not allowed here." { + return true + } + for r := range reserved { + // JSON body objects don't handle repeated blocks like HCL but + // reserved name attributes should be allowed when multi bodies are merged. + // https://github.com/hashicorp/hcl/blob/main/json/spec.md#blocks + if strings.HasPrefix(d.Detail, fmt.Sprintf(`Argument "%s" was already set at `, r)) { + return true + } + } + return false + }(d); !fout { + fdiags = append(fdiags, d) + } + } + return fdiags +} From cad7ed68be7af1f69da40cafb229f8ef6ce3fee9 Mon Sep 17 00:00:00 2001 From: CrazyMax Date: Wed, 6 Apr 2022 00:55:56 +0200 Subject: [PATCH 2/2] bake: merge vars from multiple JSON files Signed-off-by: CrazyMax --- bake/hcl_test.go | 51 ++++++++++++++++++++++++++++++++++++- bake/hclparser/hclparser.go | 10 ++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/bake/hcl_test.go b/bake/hcl_test.go index 20131dee..18d7a63e 100644 --- a/bake/hcl_test.go +++ b/bake/hcl_test.go @@ -621,7 +621,7 @@ func TestHCLBuiltinVars(t *testing.T) { require.Equal(t, "test", *c.Targets[0].Dockerfile) } -func TestCombineHCLAndJSON(t *testing.T) { +func TestCombineHCLAndJSONTargets(t *testing.T) { c, err := ParseFiles([]File{ { Name: "docker-bake.hcl", @@ -696,3 +696,52 @@ target "b" { require.Equal(t, ".", *c.Targets[3].Context) require.Equal(t, "b", *c.Targets[3].Target) } + +func TestCombineHCLAndJSONVars(t *testing.T) { + c, err := ParseFiles([]File{ + { + Name: "docker-bake.hcl", + Data: []byte(` +variable "ABC" { + default = "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(`{"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 c4bbf1fb..233d6557 100644 --- a/bake/hclparser/hclparser.go +++ b/bake/hclparser/hclparser.go @@ -302,7 +302,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics { attrs, diags := b.JustAttributes() if diags.HasErrors() { - if d := removeAttributesDiags(diags, reserved); len(d) > 0 { + if d := removeAttributesDiags(diags, reserved, p.vars); len(d) > 0 { return d } } @@ -513,7 +513,7 @@ func setLabel(v reflect.Value, lbl string) int { return -1 } -func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}) hcl.Diagnostics { +func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}, vars map[string]*variable) hcl.Diagnostics { var fdiags hcl.Diagnostics for _, d := range diags { if fout := func(d *hcl.Diagnostic) bool { @@ -529,6 +529,12 @@ func removeAttributesDiags(diags hcl.Diagnostics, reserved map[string]struct{}) return true } } + for v := range vars { + // Do the same for global variables + if strings.HasPrefix(d.Detail, fmt.Sprintf(`Argument "%s" was already set at `, v)) { + return true + } + } return false }(d); !fout { fdiags = append(fdiags, d)