diff --git a/bake/bake.go b/bake/bake.go index 66ef0a1d55b4..d14a24fcd5c2 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -413,6 +413,10 @@ func ParseFiles(files []File, defaults, vars map[string]string) (_ *Config, _ *h pm = *res } + if err := validateTargetGroups(hclFiles); err != nil { + return nil, nil, err + } + return &c, &pm, nil } @@ -442,6 +446,36 @@ func ParseFile(dt []byte, fn string) (*Config, error) { return c, err } +func validateTargetGroups(files []*hcl.File) error { + schema := &hcl.BodySchema{ + Blocks: []hcl.BlockHeaderSchema{ + {Type: "target", LabelNames: []string{"name"}}, + }, + } + for _, f := range files { + content, _, diags := f.Body.PartialContent(schema) + if diags.HasErrors() { + return diags + } + for _, block := range content.Blocks { + attrs, diags := block.Body.JustAttributes() + if diags.HasErrors() { + return diags + } + if attr, ok := attrs["targets"]; ok { + return hcl.Diagnostics{&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Unsupported argument", + Detail: "The \"targets\" argument is not valid inside a target block; use a group block instead.", + Subject: attr.NameRange.Ptr(), + Context: attr.Range.Ptr(), + }} + } + } + } + return nil +} + type Config struct { Groups []*Group `json:"group" hcl:"group,block" cty:"group"` Targets []*Target `json:"target" hcl:"target,block" cty:"target"` diff --git a/bake/hcl_test.go b/bake/hcl_test.go index c57ac78213b7..5eb650ea2789 100644 --- a/bake/hcl_test.go +++ b/bake/hcl_test.go @@ -188,6 +188,164 @@ func TestHCLWithUserDefinedFunctions(t *testing.T) { require.Equal(t, ptrstr("124"), c.Targets[0].Args["buildno"]) } +func TestTargetDisallowsGroupTargetsAttribute(t *testing.T) { + t.Run("hcl", func(t *testing.T) { + dt := []byte(` + target "app" { + dockerfile = "app.Dockerfile" + } + + target "db" { + dockerfile = "db.Dockerfile" + } + + target "foo" { + targets = ["app", "db"] + } + `) + + _, err := ParseFile(dt, "docker-bake.hcl") + require.Error(t, err) + require.ErrorContains(t, err, "Unsupported argument") + require.ErrorContains(t, err, "targets") + require.ErrorContains(t, err, "docker-bake.hcl") + }) + + t.Run("json", func(t *testing.T) { + dt := []byte(`{ + "group": { + "default": { + "targets": ["foo"] + } + }, + "target": { + "app": { + "dockerfile": "app.Dockerfile" + }, + "db": { + "dockerfile": "db.Dockerfile" + }, + "foo": { + "targets": ["app", "db"] + } + } + }`) + + _, err := ParseFile(dt, "docker-bake.json") + require.Error(t, err) + require.ErrorContains(t, err, "Unsupported argument") + require.ErrorContains(t, err, "targets") + require.ErrorContains(t, err, "docker-bake.json") + }) + + t.Run("merged hcl files", func(t *testing.T) { + _, _, err := ParseFiles([]File{ + { + Name: "base.hcl", + Data: []byte(` + target "app" { + dockerfile = "app.Dockerfile" + } + + target "db" { + dockerfile = "db.Dockerfile" + } + `), + }, + { + Name: "group-as-target.hcl", + Data: []byte(` + target "foo" { + targets = ["app", "db"] + } + `), + }, + }, nil, nil) + require.Error(t, err) + require.ErrorContains(t, err, "Unsupported argument") + require.ErrorContains(t, err, "targets") + require.ErrorContains(t, err, "group-as-target.hcl") + }) + + t.Run("merged hcl and json", func(t *testing.T) { + _, _, err := ParseFiles([]File{ + { + Name: "docker-bake.hcl", + Data: []byte(` + group "default" { + targets = ["foo"] + } + + target "app" { + dockerfile = "app.Dockerfile" + } + `), + }, + { + Name: "extra.json", + Data: []byte(`{ + "target": { + "foo": { + "targets": ["app"] + } + } + }`), + }, + }, nil, nil) + require.Error(t, err) + require.ErrorContains(t, err, "Unsupported argument") + require.ErrorContains(t, err, "targets") + require.ErrorContains(t, err, "extra.json") + }) + + t.Run("group targets still valid", func(t *testing.T) { + dt := []byte(` + target "app" { + dockerfile = "app.Dockerfile" + } + + target "db" { + dockerfile = "db.Dockerfile" + } + + group "foo" { + targets = ["app", "db"] + } + `) + + c, err := ParseFile(dt, "docker-bake.hcl") + require.NoError(t, err) + require.Equal(t, 1, len(c.Groups)) + require.Equal(t, "foo", c.Groups[0].Name) + require.Equal(t, []string{"app", "db"}, c.Groups[0].Targets) + }) + + t.Run("merged valid files", func(t *testing.T) { + c, _, err := ParseFiles([]File{ + { + Name: "targets.hcl", + Data: []byte(` + target "app" { + dockerfile = "app.Dockerfile" + } + `), + }, + { + Name: "groups.hcl", + Data: []byte(` + group "foo" { + targets = ["app"] + } + `), + }, + }, nil, nil) + require.NoError(t, err) + require.Equal(t, 1, len(c.Groups)) + require.Equal(t, "foo", c.Groups[0].Name) + require.Equal(t, []string{"app"}, c.Groups[0].Targets) + }) +} + func TestHCLWithVariables(t *testing.T) { dt := []byte(` variable "BUILD_NUMBER" {