mirror of https://github.com/docker/buildx.git
bake: add call methods support and printing
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
7e49141c4e
commit
8da28574b0
14
bake/bake.go
14
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
|
||||
|
|
155
commands/bake.go
155
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)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue