diff --git a/bake/bake.go b/bake/bake.go index ad626ee6..66cd5d19 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -181,7 +181,7 @@ func readWithProgress(r io.Reader, setStatus func(st *client.VertexStatus)) (dt } func ListTargets(files []File) ([]string, error) { - c, _, err := ParseFiles(files, nil) + c, _, err := ParseFiles(files, nil, nil) if err != nil { return nil, err } @@ -196,7 +196,7 @@ func ListTargets(files []File) ([]string, error) { } func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string, ent *EntitlementConf) (map[string]*Target, map[string]*Group, error) { - c, _, err := ParseFiles(files, defaults) + c, _, err := ParseFiles(files, overrides, defaults) if err != nil { return nil, nil, err } @@ -304,7 +304,7 @@ func sliceToMap(env []string) (res map[string]string) { return } -func ParseFiles(files []File, defaults map[string]string) (_ *Config, _ *hclparser.ParseMeta, err error) { +func ParseFiles(files []File, overrides []string, defaults map[string]string) (_ *Config, _ *hclparser.ParseMeta, err error) { defer func() { err = formatHCLError(err, files) }() @@ -312,8 +312,12 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, _ *hclpars var c Config var composeFiles []File var hclFiles []*hcl.File + envOverrides, err := parseEnvOverrides(overrides) + if err != nil { + return nil, nil, err + } for _, f := range files { - isCompose, composeErr := validateComposeFile(f.Data, f.Name) + isCompose, composeErr := validateComposeFile(f.Data, f.Name, envOverrides) if isCompose { if composeErr != nil { return nil, nil, composeErr @@ -336,7 +340,7 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, _ *hclpars } if len(composeFiles) > 0 { - cfg, cmperr := ParseComposeFiles(composeFiles) + cfg, cmperr := ParseComposeFiles(composeFiles, envOverrides) if cmperr != nil { return nil, nil, errors.Wrap(cmperr, "failed to parse compose file") } @@ -374,6 +378,28 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, _ *hclpars return &c, &pm, nil } +// Considering overrides are "targetpattern.key=value" +// Parse env overrides like example "env.key=value" +func parseEnvOverrides(overrides []string) (map[string]string, error) { + envs := make(map[string]string) + for _, o := range overrides { + parts := strings.SplitN(o, "=", 2) + if len(parts) != 2 { + return nil, errors.Errorf("invalid env override %q", o) + } + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + if strings.HasPrefix(key, "env.") { + strippedKey := strings.TrimPrefix(key, "env.") + if strippedKey == "" || value == "" { + return nil, errors.Errorf("invalid env override %q", o) + } + envs[strippedKey] = value + } + } + return envs, nil +} + func dedupeConfig(c Config) Config { c2 := c c2.Groups = make([]*Group, 0, len(c2.Groups)) @@ -396,7 +422,7 @@ func dedupeConfig(c Config) Config { } func ParseFile(dt []byte, fn string) (*Config, error) { - c, _, err := ParseFiles([]File{{Data: dt, Name: fn}}, nil) + c, _, err := ParseFiles([]File{{Data: dt, Name: fn}}, nil, nil) return c, err } diff --git a/bake/bake_test.go b/bake/bake_test.go index 482b83ad..452a5a94 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -1641,7 +1641,7 @@ services: c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.foo"}, {Data: dt2, Name: "c2.bar"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) diff --git a/bake/compose.go b/bake/compose.go index 1ca046d0..f0ee4b70 100644 --- a/bake/compose.go +++ b/bake/compose.go @@ -19,8 +19,8 @@ import ( "gopkg.in/yaml.v3" ) -func ParseComposeFiles(fs []File) (*Config, error) { - envs, err := composeEnv() +func ParseComposeFiles(fs []File, envOverrides map[string]string) (*Config, error) { + envs, err := composeEnv(envOverrides) if err != nil { return nil, err } @@ -199,8 +199,8 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf return &c, nil } -func validateComposeFile(dt []byte, fn string) (bool, error) { - envs, err := composeEnv() +func validateComposeFile(dt []byte, fn string, envOverrides map[string]string) (bool, error) { + envs, err := composeEnv(envOverrides) if err != nil { return true, err } @@ -233,7 +233,7 @@ func validateCompose(dt []byte, envs map[string]string) error { return err } -func composeEnv() (map[string]string, error) { +func composeEnv(envOverrides map[string]string) (map[string]string, error) { envs := sliceToMap(os.Environ()) if wd, err := os.Getwd(); err == nil { envs, err = loadDotEnv(envs, wd) @@ -241,6 +241,12 @@ func composeEnv() (map[string]string, error) { return nil, err } } + if envOverrides != nil { + // Apply envOverrides on envs + for key, value := range envOverrides { + envs[key] = value + } + } return envs, nil } diff --git a/bake/compose_test.go b/bake/compose_test.go index e19ff138..9ed62ec5 100644 --- a/bake/compose_test.go +++ b/bake/compose_test.go @@ -437,7 +437,33 @@ services: c, err := ParseComposeFiles([]File{{ Name: "docker-compose.yml", Data: dt, - }}) + }}, nil) + require.NoError(t, err) + require.Equal(t, map[string]*string{"FOO": ptrstr("bar")}, c.Targets[0].Args) +} + +func TestEnvOverrides(t *testing.T) { + tmpdir := t.TempDir() + + overrides := []string{"env.FOO=bar"} + + envOverrides, err := parseEnvOverrides(overrides) + require.NoError(t, err) + + dt := []byte(` +services: + scratch: + build: + context: . + args: + FOO: +`) + + chdir(t, tmpdir) + c, err := ParseComposeFiles([]File{{ + Name: "docker-compose.yml", + Data: dt, + }}, envOverrides) require.NoError(t, err) require.Equal(t, map[string]*string{"FOO": ptrstr("bar")}, c.Targets[0].Args) } @@ -664,7 +690,7 @@ target "default" { } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - isCompose, err := validateComposeFile(tt.dt, tt.fn) + isCompose, err := validateComposeFile(tt.dt, tt.fn, nil) assert.Equal(t, tt.isCompose, isCompose) if tt.wantErr { require.Error(t, err) @@ -738,7 +764,7 @@ services: c, err := ParseComposeFiles([]File{{ Name: "composetypes.yml", Data: dt, - }}) + }}, nil) require.NoError(t, err) require.Equal(t, 2, len(c.Targets)) diff --git a/bake/hcl_test.go b/bake/hcl_test.go index db1ca9c6..47cdfc83 100644 --- a/bake/hcl_test.go +++ b/bake/hcl_test.go @@ -284,7 +284,7 @@ func TestHCLMultiFileSharedVariables(t *testing.T) { c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) require.Equal(t, "app", c.Targets[0].Name) @@ -296,7 +296,7 @@ func TestHCLMultiFileSharedVariables(t *testing.T) { c, _, err = ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) @@ -333,7 +333,7 @@ func TestHCLVarsWithVars(t *testing.T) { c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) require.Equal(t, "app", c.Targets[0].Name) @@ -345,7 +345,7 @@ func TestHCLVarsWithVars(t *testing.T) { c, _, err = ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) @@ -841,7 +841,7 @@ func TestHCLMultiFileAttrs(t *testing.T) { c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) require.Equal(t, "app", c.Targets[0].Name) @@ -852,7 +852,7 @@ func TestHCLMultiFileAttrs(t *testing.T) { c, _, err = ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) @@ -876,7 +876,7 @@ func TestHCLMultiFileGlobalAttrs(t *testing.T) { c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) require.Equal(t, "app", c.Targets[0].Name) @@ -1060,7 +1060,7 @@ func TestHCLRenameMultiFile(t *testing.T) { {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, {Data: dt3, Name: "c3.hcl"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 2, len(c.Targets)) @@ -1278,7 +1278,7 @@ func TestHCLMatrixArgsOverride(t *testing.T) { c, _, err := ParseFiles([]File{ {Data: dt, Name: "docker-bake.hcl"}, - }, map[string]string{"ABC": "11,22,33"}) + }, nil, map[string]string{"ABC": "11,22,33"}) require.NoError(t, err) require.Equal(t, 3, len(c.Targets)) @@ -1465,7 +1465,7 @@ services: c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.yml"}, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Targets)) @@ -1486,7 +1486,7 @@ func TestHCLBuiltinVars(t *testing.T) { c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, - }, map[string]string{ + }, nil, map[string]string{ "BAKE_CMD_CONTEXT": "foo", }) require.NoError(t, err) @@ -1549,7 +1549,7 @@ target "b" { }] }`), }, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Groups)) @@ -1606,7 +1606,7 @@ target "two" { Name: "bar.json", Data: []byte(`{"ABC": "ghi", "DEF": "jkl"}`), }, - }, nil) + }, nil, nil) require.NoError(t, err) require.Equal(t, 1, len(c.Groups)) @@ -2267,6 +2267,7 @@ func TestJSONOverridePriority(t *testing.T) { t.Setenv("FOO_JSON", "[3,4,5]") c, _, err := ParseFiles( []File{{Name: "docker-bake.hcl", Data: dt}}, + nil, map[string]string{"FOO_JSON": "whatever"}, ) require.NoError(t, err) diff --git a/commands/bake.go b/commands/bake.go index e9636e2f..5a024092 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -198,7 +198,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba } if in.list != "" { - cfg, pm, err := bake.ParseFiles(files, defaults) + cfg, pm, err := bake.ParseFiles(files, overrides, defaults) if err != nil { return err }