Merge pull request #2900 from crazy-max/bake-list-flag

bake: replace --list-targets and --list-variables flags with --list flag
This commit is contained in:
Tõnis Tiigi 2025-01-10 07:59:28 -08:00 committed by GitHub
commit ada44e82ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 137 additions and 35 deletions

View File

@ -579,9 +579,9 @@ func (p *parser) validateVariables(vars map[string]*variable, ectx *hcl.EvalCont
} }
type Variable struct { type Variable struct {
Name string Name string `json:"name"`
Description string Description string `json:"description,omitempty"`
Value *string Value *string `json:"value,omitempty"`
} }
type ParseMeta struct { type ParseMeta struct {

View File

@ -25,7 +25,6 @@ import (
"github.com/docker/buildx/controller/pb" "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/localstate" "github.com/docker/buildx/localstate"
"github.com/docker/buildx/util/buildflags" "github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/confutil" "github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/desktop" "github.com/docker/buildx/util/desktop"
@ -38,24 +37,30 @@ import (
"github.com/moby/buildkit/util/progress/progressui" "github.com/moby/buildkit/util/progress/progressui"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tonistiigi/go-csvvalue"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
) )
type bakeOptions struct { type bakeOptions struct {
files []string files []string
overrides []string overrides []string
printOnly bool
listTargets bool sbom string
listVars bool provenance string
sbom string allow []string
provenance string
allow []string
builder string builder string
metadataFile string metadataFile string
exportPush bool exportPush bool
exportLoad bool exportLoad bool
callFunc string callFunc string
print bool
list string
// TODO: remove deprecated flags
listTargets bool
listVars bool
} }
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) { func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
@ -121,9 +126,13 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
var nodes []builder.Node var nodes []builder.Node
var progressConsoleDesc, progressTextDesc string var progressConsoleDesc, progressTextDesc string
if in.print && in.list != "" {
return errors.New("--print and --list are mutually exclusive")
}
// instance only needed for reading remote bake files or building // instance only needed for reading remote bake files or building
var driverType string var driverType string
if url != "" || !(in.printOnly || in.listTargets || in.listVars) { if url != "" || !(in.print || in.list != "") {
b, err := builder.New(dockerCli, b, err := builder.New(dockerCli,
builder.WithName(in.builder), builder.WithName(in.builder),
builder.WithContextPathHash(contextPathHash), builder.WithContextPathHash(contextPathHash),
@ -184,7 +193,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
"BAKE_LOCAL_PLATFORM": platforms.Format(platforms.DefaultSpec()), "BAKE_LOCAL_PLATFORM": platforms.Format(platforms.DefaultSpec()),
} }
if in.listTargets || in.listVars { if in.list != "" {
cfg, pm, err := bake.ParseFiles(files, defaults) cfg, pm, err := bake.ParseFiles(files, defaults)
if err != nil { if err != nil {
return err return err
@ -192,10 +201,15 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
if err = printer.Wait(); err != nil { if err = printer.Wait(); err != nil {
return err return err
} }
if in.listTargets { list, err := parseList(in.list)
return printTargetList(dockerCli.Out(), cfg) if err != nil {
} else if in.listVars { return err
return printVars(dockerCli.Out(), pm.AllVariables) }
switch list.Type {
case "targets":
return printTargetList(dockerCli.Out(), list.Format, cfg)
case "variables":
return printVars(dockerCli.Out(), list.Format, pm.AllVariables)
} }
} }
@ -231,7 +245,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
Target: tgts, Target: tgts,
} }
if in.printOnly { if in.print {
if err = printer.Wait(); err != nil { if err = printer.Wait(); err != nil {
return err return err
} }
@ -427,6 +441,13 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
if !cmd.Flags().Lookup("pull").Changed { if !cmd.Flags().Lookup("pull").Changed {
cFlags.pull = nil cFlags.pull = nil
} }
if options.list == "" {
if options.listTargets {
options.list = "targets"
} else if options.listVars {
options.list = "variables"
}
}
options.builder = rootOpts.builder options.builder = rootOpts.builder
options.metadataFile = cFlags.metadataFile options.metadataFile = cFlags.metadataFile
// Other common flags (noCache, pull and progress) are processed in runBake function. // Other common flags (noCache, pull and progress) are processed in runBake function.
@ -439,7 +460,6 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *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.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`) 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.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`) 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.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`)
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`) flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`)
@ -450,13 +470,16 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
flags.VarPF(callAlias(&options.callFunc, "check"), "check", "", `Shorthand for "--call=check"`) flags.VarPF(callAlias(&options.callFunc, "check"), "check", "", `Shorthand for "--call=check"`)
flags.Lookup("check").NoOptDefVal = "true" flags.Lookup("check").NoOptDefVal = "true"
flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets") flags.BoolVar(&options.print, "print", false, "Print the options without building")
cobrautil.MarkFlagsExperimental(flags, "list-targets") flags.StringVar(&options.list, "list", "", "List targets or variables")
flags.MarkHidden("list-targets")
// TODO: remove deprecated flags
flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets")
flags.MarkHidden("list-targets")
flags.MarkDeprecated("list-targets", "list-targets is deprecated, use list=targets instead")
flags.BoolVar(&options.listVars, "list-variables", false, "List defined variables") flags.BoolVar(&options.listVars, "list-variables", false, "List defined variables")
cobrautil.MarkFlagsExperimental(flags, "list-variables")
flags.MarkHidden("list-variables") flags.MarkHidden("list-variables")
flags.MarkDeprecated("list-variables", "list-variables is deprecated, use list=variables instead")
commonBuildFlags(&cFlags, flags) commonBuildFlags(&cFlags, flags)
@ -557,10 +580,70 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names
return return
} }
func printVars(w io.Writer, vars []*hclparser.Variable) error { type listEntry struct {
Type string
Format string
}
func parseList(input string) (listEntry, error) {
res := listEntry{}
fields, err := csvvalue.Fields(input, nil)
if err != nil {
return res, err
}
if len(fields) == 1 && fields[0] == input && !strings.HasPrefix(input, "type=") {
res.Type = input
}
if res.Type == "" {
for _, field := range fields {
key, value, ok := strings.Cut(field, "=")
if !ok {
return res, errors.Errorf("invalid value %s", field)
}
key = strings.TrimSpace(strings.ToLower(key))
switch key {
case "type":
res.Type = value
case "format":
res.Format = value
default:
return res, errors.Errorf("unexpected key '%s' in '%s'", key, field)
}
}
}
if res.Format == "" {
res.Format = "table"
}
switch res.Type {
case "targets", "variables":
default:
return res, errors.Errorf("invalid list type %q", res.Type)
}
switch res.Format {
case "table", "json":
default:
return res, errors.Errorf("invalid list format %q", res.Format)
}
return res, nil
}
func printVars(w io.Writer, format string, vars []*hclparser.Variable) error {
slices.SortFunc(vars, func(a, b *hclparser.Variable) int { slices.SortFunc(vars, func(a, b *hclparser.Variable) int {
return cmp.Compare(a.Name, b.Name) return cmp.Compare(a.Name, b.Name)
}) })
if format == "json" {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(vars)
}
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0) tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
defer tw.Flush() defer tw.Flush()
@ -578,12 +661,7 @@ func printVars(w io.Writer, vars []*hclparser.Variable) error {
return nil return nil
} }
func printTargetList(w io.Writer, cfg *bake.Config) error { func printTargetList(w io.Writer, format string, 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 { type targetOrGroup struct {
name string name string
target *bake.Target target *bake.Target
@ -602,6 +680,20 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
return cmp.Compare(a.name, b.name) return cmp.Compare(a.name, b.name)
}) })
var tw *tabwriter.Writer
if format == "table" {
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
defer tw.Flush()
tw.Write([]byte("TARGET\tDESCRIPTION\n"))
}
type targetList struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Group bool `json:"group,omitempty"`
}
var targetsList []targetList
for _, tgt := range list { for _, tgt := range list {
if strings.HasPrefix(tgt.name, "_") { if strings.HasPrefix(tgt.name, "_") {
// convention for a private target // convention for a private target
@ -610,9 +702,9 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
var descr string var descr string
if tgt.target != nil { if tgt.target != nil {
descr = tgt.target.Description descr = tgt.target.Description
targetsList = append(targetsList, targetList{Name: tgt.name, Description: descr})
} else if tgt.group != nil { } else if tgt.group != nil {
descr = tgt.group.Description descr = tgt.group.Description
if len(tgt.group.Targets) > 0 { if len(tgt.group.Targets) > 0 {
slices.Sort(tgt.group.Targets) slices.Sort(tgt.group.Targets)
names := strings.Join(tgt.group.Targets, ", ") names := strings.Join(tgt.group.Targets, ", ")
@ -622,8 +714,17 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
descr = names descr = names
} }
} }
targetsList = append(targetsList, targetList{Name: tgt.name, Description: descr, Group: true})
} }
fmt.Fprintf(tw, "%s\t%s\n", tgt.name, descr) if format == "table" {
fmt.Fprintf(tw, "%s\t%s\n", tgt.name, descr)
}
}
if format == "json" {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(targetsList)
} }
return nil return nil

View File

@ -21,6 +21,7 @@ Build from a file
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` | | [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
| `-D`, `--debug` | `bool` | | Enable debug logging | | `-D`, `--debug` | `bool` | | Enable debug logging |
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file | | [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
| `--list` | `string` | | List targets or variables |
| `--load` | `bool` | | Shorthand for `--set=*.output=type=docker` | | `--load` | `bool` | | Shorthand for `--set=*.output=type=docker` |
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file | | [`--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 | | [`--no-cache`](#no-cache) | `bool` | | Do not use cache when building the image |

View File

@ -1599,7 +1599,7 @@ target "abc" {
out, err := bakeCmd( out, err := bakeCmd(
sb, sb,
withDir(dir), withDir(dir),
withArgs("--list-targets"), withArgs("--list=targets"),
) )
require.NoError(t, err, out) require.NoError(t, err, out)
@ -1628,7 +1628,7 @@ target "default" {
out, err := bakeCmd( out, err := bakeCmd(
sb, sb,
withDir(dir), withDir(dir),
withArgs("--list-variables"), withArgs("--list=variables"),
) )
require.NoError(t, err, out) require.NoError(t, err, out)