Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bake: allow overriding no-cache and pull per target via --set #165

Merged
merged 1 commit into from Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -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.
Expand Down Expand Up @@ -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...]`

Expand Down
30 changes: 24 additions & 6 deletions bake/bake.go
Expand Up @@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path"
"strconv"
"strings"

"github.com/docker/buildx/build"
Expand Down Expand Up @@ -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])
}
Expand Down Expand Up @@ -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"`
Expand All @@ -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() {
Expand All @@ -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
}
Expand All @@ -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")
}
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion commands/bake.go
Expand Up @@ -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 {
Expand All @@ -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
}
Expand Down
22 changes: 16 additions & 6 deletions commands/build.go
Expand Up @@ -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"
Expand Down Expand Up @@ -62,9 +63,9 @@ type buildOptions struct {
}

type commonOptions struct {
noCache bool
noCache *bool
progress string
pull bool
pull *bool
exportPush bool
exportLoad bool
}
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
36 changes: 36 additions & 0 deletions 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
}