From 2bca8fa677ba0ca1b749d7362dfac1455c34e783 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Tue, 21 Apr 2020 21:47:40 +0000 Subject: [PATCH] bake: allow pattern matching for target names in --set Although bake is for running multiple targets, --set required a single target name for overriding a property. This change allows matching multiple targets for overrides. Signed-off-by: Tibor Vass --- README.md | 8 +-- bake/bake.go | 124 +++++++++++++++++++++++++++------------------- bake/bake_test.go | 2 +- commands/bake.go | 2 +- 4 files changed, 81 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index c4d7736d..d2a7af48 100644 --- a/README.md +++ b/README.md @@ -511,7 +511,7 @@ Options: | --print | Print the options without building | --progress string | Set type of progress output (auto, plain, tty). Use plain to show container output (default "auto") | --pull | Always attempt to pull a newer version of the image -| --set stringArray | Override target value (eg: target.key=value) +| --set stringArray | Override target value (eg: targetpattern.key=value) #### `-f, --file FILE` @@ -554,14 +554,16 @@ Same as `build --progress`. Set type of progress output (auto, plain, tty). Use Same as `build --pull`. -#### `--set target.key[.subkey]=value` +#### `--set targetpattern.key[.subkey]=value` -Override target configurations from command line. +Override target configurations from command line. The pattern matching syntax is defined in https://golang.org/pkg/path/#Match. Example: ``` docker buildx bake --set target.args.mybuildarg=value docker buildx bake --set target.platform=linux/arm64 +docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo' +docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets ``` #### File definition diff --git a/bake/bake.go b/bake/bake.go index 6ec277f9..fbabd5db 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -106,6 +106,26 @@ func mergeConfig(c1, c2 Config) Config { return c1 } +func (c Config) expandTargets(pattern string) ([]string, error) { + if _, ok := c.Target[pattern]; ok { + return []string{pattern}, nil + } + var names []string + for name := range c.Target { + ok, err := path.Match(pattern, name) + if err != nil { + return nil, errors.Wrapf(err, "could not match targets with '%s'", pattern) + } + if ok { + names = append(names, name) + } + } + if len(names) == 0 { + return nil, errors.Errorf("could not find any target matching '%s'", pattern) + } + return names, nil +} + func (c Config) newOverrides(v []string) (map[string]Target, error) { m := map[string]Target{} for _, v := range v { @@ -116,65 +136,69 @@ func (c Config) newOverrides(v []string) (map[string]Target, error) { return nil, errors.Errorf("invalid override key %s, expected target.name", parts[0]) } - name := keys[0] + pattern := keys[0] if len(parts) != 2 && keys[1] != "args" { return nil, errors.Errorf("invalid override %s, expected target.name=value", v) } - t := m[name] - if _, ok := c.Target[name]; !ok { - return nil, errors.Errorf("unknown target %s", name) + names, err := c.expandTargets(pattern) + if err != nil { + return nil, err } - switch keys[1] { - case "context": - t.Context = &parts[1] - case "dockerfile": - t.Dockerfile = &parts[1] - case "args": - if len(keys) != 3 { - return nil, errors.Errorf("invalid key %s, args requires name", parts[0]) - } - if t.Args == nil { - t.Args = map[string]string{} - } - if len(parts) < 2 { - v, ok := os.LookupEnv(keys[2]) - if ok { - t.Args[keys[2]] = v + for _, name := range names { + t := m[name] + + switch keys[1] { + case "context": + t.Context = &parts[1] + case "dockerfile": + t.Dockerfile = &parts[1] + case "args": + if len(keys) != 3 { + return nil, errors.Errorf("invalid key %s, args requires name", parts[0]) } - } else { - t.Args[keys[2]] = parts[1] - } - case "labels": - if len(keys) != 3 { - return nil, errors.Errorf("invalid key %s, lanels requires name", parts[0]) - } - if t.Labels == nil { - t.Labels = map[string]string{} + if t.Args == nil { + t.Args = map[string]string{} + } + if len(parts) < 2 { + v, ok := os.LookupEnv(keys[2]) + if ok { + t.Args[keys[2]] = v + } + } else { + t.Args[keys[2]] = parts[1] + } + case "labels": + if len(keys) != 3 { + return nil, errors.Errorf("invalid key %s, lanels requires name", parts[0]) + } + if t.Labels == nil { + t.Labels = map[string]string{} + } + t.Labels[keys[2]] = parts[1] + case "tags": + t.Tags = append(t.Tags, parts[1]) + case "cache-from": + t.CacheFrom = append(t.CacheFrom, parts[1]) + case "cache-to": + t.CacheTo = append(t.CacheTo, parts[1]) + case "target": + s := parts[1] + t.Target = &s + case "secrets": + t.Secrets = append(t.Secrets, parts[1]) + case "ssh": + t.SSH = append(t.SSH, parts[1]) + case "platform": + t.Platforms = append(t.Platforms, parts[1]) + case "output": + t.Outputs = append(t.Outputs, parts[1]) + default: + return nil, errors.Errorf("unknown key: %s", keys[1]) } - t.Labels[keys[2]] = parts[1] - case "tags": - t.Tags = append(t.Tags, parts[1]) - case "cache-from": - t.CacheFrom = append(t.CacheFrom, parts[1]) - case "cache-to": - t.CacheTo = append(t.CacheTo, parts[1]) - case "target": - s := parts[1] - t.Target = &s - case "secrets": - t.Secrets = append(t.Secrets, parts[1]) - case "ssh": - t.SSH = append(t.SSH, parts[1]) - case "platform": - t.Platforms = append(t.Platforms, parts[1]) - case "output": - t.Outputs = append(t.Outputs, parts[1]) - default: - return nil, errors.Errorf("unknown key: %s", keys[1]) + m[name] = t } - m[name] = t } return m, nil } diff --git a/bake/bake_test.go b/bake/bake_test.go index cc004d0b..4ac8ca2e 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -49,7 +49,7 @@ target "webapp" { t.Run("InvalidTargetOverrides", func(t *testing.T) { _, err := ReadTargets(ctx, []string{fp}, []string{"webapp"}, []string{"nosuchtarget.context=foo"}) require.NotNil(t, err) - require.Equal(t, err.Error(), "unknown target nosuchtarget") + require.Equal(t, err.Error(), "could not find any target matching 'nosuchtarget'") }) t.Run("ArgsOverrides", func(t *testing.T) { diff --git a/commands/bake.go b/commands/bake.go index edc93d23..660bb6fd 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -99,7 +99,7 @@ func bakeCmd(dockerCli command.Cli) *cobra.Command { flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file") flags.BoolVar(&options.printOnly, "print", false, "Print the options without building") - flags.StringArrayVar(&options.overrides, "set", nil, "Override target value (eg: target.key=value)") + flags.StringArrayVar(&options.overrides, "set", nil, "Override target value (eg: targetpattern.key=value)") commonFlags(&options.commonOptions, flags)