From 6e74b0d7436152719330fce574dcbd5801021f5f Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Wed, 16 Oct 2019 22:10:17 +0000 Subject: [PATCH] bake: allow overriding no-cache and pull per target via --set Signed-off-by: Tibor Vass --- README.md | 6 ++++++ bake/bake.go | 30 ++++++++++++++++++++++++------ commands/bake.go | 8 +++++++- commands/build.go | 22 ++++++++++++++++------ util/flagutil/flagutil.go | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 util/flagutil/flagutil.go diff --git a/README.md b/README.md index 175d7d69a3b..2d1e25eaa15 100644 --- a/README.md +++ b/README.md @@ -566,8 +566,12 @@ 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 +docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo' ``` +Complete list of overridable fields: + args, cache-from, cache-to, context, dockerfile, labels, no-cache, output, platform, pull, secrets, ssh, tags, target + #### File definition In addition to compose files, bake supports a JSON and an equivalent HCL file format for defining build groups and targets. @@ -607,6 +611,8 @@ target "db" { } ``` +Complete list of valid target fields: + args, cache-from, cache-to, context, dockerfile, inherits, labels, no-cache, output, platform, pull, secrets, ssh, tags, target ### `buildx imagetools create [OPTIONS] [SOURCE] [SOURCE...]` diff --git a/bake/bake.go b/bake/bake.go index fbabd5dbe0e..c2a1554c546 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path" + "strconv" "strings" "github.com/docker/buildx/build" @@ -194,6 +195,18 @@ func (c Config) newOverrides(v []string) (map[string]Target, error) { t.Platforms = append(t.Platforms, parts[1]) case "output": t.Outputs = append(t.Outputs, parts[1]) + case "no-cache": + noCache, err := strconv.ParseBool(parts[1]) + if err != nil { + return nil, errors.Errorf("invalid value %s for boolean key no-cache", parts[1]) + } + t.NoCache = noCache + case "pull": + pull, err := strconv.ParseBool(parts[1]) + if err != nil { + return nil, errors.Errorf("invalid value %s for boolean key pull", parts[1]) + } + t.Pull = pull default: return nil, errors.Errorf("unknown key: %s", keys[1]) } @@ -270,7 +283,9 @@ type Group struct { } type Target struct { - Inherits []string `json:"inherits,omitempty" hcl:"inherits,omitempty"` + // Inherits is the only field that cannot be overridden with --set + Inherits []string `json:"inherits,omitempty" hcl:"inherits,omitempty"` + Context *string `json:"context,omitempty" hcl:"context,omitempty"` Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,omitempty"` Args map[string]string `json:"args,omitempty" hcl:"args,omitempty"` @@ -283,6 +298,9 @@ type Target struct { SSH []string `json:"ssh,omitempty" hcl:"ssh,omitempty"` Platforms []string `json:"platforms,omitempty" hcl:"platforms,omitempty"` Outputs []string `json:"output,omitempty" hcl:"output,omitempty"` + Pull bool `json:"pull,omitempty": hcl:"pull,omitempty"` + NoCache bool `json:"no-cache,omitempty": hcl:"no-cache,omitempty"` + // IMPORTANT: if you add more fields here, do not forget to update newOverrides and README. } func (t *Target) normalize() { @@ -295,10 +313,10 @@ func (t *Target) normalize() { t.Outputs = removeDupes(t.Outputs) } -func TargetsToBuildOpt(m map[string]Target, noCache, pull bool) (map[string]build.Options, error) { +func TargetsToBuildOpt(m map[string]Target) (map[string]build.Options, error) { m2 := make(map[string]build.Options, len(m)) for k, v := range m { - bo, err := toBuildOpt(v, noCache, pull) + bo, err := toBuildOpt(v) if err != nil { return nil, err } @@ -307,7 +325,7 @@ func TargetsToBuildOpt(m map[string]Target, noCache, pull bool) (map[string]buil return m2, nil } -func toBuildOpt(t Target, noCache, pull bool) (*build.Options, error) { +func toBuildOpt(t Target) (*build.Options, error) { if v := t.Context; v != nil && *v == "-" { return nil, errors.Errorf("context from stdin not allowed in bake") } @@ -336,8 +354,8 @@ func toBuildOpt(t Target, noCache, pull bool) (*build.Options, error) { Tags: t.Tags, BuildArgs: t.Args, Labels: t.Labels, - NoCache: noCache, - Pull: pull, + NoCache: t.NoCache, + Pull: t.Pull, } platforms, err := platformutil.Parse(t.Platforms) diff --git a/commands/bake.go b/commands/bake.go index fa1daf35651..68a61e8d660 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -46,6 +46,12 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) error { } else if in.exportLoad { overrides = append(overrides, "*.output=type=docker") } + if in.noCache != nil { + overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *in.noCache)) + } + if in.pull != nil { + overrides = append(overrides, fmt.Sprintf("*.pull=%t", *in.pull)) + } m, err := bake.ReadTargets(ctx, in.files, targets, overrides) if err != nil { @@ -61,7 +67,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) error { return nil } - bo, err := bake.TargetsToBuildOpt(m, in.noCache, in.pull) + bo, err := bake.TargetsToBuildOpt(m) if err != nil { return err } diff --git a/commands/build.go b/commands/build.go index 86274df0868..153a4c1d5b0 100644 --- a/commands/build.go +++ b/commands/build.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/docker/buildx/build" + "github.com/docker/buildx/util/flagutil" "github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/progress" "github.com/docker/cli/cli" @@ -62,9 +63,9 @@ type buildOptions struct { } type commonOptions struct { - noCache bool + noCache *bool progress string - pull bool + pull *bool exportPush bool exportLoad bool } @@ -79,6 +80,15 @@ func runBuild(dockerCli command.Cli, in buildOptions) error { ctx := appcontext.Context() + noCache := false + if in.noCache != nil { + noCache = *in.noCache + } + pull := false + if in.pull != nil { + pull = *in.pull + } + opts := build.Options{ Inputs: build.Inputs{ ContextPath: in.contextPath, @@ -88,8 +98,8 @@ func runBuild(dockerCli command.Cli, in buildOptions) error { Tags: in.tags, Labels: listToMap(in.labels, false), BuildArgs: listToMap(in.buildArgs, true), - Pull: in.pull, - NoCache: in.noCache, + Pull: pull, + NoCache: noCache, Target: in.target, ImageIDFile: in.imageIDFile, ExtraHosts: in.extraHosts, @@ -293,9 +303,9 @@ func buildCmd(dockerCli command.Cli) *cobra.Command { } func commonFlags(options *commonOptions, flags *pflag.FlagSet) { - flags.BoolVar(&options.noCache, "no-cache", false, "Do not use cache when building the image") + flags.Var(flagutil.Tristate(options.noCache), "no-cache", "Do not use cache when building the image") flags.StringVar(&options.progress, "progress", "auto", "Set type of progress output (auto, plain, tty). Use plain to show container output") - flags.BoolVar(&options.pull, "pull", false, "Always attempt to pull a newer version of the image") + flags.Var(flagutil.Tristate(options.pull), "pull", "Always attempt to pull a newer version of the image") } func listToMap(values []string, defaultEnv bool) map[string]string { diff --git a/util/flagutil/flagutil.go b/util/flagutil/flagutil.go new file mode 100644 index 00000000000..f1f9c9b7d4e --- /dev/null +++ b/util/flagutil/flagutil.go @@ -0,0 +1,36 @@ +package flagutil + +import "strconv" + +type tristate struct { + opt *bool +} + +// Tristate is a tri-state boolean flag type. +// It can be set, but not unset. +func Tristate(opt *bool) tristate { + return tristate{opt} +} + +func (t tristate) Type() string { + return "tristate" +} + +func (t tristate) String() string { + if t.opt == nil { + return "(unset)" + } + if *t.opt { + return "true" + } + return "false" +} + +func (t tristate) Set(s string) error { + b, err := strconv.ParseBool(s) + if err != nil { + return err + } + t.opt = &b + return nil +}