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 <tibor@docker.com>
This commit is contained in:
Tibor Vass 2020-04-21 21:47:40 +00:00
parent 721b63f3a0
commit 2bca8fa677
4 changed files with 81 additions and 55 deletions

View File

@ -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

View File

@ -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]
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])
}
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])
m[name] = t
}
m[name] = t
}
return m, nil
}

View File

@ -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) {

View File

@ -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)