From 8da28574b0cd6a428d52786da865a8d8ea608fdc Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Tue, 25 Jun 2024 23:19:45 -0700 Subject: [PATCH 1/7] bake: add call methods support and printing Signed-off-by: Tonis Tiigi --- bake/bake.go | 14 ++++- commands/bake.go | 155 ++++++++++++++++++++++++++++++++++++++++++---- commands/build.go | 52 ++++++++-------- 3 files changed, 182 insertions(+), 39 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index 54c19a15..db008520 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -702,7 +702,8 @@ type Target struct { NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"` ShmSize *string `json:"shm-size,omitempty" hcl:"shm-size,optional"` Ulimits []string `json:"ulimits,omitempty" hcl:"ulimits,optional"` - // IMPORTANT: if you add more fields here, do not forget to update newOverrides and docs/bake-reference.md. + Call *string `json:"call,omitempty" hcl:"call,optional" cty:"call"` + // IMPORTANT: if you add more fields here, do not forget to update newOverrides/AddOverrides and docs/bake-reference.md. // linked is a private field to mark a target used as a linked one linked bool @@ -776,6 +777,9 @@ func (t *Target) Merge(t2 *Target) { if t2.Target != nil { t.Target = t2.Target } + if t2.Call != nil { + t.Call = t2.Call + } if t2.Annotations != nil { // merge t.Annotations = append(t.Annotations, t2.Annotations...) } @@ -863,6 +867,8 @@ func (t *Target) AddOverrides(overrides map[string]Override) error { t.CacheTo = o.ArrValue case "target": t.Target = &value + case "call": + t.Call = &value case "secrets": t.Secrets = o.ArrValue case "ssh": @@ -1298,6 +1304,12 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { bo.Target = *t.Target } + if t.Call != nil { + bo.PrintFunc = &build.PrintFunc{ + Name: *t.Call, + } + } + cacheImports, err := buildflags.ParseCacheEntry(t.CacheFrom) if err != nil { return nil, err diff --git a/commands/bake.go b/commands/bake.go index 2e0164f6..ace78d96 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -1,11 +1,13 @@ package commands import ( + "bytes" "context" "encoding/json" "fmt" "io" "os" + "slices" "strings" "github.com/containerd/console" @@ -13,6 +15,7 @@ import ( "github.com/docker/buildx/bake" "github.com/docker/buildx/build" "github.com/docker/buildx/builder" + "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/localstate" "github.com/docker/buildx/util/buildflags" "github.com/docker/buildx/util/cobrautil/completion" @@ -40,6 +43,7 @@ type bakeOptions struct { metadataFile string exportPush bool exportLoad bool + callFunc string } func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) { @@ -71,6 +75,11 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba targets = []string{"default"} } + callFunc, err := buildflags.ParsePrintFunc(in.callFunc) + if err != nil { + return err + } + overrides := in.overrides if in.exportPush { overrides = append(overrides, "*.push=true") @@ -78,6 +87,9 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba if in.exportLoad { overrides = append(overrides, "*.load=true") } + if callFunc != nil { + overrides = append(overrides, fmt.Sprintf("*.call=%s", callFunc.Name)) + } if cFlags.noCache != nil { overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache)) } @@ -146,19 +158,19 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba if err != nil { return } - if progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode { - desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term) + } + if progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode { + desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term) + } + if resp != nil && len(in.metadataFile) > 0 { + dt := make(map[string]interface{}) + for t, r := range resp { + dt[t] = decodeExporterResponse(r.ExporterResponse) } - if resp != nil && len(in.metadataFile) > 0 { - dt := make(map[string]interface{}) - for t, r := range resp { - dt[t] = decodeExporterResponse(r.ExporterResponse) - } - if warnings := printer.Warnings(); len(warnings) > 0 && confutil.MetadataWarningsEnabled() { - dt["buildx.build.warnings"] = warnings - } - err = writeMetadataFile(in.metadataFile, dt) + if warnings := printer.Warnings(); len(warnings) > 0 && confutil.MetadataWarningsEnabled() { + dt["buildx.build.warnings"] = warnings } + err = writeMetadataFile(in.metadataFile, dt) } }() @@ -222,6 +234,16 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba return nil } + for _, opt := range bo { + if opt.PrintFunc != nil { + cf, err := buildflags.ParsePrintFunc(opt.PrintFunc.Name) + if err != nil { + return err + } + opt.PrintFunc.Name = cf.Name + } + } + prm := confutil.MetadataProvenance() if len(in.metadataFile) == 0 { prm = confutil.MetadataProvenanceModeDisabled @@ -254,7 +276,113 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba return wrapBuildError(err, true) } - return + err = printer.Wait() + printer = nil + if err != nil { + return err + } + + var callFormatJSON bool + var jsonResults = map[string]map[string]any{} + if callFunc != nil { + callFormatJSON = callFunc.Format == "json" + } + var sep bool + var exitCode int + + names := make([]string, 0, len(bo)) + for name := range bo { + names = append(names, name) + } + slices.Sort(names) + + for _, name := range names { + req := bo[name] + if req.PrintFunc == nil { + continue + } + + pf := &pb.PrintFunc{ + Name: req.PrintFunc.Name, + Format: req.PrintFunc.Format, + IgnoreStatus: req.PrintFunc.IgnoreStatus, + } + + if callFunc != nil { + pf.Format = callFunc.Format + pf.IgnoreStatus = callFunc.IgnoreStatus + } + + var res map[string]string + if sp, ok := resp[name]; ok { + res = sp.ExporterResponse + } + + if callFormatJSON { + jsonResults[name] = map[string]any{} + buf := &bytes.Buffer{} + if code, err := printResult(buf, pf, res); err != nil { + jsonResults[name]["error"] = err.Error() + exitCode = 1 + } else if code != 0 && exitCode == 0 { + exitCode = code + } + m := map[string]*json.RawMessage{} + if err := json.Unmarshal(buf.Bytes(), &m); err == nil { + for k, v := range m { + jsonResults[name][k] = v + } + } else { + jsonResults[name][pf.Name] = json.RawMessage(buf.Bytes()) + } + } else { + if sep { + fmt.Fprintf(dockerCli.Out(), "\n\n") + } else { + sep = true + } + fmt.Fprintf(dockerCli.Out(), "%s\n", name) + if code, err := printResult(dockerCli.Out(), pf, res); err != nil { + fmt.Fprintf(dockerCli.Out(), "error: %v\n", err) + exitCode = 1 + } else if code != 0 && exitCode == 0 { + exitCode = code + } + } + } + if callFormatJSON { + out := struct { + Group map[string]*bake.Group `json:"group,omitempty"` + Target map[string]map[string]any `json:"target"` + }{ + Group: grps, + Target: map[string]map[string]any{}, + } + + for name, def := range tgts { + out.Target[name] = map[string]any{ + "build": def, + } + if res, ok := jsonResults[name]; ok { + printName := bo[name].PrintFunc.Name + if printName == "lint" { + printName = "check" + } + out.Target[name][printName] = res + } + } + dt, err := json.MarshalIndent(out, "", " ") + if err != nil { + return err + } + fmt.Fprintln(dockerCli.Out(), string(dt)) + } + + if exitCode != 0 { + os.Exit(exitCode) + } + + return nil } func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { @@ -290,6 +418,9 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`) flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`) flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`) + flags.StringVar(&options.callFunc, "call", "build", `Set method for evaluating build ("check", "outline", "targets")`) + flags.VarPF(callAlias(&options.callFunc, "check"), "check", "", `Shorthand for "--call=check"`) + flags.Lookup("check").NoOptDefVal = "true" commonBuildFlags(&cFlags, flags) diff --git a/commands/build.go b/commands/build.go index 1bf77dc8..9e5d3f00 100644 --- a/commands/build.go +++ b/commands/build.go @@ -9,7 +9,6 @@ import ( "encoding/json" "fmt" "io" - "log" "os" "path/filepath" "strconv" @@ -369,8 +368,10 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) } } if opts.PrintFunc != nil { - if err := printResult(opts.PrintFunc, resp.ExporterResponse); err != nil { + if exitcode, err := printResult(dockerCli.Out(), opts.PrintFunc, resp.ExporterResponse); err != nil { return err + } else if exitcode != 0 { + os.Exit(exitcode) } } else if options.metadataFile != "" { dt := decodeExporterResponse(resp.ExporterResponse) @@ -634,7 +635,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D } flags.StringVar(&options.printFunc, "call", "build", `Set method for evaluating build ("check", "outline", "targets")`) - flags.VarPF(callAlias(options, "check"), "check", "", `Shorthand for "--call=check"`) + flags.VarPF(callAlias(&options.printFunc, "check"), "check", "", `Shorthand for "--call=check"`) flags.Lookup("check").NoOptDefVal = "true" // hidden flags @@ -862,24 +863,24 @@ func printWarnings(w io.Writer, warnings []client.VertexWarning, mode progressui } } -func printResult(f *controllerapi.PrintFunc, res map[string]string) error { +func printResult(w io.Writer, f *controllerapi.PrintFunc, res map[string]string) (int, error) { switch f.Name { case "outline": - return printValue(outline.PrintOutline, outline.SubrequestsOutlineDefinition.Version, f.Format, res) + return 0, printValue(w, outline.PrintOutline, outline.SubrequestsOutlineDefinition.Version, f.Format, res) case "targets": - return printValue(targets.PrintTargets, targets.SubrequestsTargetsDefinition.Version, f.Format, res) + return 0, printValue(w, targets.PrintTargets, targets.SubrequestsTargetsDefinition.Version, f.Format, res) case "subrequests.describe": - return printValue(subrequests.PrintDescribe, subrequests.SubrequestsDescribeDefinition.Version, f.Format, res) + return 0, printValue(w, subrequests.PrintDescribe, subrequests.SubrequestsDescribeDefinition.Version, f.Format, res) case "lint": - err := printValue(lint.PrintLintViolations, lint.SubrequestLintDefinition.Version, f.Format, res) + err := printValue(w, lint.PrintLintViolations, lint.SubrequestLintDefinition.Version, f.Format, res) if err != nil { - return err + return 0, err } lintResults := lint.LintResults{} if result, ok := res["result.json"]; ok { if err := json.Unmarshal([]byte(result), &lintResults); err != nil { - return err + return 0, err } } if lintResults.Error != nil { @@ -889,52 +890,51 @@ func printResult(f *controllerapi.PrintFunc, res map[string]string) error { // but here we want to print the error in a way that's consistent with how // the lint warnings are printed via the `lint.PrintLintViolations` function, // which differs from the default error printing. - fmt.Println() - lintBuf := bytes.NewBuffer([]byte(lintResults.Error.Message)) if f.Format != "json" { - fmt.Fprintln(lintBuf) + fmt.Fprintln(w) } + lintBuf := bytes.NewBuffer([]byte(lintResults.Error.Message + "\n")) sourceInfo := lintResults.Sources[lintResults.Error.Location.SourceIndex] source := errdefs.Source{ Info: sourceInfo, Ranges: lintResults.Error.Location.Ranges, } source.Print(lintBuf) - return errors.New(lintBuf.String()) + return 0, errors.New(lintBuf.String()) } else if len(lintResults.Warnings) == 0 && f.Format != "json" { - fmt.Println("Check complete, no warnings found.") + fmt.Fprintln(w, "Check complete, no warnings found.") } default: if dt, ok := res["result.json"]; ok && f.Format == "json" { - fmt.Println(dt) + fmt.Fprintln(w, dt) } else if dt, ok := res["result.txt"]; ok { - fmt.Print(dt) + fmt.Fprint(w, dt) } else { - log.Printf("%s %+v", f, res) + fmt.Fprintf(w, "%s %+v\n", f, res) } } if v, ok := res["result.statuscode"]; !f.IgnoreStatus && ok { if n, err := strconv.Atoi(v); err == nil && n != 0 { - os.Exit(n) + return n, nil } } - return nil + return 0, nil } type printFunc func([]byte, io.Writer) error -func printValue(printer printFunc, version string, format string, res map[string]string) error { +func printValue(w io.Writer, printer printFunc, version string, format string, res map[string]string) error { if format == "json" { - fmt.Fprintln(os.Stdout, res["result.json"]) + fmt.Fprintln(w, res["result.json"]) return nil } if res["version"] != "" && versions.LessThan(version, res["version"]) && res["result.txt"] != "" { // structure is too new and we don't know how to print it - fmt.Fprint(os.Stdout, res["result.txt"]) + fmt.Fprint(w, res["result.txt"]) return nil } - return printer([]byte(res["result.json"]), os.Stdout) + return printer([]byte(res["result.json"]), w) } type invokeConfig struct { @@ -1042,7 +1042,7 @@ func maybeJSONArray(v string) []string { return []string{v} } -func callAlias(options *buildOptions, value string) cobrautil.BoolFuncValue { +func callAlias(target *string, value string) cobrautil.BoolFuncValue { return func(s string) error { v, err := strconv.ParseBool(s) if err != nil { @@ -1050,7 +1050,7 @@ func callAlias(options *buildOptions, value string) cobrautil.BoolFuncValue { } if v { - options.printFunc = value + *target = value } return nil } From 8f4c8b094a3aea1d73bd2db74bfc5986beda2e6b Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Tue, 25 Jun 2024 23:30:08 -0700 Subject: [PATCH 2/7] bake: allow text descriptions for targets Signed-off-by: Tonis Tiigi --- bake/bake.go | 6 +++++- commands/bake.go | 7 ++++++- commands/build.go | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index db008520..b783689b 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -675,7 +675,8 @@ type Group struct { } type Target struct { - Name string `json:"-" hcl:"name,label" cty:"name"` + Name string `json:"-" hcl:"name,label" cty:"name"` + Description string `json:"description,omitempty" hcl:"description,optional" cty:"description"` // Inherits is the only field that cannot be overridden with --set Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional" cty:"inherits"` @@ -823,6 +824,9 @@ func (t *Target) Merge(t2 *Target) { if t2.Ulimits != nil { // merge t.Ulimits = append(t.Ulimits, t2.Ulimits...) } + if t2.Description != "" { + t.Description = t2.Description + } t.Inherits = append(t.Inherits, t2.Inherits...) } diff --git a/commands/bake.go b/commands/bake.go index ace78d96..19fba2ff 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -337,11 +337,16 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba } } else { if sep { - fmt.Fprintf(dockerCli.Out(), "\n\n") + fmt.Fprintln(dockerCli.Out()) } else { sep = true } fmt.Fprintf(dockerCli.Out(), "%s\n", name) + if descr := tgts[name].Description; descr != "" { + fmt.Fprintf(dockerCli.Out(), "%s\n", descr) + } + + fmt.Fprintln(dockerCli.Out()) if code, err := printResult(dockerCli.Out(), pf, res); err != nil { fmt.Fprintf(dockerCli.Out(), "error: %v\n", err) exitCode = 1 diff --git a/commands/build.go b/commands/build.go index 9e5d3f00..12d45a08 100644 --- a/commands/build.go +++ b/commands/build.go @@ -890,7 +890,7 @@ func printResult(w io.Writer, f *controllerapi.PrintFunc, res map[string]string) // but here we want to print the error in a way that's consistent with how // the lint warnings are printed via the `lint.PrintLintViolations` function, // which differs from the default error printing. - if f.Format != "json" { + if f.Format != "json" && len(lintResults.Warnings) > 0 { fmt.Fprintln(w) } lintBuf := bytes.NewBuffer([]byte(lintResults.Error.Message + "\n")) From 7460f049f2d1525fa0443eeed92feae68a97874d Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Wed, 26 Jun 2024 23:03:23 -0700 Subject: [PATCH 3/7] bake: add list-targets options to list available targets/groups Signed-off-by: Tonis Tiigi --- bake/bake.go | 5 +-- commands/bake.go | 85 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index b783689b..f6b9e0fe 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -669,8 +669,9 @@ func (c Config) target(name string, visited map[string]*Target, overrides map[st } type Group struct { - Name string `json:"-" hcl:"name,label" cty:"name"` - Targets []string `json:"targets" hcl:"targets" cty:"targets"` + Name string `json:"-" hcl:"name,label" cty:"name"` + Description string `json:"description,omitempty" hcl:"description,optional" cty:"description"` + Targets []string `json:"targets" hcl:"targets" cty:"targets"` // Target // TODO? } diff --git a/commands/bake.go b/commands/bake.go index 19fba2ff..14d35a57 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -2,6 +2,7 @@ package commands import ( "bytes" + "cmp" "context" "encoding/json" "fmt" @@ -9,6 +10,7 @@ import ( "os" "slices" "strings" + "text/tabwriter" "github.com/containerd/console" "github.com/containerd/platforms" @@ -33,11 +35,12 @@ import ( ) type bakeOptions struct { - files []string - overrides []string - printOnly bool - sbom string - provenance string + files []string + overrides []string + printOnly bool + listTargets bool + sbom string + provenance string builder string metadataFile string @@ -183,12 +186,28 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba return errors.New("couldn't find a bake definition") } - tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, map[string]string{ + defaults := map[string]string{ // don't forget to update documentation if you add a new // built-in variable: docs/bake-reference.md#built-in-variables "BAKE_CMD_CONTEXT": cmdContext, "BAKE_LOCAL_PLATFORM": platforms.Format(platforms.DefaultSpec()), - }) + } + + if in.listTargets { + cfg, err := bake.ParseFiles(files, defaults) + if err != nil { + return err + } + + err = printer.Wait() + printer = nil + if err != nil { + return err + } + return printTargetList(dockerCli.Out(), cfg) + } + + tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, defaults) if err != nil { return err } @@ -419,6 +438,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file") flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`) flags.BoolVar(&options.printOnly, "print", false, "Print the options without building") + flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets") flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`) flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`) flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`) @@ -482,3 +502,54 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names return } + +func printTargetList(w io.Writer, cfg *bake.Config) error { + tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) + defer tw.Flush() + + tw.Write([]byte("TARGET\tDESCRIPTION\n")) + + type targetOrGroup struct { + name string + target *bake.Target + group *bake.Group + } + + list := make([]targetOrGroup, 0, len(cfg.Targets)+len(cfg.Groups)) + for _, tgt := range cfg.Targets { + list = append(list, targetOrGroup{name: tgt.Name, target: tgt}) + } + for _, grp := range cfg.Groups { + list = append(list, targetOrGroup{name: grp.Name, group: grp}) + } + + slices.SortFunc(list, func(a, b targetOrGroup) int { + return cmp.Compare(a.name, b.name) + }) + + for _, tgt := range list { + if strings.HasPrefix(tgt.name, "_") { + // convention for a private target + continue + } + var descr string + if tgt.target != nil { + descr = tgt.target.Description + } else if tgt.group != nil { + descr = tgt.group.Description + + if len(tgt.group.Targets) > 0 { + slices.Sort(tgt.group.Targets) + names := strings.Join(tgt.group.Targets, ", ") + if descr != "" { + descr += " (" + names + ")" + } else { + descr = names + } + } + } + fmt.Fprintf(tw, "%s\t%s\n", tgt.name, descr) + } + + return nil +} From 233b869c63844ddabc4100b2fc2e2f7e4bdd6aa4 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 27 Jun 2024 22:18:40 -0700 Subject: [PATCH 4/7] bake: add list-variables option Signed-off-by: Tonis Tiigi --- bake/bake.go | 29 ++++++++++--------- bake/bake_test.go | 2 +- bake/hcl_test.go | 26 ++++++++--------- bake/hclparser/hclparser.go | 42 ++++++++++++++++++++++++---- bake/hclparser/merged.go | 26 ++++++++--------- commands/bake.go | 56 +++++++++++++++++++++++++++---------- 6 files changed, 121 insertions(+), 60 deletions(-) diff --git a/bake/bake.go b/bake/bake.go index f6b9e0fe..c766e7e3 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -177,7 +177,7 @@ func readWithProgress(r io.Reader, setStatus func(st *client.VertexStatus)) (dt } func ListTargets(files []File) ([]string, error) { - c, err := ParseFiles(files, nil) + c, _, err := ParseFiles(files, nil) if err != nil { return nil, err } @@ -192,7 +192,7 @@ func ListTargets(files []File) ([]string, error) { } func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string) (map[string]*Target, map[string]*Group, error) { - c, err := ParseFiles(files, defaults) + c, _, err := ParseFiles(files, defaults) if err != nil { return nil, nil, err } @@ -298,7 +298,7 @@ func sliceToMap(env []string) (res map[string]string) { return } -func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) { +func ParseFiles(files []File, defaults map[string]string) (_ *Config, _ *hclparser.ParseMeta, err error) { defer func() { err = formatHCLError(err, files) }() @@ -310,7 +310,7 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) isCompose, composeErr := validateComposeFile(f.Data, f.Name) if isCompose { if composeErr != nil { - return nil, composeErr + return nil, nil, composeErr } composeFiles = append(composeFiles, f) } @@ -318,13 +318,13 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) hf, isHCL, err := ParseHCLFile(f.Data, f.Name) if isHCL { if err != nil { - return nil, err + return nil, nil, err } hclFiles = append(hclFiles, hf) } else if composeErr != nil { - return nil, errors.Wrapf(err, "failed to parse %s: parsing yaml: %v, parsing hcl", f.Name, composeErr) + return nil, nil, errors.Wrapf(err, "failed to parse %s: parsing yaml: %v, parsing hcl", f.Name, composeErr) } else { - return nil, err + return nil, nil, err } } } @@ -332,23 +332,24 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) if len(composeFiles) > 0 { cfg, cmperr := ParseComposeFiles(composeFiles) if cmperr != nil { - return nil, errors.Wrap(cmperr, "failed to parse compose file") + return nil, nil, errors.Wrap(cmperr, "failed to parse compose file") } c = mergeConfig(c, *cfg) c = dedupeConfig(c) } + var pm hclparser.ParseMeta if len(hclFiles) > 0 { - renamed, err := hclparser.Parse(hclparser.MergeFiles(hclFiles), hclparser.Opt{ + res, err := hclparser.Parse(hclparser.MergeFiles(hclFiles), hclparser.Opt{ LookupVar: os.LookupEnv, Vars: defaults, ValidateLabel: validateTargetName, }, &c) if err.HasErrors() { - return nil, err + return nil, nil, err } - for _, renamed := range renamed { + for _, renamed := range res.Renamed { for oldName, newNames := range renamed { newNames = dedupSlice(newNames) if len(newNames) == 1 && oldName == newNames[0] { @@ -361,9 +362,10 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) } } c = dedupeConfig(c) + pm = *res } - return &c, nil + return &c, &pm, nil } func dedupeConfig(c Config) Config { @@ -388,7 +390,8 @@ func dedupeConfig(c Config) Config { } func ParseFile(dt []byte, fn string) (*Config, error) { - return ParseFiles([]File{{Data: dt, Name: fn}}, nil) + c, _, err := ParseFiles([]File{{Data: dt, Name: fn}}, nil) + return c, err } type Config struct { diff --git a/bake/bake_test.go b/bake/bake_test.go index fe21d2b5..cc2bed87 100644 --- a/bake/bake_test.go +++ b/bake/bake_test.go @@ -1528,7 +1528,7 @@ services: v2: "bar" `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.foo"}, {Data: dt2, Name: "c2.bar"}, }, nil) diff --git a/bake/hcl_test.go b/bake/hcl_test.go index f33722c7..ac5e61eb 100644 --- a/bake/hcl_test.go +++ b/bake/hcl_test.go @@ -273,7 +273,7 @@ func TestHCLMultiFileSharedVariables(t *testing.T) { } `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, }, nil) @@ -285,7 +285,7 @@ func TestHCLMultiFileSharedVariables(t *testing.T) { t.Setenv("FOO", "def") - c, err = ParseFiles([]File{ + c, _, err = ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, }, nil) @@ -322,7 +322,7 @@ func TestHCLVarsWithVars(t *testing.T) { } `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, }, nil) @@ -334,7 +334,7 @@ func TestHCLVarsWithVars(t *testing.T) { t.Setenv("BASE", "new") - c, err = ParseFiles([]File{ + c, _, err = ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, }, nil) @@ -612,7 +612,7 @@ func TestHCLMultiFileAttrs(t *testing.T) { FOO="def" `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, }, nil) @@ -623,7 +623,7 @@ func TestHCLMultiFileAttrs(t *testing.T) { t.Setenv("FOO", "ghi") - c, err = ParseFiles([]File{ + c, _, err = ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, }, nil) @@ -647,7 +647,7 @@ func TestHCLMultiFileGlobalAttrs(t *testing.T) { FOO = "def" `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, }, nil) @@ -830,7 +830,7 @@ func TestHCLRenameMultiFile(t *testing.T) { } `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.hcl"}, {Data: dt3, Name: "c3.hcl"}, @@ -1050,7 +1050,7 @@ func TestHCLMatrixArgsOverride(t *testing.T) { } `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "docker-bake.hcl"}, }, map[string]string{"ABC": "11,22,33"}) require.NoError(t, err) @@ -1236,7 +1236,7 @@ services: v2: "bar" `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, {Data: dt2, Name: "c2.yml"}, }, nil) @@ -1258,7 +1258,7 @@ func TestHCLBuiltinVars(t *testing.T) { } `) - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ {Data: dt, Name: "c1.hcl"}, }, map[string]string{ "BAKE_CMD_CONTEXT": "foo", @@ -1272,7 +1272,7 @@ func TestHCLBuiltinVars(t *testing.T) { } func TestCombineHCLAndJSONTargets(t *testing.T) { - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ { Name: "docker-bake.hcl", Data: []byte(` @@ -1348,7 +1348,7 @@ target "b" { } func TestCombineHCLAndJSONVars(t *testing.T) { - c, err := ParseFiles([]File{ + c, _, err := ParseFiles([]File{ { Name: "docker-bake.hcl", Data: []byte(` diff --git a/bake/hclparser/hclparser.go b/bake/hclparser/hclparser.go index 7efdc9f6..eee3a670 100644 --- a/bake/hclparser/hclparser.go +++ b/bake/hclparser/hclparser.go @@ -25,9 +25,11 @@ type Opt struct { } type variable struct { - Name string `json:"-" hcl:"name,label"` - Default *hcl.Attribute `json:"default,omitempty" hcl:"default,optional"` - Body hcl.Body `json:"-" hcl:",body"` + Name string `json:"-" hcl:"name,label"` + Default *hcl.Attribute `json:"default,omitempty" hcl:"default,optional"` + Description string `json:"description,omitempty" hcl:"description,optional"` + Body hcl.Body `json:"-" hcl:",body"` + Remain hcl.Body `json:"-" hcl:",remain"` } type functionDef struct { @@ -534,7 +536,18 @@ func (p *parser) resolveBlockNames(block *hcl.Block) ([]string, error) { return names, nil } -func Parse(b hcl.Body, opt Opt, val interface{}) (map[string]map[string][]string, hcl.Diagnostics) { +type Variable struct { + Name string + Description string + Value *string +} + +type ParseMeta struct { + Renamed map[string]map[string][]string + AllVariables []*Variable +} + +func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) { reserved := map[string]struct{}{} schema, _ := gohcl.ImpliedBodySchema(val) @@ -643,6 +656,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (map[string]map[string][]string } } + vars := make([]*Variable, 0, len(p.vars)) for k := range p.vars { if err := p.resolveValue(p.ectx, k); err != nil { if diags, ok := err.(hcl.Diagnostics); ok { @@ -651,6 +665,21 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (map[string]map[string][]string r := p.vars[k].Body.MissingItemRange() return nil, wrapErrorDiagnostic("Invalid value", err, &r, &r) } + v := &Variable{ + Name: p.vars[k].Name, + Description: p.vars[k].Description, + } + if vv := p.ectx.Variables[k]; !vv.IsNull() { + var s string + switch vv.Type() { + case cty.String: + s = vv.AsString() + case cty.Bool: + s = strconv.FormatBool(vv.True()) + } + v.Value = &s + } + vars = append(vars, v) } for k := range p.funcs { @@ -795,7 +824,10 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (map[string]map[string][]string } } - return renamed, nil + return &ParseMeta{ + Renamed: renamed, + AllVariables: vars, + }, nil } // wrapErrorDiagnostic wraps an error into a hcl.Diagnostics object. diff --git a/bake/hclparser/merged.go b/bake/hclparser/merged.go index 6faf6ace..7fdf1234 100644 --- a/bake/hclparser/merged.go +++ b/bake/hclparser/merged.go @@ -111,21 +111,19 @@ func (mb mergedBodies) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { diags = append(diags, thisDiags...) } - if thisAttrs != nil { - for name, attr := range thisAttrs { - if existing := attrs[name]; existing != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate argument", - Detail: fmt.Sprintf( - "Argument %q was already set at %s", - name, existing.NameRange.String(), - ), - Subject: thisAttrs[name].NameRange.Ptr(), - }) - } - attrs[name] = attr + for name, attr := range thisAttrs { + if existing := attrs[name]; existing != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Duplicate argument", + Detail: fmt.Sprintf( + "Argument %q was already set at %s", + name, existing.NameRange.String(), + ), + Subject: thisAttrs[name].NameRange.Ptr(), + }) } + attrs[name] = attr } } diff --git a/commands/bake.go b/commands/bake.go index 14d35a57..12c89d53 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -15,6 +15,7 @@ import ( "github.com/containerd/console" "github.com/containerd/platforms" "github.com/docker/buildx/bake" + "github.com/docker/buildx/bake/hclparser" "github.com/docker/buildx/build" "github.com/docker/buildx/builder" "github.com/docker/buildx/controller/pb" @@ -39,6 +40,7 @@ type bakeOptions struct { overrides []string printOnly bool listTargets bool + listVars bool sbom string provenance string @@ -161,19 +163,19 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba if err != nil { return } - } - if progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode { - desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term) - } - if resp != nil && len(in.metadataFile) > 0 { - dt := make(map[string]interface{}) - for t, r := range resp { - dt[t] = decodeExporterResponse(r.ExporterResponse) + if progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode { + desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term) } - if warnings := printer.Warnings(); len(warnings) > 0 && confutil.MetadataWarningsEnabled() { - dt["buildx.build.warnings"] = warnings + if resp != nil && len(in.metadataFile) > 0 { + dt := make(map[string]interface{}) + for t, r := range resp { + dt[t] = decodeExporterResponse(r.ExporterResponse) + } + if warnings := printer.Warnings(); len(warnings) > 0 && confutil.MetadataWarningsEnabled() { + dt["buildx.build.warnings"] = warnings + } + err = writeMetadataFile(in.metadataFile, dt) } - err = writeMetadataFile(in.metadataFile, dt) } }() @@ -193,8 +195,8 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba "BAKE_LOCAL_PLATFORM": platforms.Format(platforms.DefaultSpec()), } - if in.listTargets { - cfg, err := bake.ParseFiles(files, defaults) + if in.listTargets || in.listVars { + cfg, pm, err := bake.ParseFiles(files, defaults) if err != nil { return err } @@ -204,7 +206,11 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba if err != nil { return err } - return printTargetList(dockerCli.Out(), cfg) + if in.listTargets { + return printTargetList(dockerCli.Out(), cfg) + } else if in.listVars { + return printVars(dockerCli.Out(), pm.AllVariables) + } } tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, defaults) @@ -439,6 +445,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`) flags.BoolVar(&options.printOnly, "print", false, "Print the options without building") flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets") + flags.BoolVar(&options.listVars, "list-variables", false, "List defined variables") flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`) flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`) flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`) @@ -503,6 +510,27 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names return } +func printVars(w io.Writer, vars []*hclparser.Variable) error { + slices.SortFunc(vars, func(a, b *hclparser.Variable) int { + return cmp.Compare(a.Name, b.Name) + }) + tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) + defer tw.Flush() + + tw.Write([]byte("VARIABLE\tVALUE\tDESCRIPTION\n")) + + for _, v := range vars { + var value string + if v.Value != nil { + value = *v.Value + } else { + value = "" + } + fmt.Fprintf(tw, "%s\t%s\t%s\n", v.Name, value, v.Description) + } + return nil +} + func printTargetList(w io.Writer, cfg *bake.Config) error { tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) defer tw.Flush() From 35313e865f9b5538821c822c900ee2683852a54b Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 28 Jun 2024 00:16:25 -0700 Subject: [PATCH 5/7] bake: add tests for call and list Signed-off-by: Tonis Tiigi --- docs/reference/buildx_bake.md | 4 + tests/bake.go | 165 ++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md index 762b70ab..fbb8ab58 100644 --- a/docs/reference/buildx_bake.md +++ b/docs/reference/buildx_bake.md @@ -16,7 +16,11 @@ Build from a file | Name | Type | Default | Description | |:------------------------------------|:--------------|:--------|:----------------------------------------------------------------------------------------------------| | [`--builder`](#builder) | `string` | | Override the configured builder instance | +| `--call` | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) | +| `--check` | `bool` | | Shorthand for `--call=check` | | [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file | +| `--list-targets` | `bool` | | List available targets | +| `--list-variables` | `bool` | | List defined variables | | `--load` | `bool` | | Shorthand for `--set=*.output=type=docker` | | [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file | | [`--no-cache`](#no-cache) | `bool` | | Do not use cache when building the image | diff --git a/tests/bake.go b/tests/bake.go index f50e6608..da70629b 100644 --- a/tests/bake.go +++ b/tests/bake.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "testing" "github.com/containerd/continuity/fs/fstest" @@ -48,6 +49,10 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){ testBakeMetadataWarningsDedup, testBakeMultiExporters, testBakeLoadPush, + testListTargets, + testListVariables, + testBakeCallCheck, + testBakeCallCheckFlag, } func testBakeLocal(t *testing.T, sb integration.Sandbox) { @@ -951,3 +956,163 @@ target "default" { // TODO: test metadata file when supported by multi exporters https://github.com/docker/buildx/issues/2181 } + +func testListTargets(t *testing.T, sb integration.Sandbox) { + bakefile := []byte(` +target "foo" { + description = "This builds foo" +} +target "abc" { +} +`) + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + ) + + out, err := bakeCmd( + sb, + withDir(dir), + withArgs("--list-targets"), + ) + require.NoError(t, err, out) + + require.Equal(t, "TARGET\tDESCRIPTION\nabc\t\nfoo\tThis builds foo", strings.TrimSpace(out)) +} + +func testListVariables(t *testing.T, sb integration.Sandbox) { + bakefile := []byte(` +variable "foo" { + default = "bar" + description = "This is foo" +} +variable "abc" { + default = null +} +variable "def" { +} +target "default" { +} +`) + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + ) + + out, err := bakeCmd( + sb, + withDir(dir), + withArgs("--list-variables"), + ) + require.NoError(t, err, out) + + require.Equal(t, "VARIABLE\tVALUE\tDESCRIPTION\nabc\t\t\t\ndef\t\t\t\nfoo\t\tbar\tThis is foo", strings.TrimSpace(out)) +} + +func testBakeCallCheck(t *testing.T, sb integration.Sandbox) { + dockerfile := []byte(` +FROM scratch +COPy foo /foo + `) + bakefile := []byte(` +target "validate" { + call = "check" +} +`) + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + + out, err := bakeCmd( + sb, + withDir(dir), + withArgs("validate"), + ) + require.Error(t, err, out) + + require.Contains(t, out, "validate") + require.Contains(t, out, "ConsistentInstructionCasing") +} + +func testBakeCallCheckFlag(t *testing.T, sb integration.Sandbox) { + dockerfile := []byte(` +FROM scratch +COPy foo /foo + `) + dockerfile2 := []byte(` +FROM scratch +COPY foo$BAR /foo + `) + bakefile := []byte(` +target "build" { + dockerfile = "a.Dockerfile" +} + +target "another" { + dockerfile = "b.Dockerfile" +} +`) + dir := tmpdir( + t, + fstest.CreateFile("docker-bake.hcl", bakefile, 0600), + fstest.CreateFile("a.Dockerfile", dockerfile, 0600), + fstest.CreateFile("b.Dockerfile", dockerfile2, 0600), + ) + + out, err := bakeCmd( + sb, + withDir(dir), + withArgs("build", "another", "--check"), + ) + require.Error(t, err, out) + + require.Contains(t, out, "build") + require.Contains(t, out, "ConsistentInstructionCasing") + + require.Contains(t, out, "another") + require.Contains(t, out, "UndefinedVar") + + out, err = bakeCmd( + sb, + withDir(dir), + withArgs("build", "another", "--call", "check,format=json"), + ) + require.Error(t, err, out) + + var res map[string]any + err = json.Unmarshal([]byte(out), &res) + require.NoError(t, err, out) + + targets, ok := res["target"].(map[string]any) + require.True(t, ok) + + build, ok := targets["build"].(map[string]any) + require.True(t, ok) + + _, ok = build["build"] + require.True(t, ok) + + check, ok := build["check"].(map[string]any) + require.True(t, ok) + + warnings, ok := check["warnings"].([]any) + require.True(t, ok) + + require.Len(t, warnings, 1) + + another, ok := targets["another"].(map[string]any) + require.True(t, ok) + + _, ok = another["build"] + require.True(t, ok) + + check, ok = another["check"].(map[string]any) + require.True(t, ok) + + warnings, ok = check["warnings"].([]any) + require.True(t, ok) + + require.Len(t, warnings, 1) +} From cc097db675b439e99d0ebb7e524e16ba22f92a6f Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 28 Jun 2024 18:21:19 -0700 Subject: [PATCH 6/7] bake: fix printer reset before metadata written Signed-off-by: Tonis Tiigi --- commands/bake.go | 1 - util/progress/printer.go | 13 ++++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/commands/bake.go b/commands/bake.go index 12c89d53..95b5b658 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -302,7 +302,6 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba } err = printer.Wait() - printer = nil if err != nil { return err } diff --git a/util/progress/printer.go b/util/progress/printer.go index deef3a67..05201933 100644 --- a/util/progress/printer.go +++ b/util/progress/printer.go @@ -19,9 +19,10 @@ import ( type Printer struct { status chan *client.SolveStatus - ready chan struct{} - done chan struct{} - paused chan struct{} + ready chan struct{} + done chan struct{} + paused chan struct{} + closeOnce sync.Once err error warnings []client.VertexWarning @@ -36,8 +37,10 @@ type Printer struct { } func (p *Printer) Wait() error { - close(p.status) - <-p.done + p.closeOnce.Do(func() { + close(p.status) + <-p.done + }) return p.err } From 153e5ed274cb9afc10a1b44d037c2422544ab4c4 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 3 Jul 2024 18:22:22 +0200 Subject: [PATCH 7/7] mark list-targets and list-variables as hidden and experimental Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- commands/bake.go | 12 ++++++++++-- docs/reference/buildx_bake.md | 2 -- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/commands/bake.go b/commands/bake.go index 95b5b658..2834494e 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -21,6 +21,7 @@ import ( "github.com/docker/buildx/controller/pb" "github.com/docker/buildx/localstate" "github.com/docker/buildx/util/buildflags" + "github.com/docker/buildx/util/cobrautil" "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/confutil" "github.com/docker/buildx/util/desktop" @@ -443,16 +444,23 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file") flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`) flags.BoolVar(&options.printOnly, "print", false, "Print the options without building") - flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets") - flags.BoolVar(&options.listVars, "list-variables", false, "List defined variables") flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`) flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`) flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`) flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`) flags.StringVar(&options.callFunc, "call", "build", `Set method for evaluating build ("check", "outline", "targets")`) + flags.VarPF(callAlias(&options.callFunc, "check"), "check", "", `Shorthand for "--call=check"`) flags.Lookup("check").NoOptDefVal = "true" + flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets") + cobrautil.MarkFlagsExperimental(flags, "list-targets") + flags.MarkHidden("list-targets") + + flags.BoolVar(&options.listVars, "list-variables", false, "List defined variables") + cobrautil.MarkFlagsExperimental(flags, "list-variables") + flags.MarkHidden("list-variables") + commonBuildFlags(&cFlags, flags) return cmd diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md index fbb8ab58..0623f1f4 100644 --- a/docs/reference/buildx_bake.md +++ b/docs/reference/buildx_bake.md @@ -19,8 +19,6 @@ Build from a file | `--call` | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) | | `--check` | `bool` | | Shorthand for `--call=check` | | [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file | -| `--list-targets` | `bool` | | List available targets | -| `--list-variables` | `bool` | | List defined variables | | `--load` | `bool` | | Shorthand for `--set=*.output=type=docker` | | [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file | | [`--no-cache`](#no-cache) | `bool` | | Do not use cache when building the image |