Merge pull request #164 from tiborvass/multitarget-set

bake: allow pattern matching for target names in --set
pull/165/head
Tõnis Tiigi 5 years ago committed by GitHub
commit 0e1f0e3c73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -507,11 +507,13 @@ Options:
| Flag | Description | | Flag | Description |
| --- | --- | | --- | --- |
| -f, --file stringArray | Build definition file | -f, --file stringArray | Build definition file
| --load | Shorthand for --set=*.output=type=docker
| --no-cache | Do not use cache when building the image | --no-cache | Do not use cache when building the image
| --print | Print the options without building | --print | Print the options without building
| --progress string | Set type of progress output (auto, plain, tty). Use plain to show container output (default "auto") | --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 | --pull | Always attempt to pull a newer version of the image
| --set stringArray | Override target value (eg: target.key=value) | --push | Shorthand for --set=*.output=type=registry
| --set stringArray | Override target value (eg: targetpattern.key=value)
#### `-f, --file FILE` #### `-f, --file FILE`
@ -554,14 +556,16 @@ Same as `build --progress`. Set type of progress output (auto, plain, tty). Use
Same as `build --pull`. 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: Example:
``` ```
docker buildx bake --set target.args.mybuildarg=value docker buildx bake --set target.args.mybuildarg=value
docker buildx bake --set target.platform=linux/arm64 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 #### File definition

@ -106,6 +106,26 @@ func mergeConfig(c1, c2 Config) Config {
return c1 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) { func (c Config) newOverrides(v []string) (map[string]Target, error) {
m := map[string]Target{} m := map[string]Target{}
for _, v := range v { for _, v := range v {
@ -116,16 +136,19 @@ func (c Config) newOverrides(v []string) (map[string]Target, error) {
return nil, errors.Errorf("invalid override key %s, expected target.name", parts[0]) 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" { if len(parts) != 2 && keys[1] != "args" {
return nil, errors.Errorf("invalid override %s, expected target.name=value", v) return nil, errors.Errorf("invalid override %s, expected target.name=value", v)
} }
t := m[name] names, err := c.expandTargets(pattern)
if _, ok := c.Target[name]; !ok { if err != nil {
return nil, errors.Errorf("unknown target %s", name) return nil, err
} }
for _, name := range names {
t := m[name]
switch keys[1] { switch keys[1] {
case "context": case "context":
t.Context = &parts[1] t.Context = &parts[1]
@ -176,6 +199,7 @@ func (c Config) newOverrides(v []string) (map[string]Target, error) {
} }
m[name] = t m[name] = t
} }
}
return m, nil return m, nil
} }

@ -18,10 +18,10 @@ func TestReadTargets(t *testing.T) {
fp := filepath.Join(tmpdir, "config.hcl") fp := filepath.Join(tmpdir, "config.hcl")
err = ioutil.WriteFile(fp, []byte(` err = ioutil.WriteFile(fp, []byte(`
target "dep" { target "webDEP" {
args { args {
VAR_INHERITED = "dep" VAR_INHERITED = "webDEP"
VAR_BOTH = "dep" VAR_BOTH = "webDEP"
} }
} }
@ -30,7 +30,7 @@ target "webapp" {
args { args {
VAR_BOTH = "webapp" VAR_BOTH = "webapp"
} }
inherits = ["dep"] inherits = ["webDEP"]
}`), 0600) }`), 0600)
require.NoError(t, err) require.NoError(t, err)
@ -43,13 +43,13 @@ target "webapp" {
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile) require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
require.Equal(t, ".", *m["webapp"].Context) require.Equal(t, ".", *m["webapp"].Context)
require.Equal(t, "dep", m["webapp"].Args["VAR_INHERITED"]) require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
}) })
t.Run("InvalidTargetOverrides", func(t *testing.T) { t.Run("InvalidTargetOverrides", func(t *testing.T) {
_, err := ReadTargets(ctx, []string{fp}, []string{"webapp"}, []string{"nosuchtarget.context=foo"}) _, err := ReadTargets(ctx, []string{fp}, []string{"webapp"}, []string{"nosuchtarget.context=foo"})
require.NotNil(t, err) 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) { t.Run("ArgsOverrides", func(t *testing.T) {
@ -87,8 +87,8 @@ target "webapp" {
// building leaf but overriding parent fields // building leaf but overriding parent fields
t.Run("parent", func(t *testing.T) { t.Run("parent", func(t *testing.T) {
m, err := ReadTargets(ctx, []string{fp}, []string{"webapp"}, []string{ m, err := ReadTargets(ctx, []string{fp}, []string{"webapp"}, []string{
"dep.args.VAR_INHERITED=override", "webDEP.args.VAR_INHERITED=override",
"dep.args.VAR_BOTH=override", "webDEP.args.VAR_BOTH=override",
}) })
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override") require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override")
@ -106,6 +106,66 @@ target "webapp" {
require.Equal(t, "foo", *m["webapp"].Context) require.Equal(t, "foo", *m["webapp"].Context)
}) })
t.Run("PatternOverride", func(t *testing.T) {
// same check for two cases
multiTargetCheck := func(t *testing.T, m map[string]Target, err error) {
require.NoError(t, err)
require.Equal(t, 2, len(m))
require.Equal(t, "foo", *m["webapp"].Dockerfile)
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
require.Equal(t, "foo", *m["webDEP"].Dockerfile)
require.Equal(t, "webDEP", m["webDEP"].Args["VAR_INHERITED"])
}
cases := []struct {
name string
targets []string
overrides []string
check func(*testing.T, map[string]Target, error)
}{
{
name: "multi target single pattern",
targets: []string{"webapp", "webDEP"},
overrides: []string{"web*.dockerfile=foo"},
check: multiTargetCheck,
},
{
name: "multi target multi pattern",
targets: []string{"webapp", "webDEP"},
overrides: []string{"web*.dockerfile=foo", "*.args.VAR_BOTH=bar"},
check: multiTargetCheck,
},
{
name: "single target",
targets: []string{"webapp"},
overrides: []string{"web*.dockerfile=foo"},
check: func(t *testing.T, m map[string]Target, err error) {
require.NoError(t, err)
require.Equal(t, 1, len(m))
require.Equal(t, "foo", *m["webapp"].Dockerfile)
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
},
},
{
name: "nomatch",
targets: []string{"webapp"},
overrides: []string{"nomatch*.dockerfile=foo"},
check: func(t *testing.T, m map[string]Target, err error) {
// NOTE: I am unsure whether failing to match should always error out
// instead of simply skipping that override.
// Let's enforce the error and we can relax it later if users complain.
require.NotNil(t, err)
require.Equal(t, err.Error(), "could not find any target matching 'nomatch*'")
},
},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
m, err := ReadTargets(ctx, []string{fp}, test.targets, test.overrides)
test.check(t, m, err)
})
}
})
} }
func TestReadTargetsCompose(t *testing.T) { func TestReadTargetsCompose(t *testing.T) {

@ -37,7 +37,17 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) error {
targets = []string{"default"} targets = []string{"default"}
} }
m, err := bake.ReadTargets(ctx, in.files, targets, in.overrides) overrides := in.overrides
if in.exportPush {
if in.exportLoad {
return errors.Errorf("push and load may not be set together at the moment")
}
overrides = append(overrides, "*.output=type=registry")
} else if in.exportLoad {
overrides = append(overrides, "*.output=type=docker")
}
m, err := bake.ReadTargets(ctx, in.files, targets, overrides)
if err != nil { if err != nil {
return err return err
} }
@ -99,7 +109,9 @@ func bakeCmd(dockerCli command.Cli) *cobra.Command {
flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file") flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file")
flags.BoolVar(&options.printOnly, "print", false, "Print the options without building") 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)")
flags.BoolVar(&options.exportPush, "push", false, "Shorthand for --set=*.output=type=registry")
flags.BoolVar(&options.exportLoad, "load", false, "Shorthand for --set=*.output=type=docker")
commonFlags(&options.commonOptions, flags) commonFlags(&options.commonOptions, flags)

@ -38,9 +38,6 @@ type buildOptions struct {
extraHosts []string extraHosts []string
networkMode string networkMode string
exportPush bool
exportLoad bool
// unimplemented // unimplemented
squash bool squash bool
quiet bool quiet bool
@ -68,6 +65,8 @@ type commonOptions struct {
noCache bool noCache bool
progress string progress string
pull bool pull bool
exportPush bool
exportLoad bool
} }
func runBuild(dockerCli command.Cli, in buildOptions) error { func runBuild(dockerCli command.Cli, in buildOptions) error {

Loading…
Cancel
Save