mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	Merge pull request #2006 from ktock/debug-command
debug: Add `buildx debug` command
This commit is contained in:
		@@ -117,7 +117,7 @@ func NewResultHandle(ctx context.Context, cc *client.Client, opt client.SolveOpt
 | 
				
			|||||||
						gwClient: c,
 | 
											gwClient: c,
 | 
				
			||||||
						gwCtx:    ctx,
 | 
											gwCtx:    ctx,
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					respErr = se
 | 
										respErr = err // return original error to preserve stacktrace
 | 
				
			||||||
					close(done)
 | 
										close(done)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// Block until the caller closes the ResultHandle.
 | 
										// Block until the caller closes the ResultHandle.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ import (
 | 
				
			|||||||
	"github.com/containerd/console"
 | 
						"github.com/containerd/console"
 | 
				
			||||||
	"github.com/docker/buildx/build"
 | 
						"github.com/docker/buildx/build"
 | 
				
			||||||
	"github.com/docker/buildx/builder"
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/commands/debug"
 | 
				
			||||||
	"github.com/docker/buildx/controller"
 | 
						"github.com/docker/buildx/controller"
 | 
				
			||||||
	cbuild "github.com/docker/buildx/controller/build"
 | 
						cbuild "github.com/docker/buildx/controller/build"
 | 
				
			||||||
	"github.com/docker/buildx/controller/control"
 | 
						"github.com/docker/buildx/controller/control"
 | 
				
			||||||
@@ -78,9 +79,6 @@ type buildOptions struct {
 | 
				
			|||||||
	target         string
 | 
						target         string
 | 
				
			||||||
	ulimits        *dockeropts.UlimitOpt
 | 
						ulimits        *dockeropts.UlimitOpt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	invoke  *invokeConfig
 | 
					 | 
				
			||||||
	noBuild bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	attests    []string
 | 
						attests    []string
 | 
				
			||||||
	sbom       string
 | 
						sbom       string
 | 
				
			||||||
	provenance string
 | 
						provenance string
 | 
				
			||||||
@@ -96,6 +94,8 @@ type buildOptions struct {
 | 
				
			|||||||
	exportLoad   bool
 | 
						exportLoad   bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	control.ControlOptions
 | 
						control.ControlOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						invokeConfig *invokeConfig
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error) {
 | 
					func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error) {
 | 
				
			||||||
@@ -338,11 +338,10 @@ func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *controllera
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, options buildOptions, printer *progress.Printer) (*client.SolveResponse, error) {
 | 
					func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, options buildOptions, printer *progress.Printer) (*client.SolveResponse, error) {
 | 
				
			||||||
	if options.invoke != nil && (options.dockerfileName == "-" || options.contextPath == "-") {
 | 
						if options.invokeConfig != nil && (options.dockerfileName == "-" || options.contextPath == "-") {
 | 
				
			||||||
		// stdin must be usable for monitor
 | 
							// stdin must be usable for monitor
 | 
				
			||||||
		return nil, errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
 | 
							return nil, errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	c, err := controller.NewController(ctx, options.ControlOptions, dockerCli, printer)
 | 
						c, err := controller.NewController(ctx, options.ControlOptions, dockerCli, printer)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -365,7 +364,6 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
 | 
				
			|||||||
	var resp *client.SolveResponse
 | 
						var resp *client.SolveResponse
 | 
				
			||||||
	f := ioset.NewSingleForwarder()
 | 
						f := ioset.NewSingleForwarder()
 | 
				
			||||||
	f.SetReader(dockerCli.In())
 | 
						f.SetReader(dockerCli.In())
 | 
				
			||||||
	if !options.noBuild {
 | 
					 | 
				
			||||||
	pr, pw := io.Pipe()
 | 
						pr, pw := io.Pipe()
 | 
				
			||||||
	f.SetWriter(pw, func() io.WriteCloser {
 | 
						f.SetWriter(pw, func() io.WriteCloser {
 | 
				
			||||||
		pw.Close() // propagate EOF
 | 
							pw.Close() // propagate EOF
 | 
				
			||||||
@@ -391,24 +389,19 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
 | 
				
			|||||||
	if err := pr.Close(); err != nil {
 | 
						if err := pr.Close(); err != nil {
 | 
				
			||||||
		logrus.Debug("failed to close stdin pipe reader")
 | 
							logrus.Debug("failed to close stdin pipe reader")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if options.invokeConfig != nil && options.invokeConfig.needsDebug(retErr) {
 | 
				
			||||||
 | 
							// Print errors before launching monitor
 | 
				
			||||||
 | 
							if err := printError(retErr, printer); err != nil {
 | 
				
			||||||
 | 
								logrus.Warnf("failed to print error information: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// post-build operations
 | 
					 | 
				
			||||||
	if options.invoke != nil && options.invoke.needsMonitor(retErr) {
 | 
					 | 
				
			||||||
		pr2, pw2 := io.Pipe()
 | 
							pr2, pw2 := io.Pipe()
 | 
				
			||||||
		f.SetWriter(pw2, func() io.WriteCloser {
 | 
							f.SetWriter(pw2, func() io.WriteCloser {
 | 
				
			||||||
			pw2.Close() // propagate EOF
 | 
								pw2.Close() // propagate EOF
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		con := console.Current()
 | 
							err = options.invokeConfig.runDebug(ctx, ref, opts, c, pr2, os.Stdout, os.Stderr, printer)
 | 
				
			||||||
		if err := con.SetRaw(); err != nil {
 | 
					 | 
				
			||||||
			if err := c.Disconnect(ctx, ref); err != nil {
 | 
					 | 
				
			||||||
				logrus.Warnf("disconnect error: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return nil, errors.Errorf("failed to configure terminal: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err = monitor.RunMonitor(ctx, ref, opts, options.invoke.InvokeConfig, c, pr2, os.Stdout, os.Stderr, printer)
 | 
					 | 
				
			||||||
		con.Reset()
 | 
					 | 
				
			||||||
		if err := pw2.Close(); err != nil {
 | 
							if err := pw2.Close(); err != nil {
 | 
				
			||||||
			logrus.Debug("failed to close monitor stdin pipe reader")
 | 
								logrus.Debug("failed to close monitor stdin pipe reader")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -424,10 +417,34 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
 | 
				
			|||||||
	return resp, retErr
 | 
						return resp, retErr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
 | 
					func printError(err error, printer *progress.Printer) error {
 | 
				
			||||||
	options := buildOptions{}
 | 
						if err := printer.Pause(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer printer.Unpause()
 | 
				
			||||||
 | 
						for _, s := range errdefs.Sources(err) {
 | 
				
			||||||
 | 
							s.Print(os.Stderr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newDebuggableBuild(dockerCli command.Cli, rootOpts *rootOptions) debug.DebuggableCmd {
 | 
				
			||||||
 | 
						return &debuggableBuild{dockerCli: dockerCli, rootOpts: rootOpts}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type debuggableBuild struct {
 | 
				
			||||||
 | 
						dockerCli command.Cli
 | 
				
			||||||
 | 
						rootOpts  *rootOptions
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *debuggableBuild) NewDebugger(cfg *debug.DebugConfig) *cobra.Command {
 | 
				
			||||||
 | 
						return buildCmd(b.dockerCli, b.rootOpts, cfg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.DebugConfig) *cobra.Command {
 | 
				
			||||||
	cFlags := &commonFlags{}
 | 
						cFlags := &commonFlags{}
 | 
				
			||||||
	var invokeFlag string
 | 
						options := &buildOptions{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd := &cobra.Command{
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
		Use:     "build [OPTIONS] PATH | URL | -",
 | 
							Use:     "build [OPTIONS] PATH | URL | -",
 | 
				
			||||||
@@ -449,15 +466,15 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
 | 
				
			|||||||
			options.progress = cFlags.progress
 | 
								options.progress = cFlags.progress
 | 
				
			||||||
			cmd.Flags().VisitAll(checkWarnedFlags)
 | 
								cmd.Flags().VisitAll(checkWarnedFlags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if invokeFlag != "" {
 | 
								if debugConfig != nil && (debugConfig.InvokeFlag != "" || debugConfig.OnFlag != "") {
 | 
				
			||||||
				invoke, err := parseInvokeConfig(invokeFlag)
 | 
									iConfig := new(invokeConfig)
 | 
				
			||||||
				if err != nil {
 | 
									if err := iConfig.parseInvokeConfig(debugConfig.InvokeFlag, debugConfig.OnFlag); err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				options.invoke = &invoke
 | 
									options.invokeConfig = iConfig
 | 
				
			||||||
				options.noBuild = invokeFlag == "debug-shell"
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return runBuild(dockerCli, options)
 | 
					
 | 
				
			||||||
 | 
								return runBuild(dockerCli, *options)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 | 
							ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 | 
				
			||||||
			return nil, cobra.ShellCompDirectiveFilterDirs
 | 
								return nil, cobra.ShellCompDirectiveFilterDirs
 | 
				
			||||||
@@ -535,8 +552,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
 | 
				
			|||||||
	flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--attest=type=provenance"`)
 | 
						flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--attest=type=provenance"`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if isExperimental() {
 | 
						if isExperimental() {
 | 
				
			||||||
		flags.StringVar(&invokeFlag, "invoke", "", "Invoke a command after the build")
 | 
							// TODO: move this to debug command if needed
 | 
				
			||||||
		flags.SetAnnotation("invoke", "experimentalCLI", nil)
 | 
					 | 
				
			||||||
		flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect")
 | 
							flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect")
 | 
				
			||||||
		flags.SetAnnotation("root", "experimentalCLI", nil)
 | 
							flags.SetAnnotation("root", "experimentalCLI", nil)
 | 
				
			||||||
		flags.BoolVar(&options.Detach, "detach", false, "Detach buildx server (supported only on linux)")
 | 
							flags.BoolVar(&options.Detach, "detach", false, "Detach buildx server (supported only on linux)")
 | 
				
			||||||
@@ -699,96 +715,6 @@ func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
 | 
				
			|||||||
	return txn.UpdateLastActivity(ng)
 | 
						return txn.UpdateLastActivity(ng)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type invokeConfig struct {
 | 
					 | 
				
			||||||
	controllerapi.InvokeConfig
 | 
					 | 
				
			||||||
	invokeFlag string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cfg *invokeConfig) needsMonitor(retErr error) bool {
 | 
					 | 
				
			||||||
	switch cfg.invokeFlag {
 | 
					 | 
				
			||||||
	case "debug-shell":
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	case "on-error":
 | 
					 | 
				
			||||||
		return retErr != nil
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return cfg.invokeFlag != ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func parseInvokeConfig(invoke string) (cfg invokeConfig, err error) {
 | 
					 | 
				
			||||||
	cfg.invokeFlag = invoke
 | 
					 | 
				
			||||||
	cfg.Tty = true
 | 
					 | 
				
			||||||
	cfg.NoCmd = true
 | 
					 | 
				
			||||||
	switch invoke {
 | 
					 | 
				
			||||||
	case "default", "debug-shell":
 | 
					 | 
				
			||||||
		return cfg, nil
 | 
					 | 
				
			||||||
	case "on-error":
 | 
					 | 
				
			||||||
		// NOTE: we overwrite the command to run because the original one should fail on the failed step.
 | 
					 | 
				
			||||||
		// TODO: make this configurable via flags or restorable from LLB.
 | 
					 | 
				
			||||||
		// Discussion: https://github.com/docker/buildx/pull/1640#discussion_r1113295900
 | 
					 | 
				
			||||||
		cfg.Cmd = []string{"/bin/sh"}
 | 
					 | 
				
			||||||
		cfg.NoCmd = false
 | 
					 | 
				
			||||||
		return cfg, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	csvReader := csv.NewReader(strings.NewReader(invoke))
 | 
					 | 
				
			||||||
	csvReader.LazyQuotes = true
 | 
					 | 
				
			||||||
	fields, err := csvReader.Read()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return cfg, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(fields) == 1 && !strings.Contains(fields[0], "=") {
 | 
					 | 
				
			||||||
		cfg.Cmd = []string{fields[0]}
 | 
					 | 
				
			||||||
		cfg.NoCmd = false
 | 
					 | 
				
			||||||
		return cfg, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	cfg.NoUser = true
 | 
					 | 
				
			||||||
	cfg.NoCwd = true
 | 
					 | 
				
			||||||
	for _, field := range fields {
 | 
					 | 
				
			||||||
		parts := strings.SplitN(field, "=", 2)
 | 
					 | 
				
			||||||
		if len(parts) != 2 {
 | 
					 | 
				
			||||||
			return cfg, errors.Errorf("invalid value %s", field)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		key := strings.ToLower(parts[0])
 | 
					 | 
				
			||||||
		value := parts[1]
 | 
					 | 
				
			||||||
		switch key {
 | 
					 | 
				
			||||||
		case "args":
 | 
					 | 
				
			||||||
			cfg.Cmd = append(cfg.Cmd, maybeJSONArray(value)...)
 | 
					 | 
				
			||||||
			cfg.NoCmd = false
 | 
					 | 
				
			||||||
		case "entrypoint":
 | 
					 | 
				
			||||||
			cfg.Entrypoint = append(cfg.Entrypoint, maybeJSONArray(value)...)
 | 
					 | 
				
			||||||
			if cfg.Cmd == nil {
 | 
					 | 
				
			||||||
				cfg.Cmd = []string{}
 | 
					 | 
				
			||||||
				cfg.NoCmd = false
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		case "env":
 | 
					 | 
				
			||||||
			cfg.Env = append(cfg.Env, maybeJSONArray(value)...)
 | 
					 | 
				
			||||||
		case "user":
 | 
					 | 
				
			||||||
			cfg.User = value
 | 
					 | 
				
			||||||
			cfg.NoUser = false
 | 
					 | 
				
			||||||
		case "cwd":
 | 
					 | 
				
			||||||
			cfg.Cwd = value
 | 
					 | 
				
			||||||
			cfg.NoCwd = false
 | 
					 | 
				
			||||||
		case "tty":
 | 
					 | 
				
			||||||
			cfg.Tty, err = strconv.ParseBool(value)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return cfg, errors.Errorf("failed to parse tty: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			return cfg, errors.Errorf("unknown key %q", key)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return cfg, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func maybeJSONArray(v string) []string {
 | 
					 | 
				
			||||||
	var list []string
 | 
					 | 
				
			||||||
	if err := json.Unmarshal([]byte(v), &list); err == nil {
 | 
					 | 
				
			||||||
		return list
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return []string{v}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func listToMap(values []string, defaultEnv bool) (map[string]string, error) {
 | 
					func listToMap(values []string, defaultEnv bool) (map[string]string, error) {
 | 
				
			||||||
	result := make(map[string]string, len(values))
 | 
						result := make(map[string]string, len(values))
 | 
				
			||||||
	for _, value := range values {
 | 
						for _, value := range values {
 | 
				
			||||||
@@ -897,3 +823,108 @@ func printValue(printer printFunc, version string, format string, res map[string
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return printer([]byte(res["result.json"]), os.Stdout)
 | 
						return printer([]byte(res["result.json"]), os.Stdout)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type invokeConfig struct {
 | 
				
			||||||
 | 
						controllerapi.InvokeConfig
 | 
				
			||||||
 | 
						onFlag     string
 | 
				
			||||||
 | 
						invokeFlag string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cfg *invokeConfig) needsDebug(retErr error) bool {
 | 
				
			||||||
 | 
						switch cfg.onFlag {
 | 
				
			||||||
 | 
						case "always":
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						case "error":
 | 
				
			||||||
 | 
							return retErr != nil
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return cfg.invokeFlag != ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cfg *invokeConfig) runDebug(ctx context.Context, ref string, options *controllerapi.BuildOptions, c control.BuildxController, stdin io.ReadCloser, stdout io.WriteCloser, stderr console.File, progress *progress.Printer) error {
 | 
				
			||||||
 | 
						con := console.Current()
 | 
				
			||||||
 | 
						if err := con.SetRaw(); err != nil {
 | 
				
			||||||
 | 
							// TODO: run disconnect in build command (on error case)
 | 
				
			||||||
 | 
							if err := c.Disconnect(ctx, ref); err != nil {
 | 
				
			||||||
 | 
								logrus.Warnf("disconnect error: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return errors.Errorf("failed to configure terminal: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer con.Reset()
 | 
				
			||||||
 | 
						return monitor.RunMonitor(ctx, ref, options, cfg.InvokeConfig, c, stdin, stdout, stderr, progress)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cfg *invokeConfig) parseInvokeConfig(invoke, on string) error {
 | 
				
			||||||
 | 
						cfg.onFlag = on
 | 
				
			||||||
 | 
						cfg.invokeFlag = invoke
 | 
				
			||||||
 | 
						cfg.Tty = true
 | 
				
			||||||
 | 
						cfg.NoCmd = true
 | 
				
			||||||
 | 
						switch invoke {
 | 
				
			||||||
 | 
						case "default", "":
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						case "on-error":
 | 
				
			||||||
 | 
							// NOTE: we overwrite the command to run because the original one should fail on the failed step.
 | 
				
			||||||
 | 
							// TODO: make this configurable via flags or restorable from LLB.
 | 
				
			||||||
 | 
							// Discussion: https://github.com/docker/buildx/pull/1640#discussion_r1113295900
 | 
				
			||||||
 | 
							cfg.Cmd = []string{"/bin/sh"}
 | 
				
			||||||
 | 
							cfg.NoCmd = false
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						csvReader := csv.NewReader(strings.NewReader(invoke))
 | 
				
			||||||
 | 
						csvReader.LazyQuotes = true
 | 
				
			||||||
 | 
						fields, err := csvReader.Read()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(fields) == 1 && !strings.Contains(fields[0], "=") {
 | 
				
			||||||
 | 
							cfg.Cmd = []string{fields[0]}
 | 
				
			||||||
 | 
							cfg.NoCmd = false
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cfg.NoUser = true
 | 
				
			||||||
 | 
						cfg.NoCwd = true
 | 
				
			||||||
 | 
						for _, field := range fields {
 | 
				
			||||||
 | 
							parts := strings.SplitN(field, "=", 2)
 | 
				
			||||||
 | 
							if len(parts) != 2 {
 | 
				
			||||||
 | 
								return errors.Errorf("invalid value %s", field)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							key := strings.ToLower(parts[0])
 | 
				
			||||||
 | 
							value := parts[1]
 | 
				
			||||||
 | 
							switch key {
 | 
				
			||||||
 | 
							case "args":
 | 
				
			||||||
 | 
								cfg.Cmd = append(cfg.Cmd, maybeJSONArray(value)...)
 | 
				
			||||||
 | 
								cfg.NoCmd = false
 | 
				
			||||||
 | 
							case "entrypoint":
 | 
				
			||||||
 | 
								cfg.Entrypoint = append(cfg.Entrypoint, maybeJSONArray(value)...)
 | 
				
			||||||
 | 
								if cfg.Cmd == nil {
 | 
				
			||||||
 | 
									cfg.Cmd = []string{}
 | 
				
			||||||
 | 
									cfg.NoCmd = false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case "env":
 | 
				
			||||||
 | 
								cfg.Env = append(cfg.Env, maybeJSONArray(value)...)
 | 
				
			||||||
 | 
							case "user":
 | 
				
			||||||
 | 
								cfg.User = value
 | 
				
			||||||
 | 
								cfg.NoUser = false
 | 
				
			||||||
 | 
							case "cwd":
 | 
				
			||||||
 | 
								cfg.Cwd = value
 | 
				
			||||||
 | 
								cfg.NoCwd = false
 | 
				
			||||||
 | 
							case "tty":
 | 
				
			||||||
 | 
								cfg.Tty, err = strconv.ParseBool(value)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return errors.Errorf("failed to parse tty: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return errors.Errorf("unknown key %q", key)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func maybeJSONArray(v string) []string {
 | 
				
			||||||
 | 
						var list []string
 | 
				
			||||||
 | 
						if err := json.Unmarshal([]byte(v), &list); err == nil {
 | 
				
			||||||
 | 
							return list
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return []string{v}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,80 +0,0 @@
 | 
				
			|||||||
package commands
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"runtime"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/containerd/console"
 | 
					 | 
				
			||||||
	"github.com/docker/buildx/controller"
 | 
					 | 
				
			||||||
	"github.com/docker/buildx/controller/control"
 | 
					 | 
				
			||||||
	controllerapi "github.com/docker/buildx/controller/pb"
 | 
					 | 
				
			||||||
	"github.com/docker/buildx/monitor"
 | 
					 | 
				
			||||||
	"github.com/docker/buildx/util/progress"
 | 
					 | 
				
			||||||
	"github.com/docker/cli/cli/command"
 | 
					 | 
				
			||||||
	"github.com/moby/buildkit/util/progress/progressui"
 | 
					 | 
				
			||||||
	"github.com/pkg/errors"
 | 
					 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
					 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func debugShellCmd(dockerCli command.Cli) *cobra.Command {
 | 
					 | 
				
			||||||
	var options control.ControlOptions
 | 
					 | 
				
			||||||
	var progressMode string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cmd := &cobra.Command{
 | 
					 | 
				
			||||||
		Use:   "debug-shell",
 | 
					 | 
				
			||||||
		Short: "Start a monitor",
 | 
					 | 
				
			||||||
		Annotations: map[string]string{
 | 
					 | 
				
			||||||
			"experimentalCLI": "",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
					 | 
				
			||||||
			printer, err := progress.NewPrinter(context.TODO(), os.Stderr, progressui.DisplayMode(progressMode))
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx := context.TODO()
 | 
					 | 
				
			||||||
			c, err := controller.NewController(ctx, options, dockerCli, printer)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			defer func() {
 | 
					 | 
				
			||||||
				if err := c.Close(); err != nil {
 | 
					 | 
				
			||||||
					logrus.Warnf("failed to close server connection %v", err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}()
 | 
					 | 
				
			||||||
			con := console.Current()
 | 
					 | 
				
			||||||
			if err := con.SetRaw(); err != nil {
 | 
					 | 
				
			||||||
				return errors.Errorf("failed to configure terminal: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err = monitor.RunMonitor(ctx, "", nil, controllerapi.InvokeConfig{
 | 
					 | 
				
			||||||
				Tty: true,
 | 
					 | 
				
			||||||
			}, c, dockerCli.In(), os.Stdout, os.Stderr, printer)
 | 
					 | 
				
			||||||
			con.Reset()
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flags := cmd.Flags()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect")
 | 
					 | 
				
			||||||
	flags.SetAnnotation("root", "experimentalCLI", nil)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flags.BoolVar(&options.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server (supported only on linux)")
 | 
					 | 
				
			||||||
	flags.SetAnnotation("detach", "experimentalCLI", nil)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flags.StringVar(&options.ServerConfig, "server-config", "", "Specify buildx server config file (used only when launching new server)")
 | 
					 | 
				
			||||||
	flags.SetAnnotation("server-config", "experimentalCLI", nil)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flags.StringVar(&progressMode, "progress", "auto", `Set type of progress output ("auto", "plain", "tty"). Use plain to show container output`)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return cmd
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func addDebugShellCommand(cmd *cobra.Command, dockerCli command.Cli) {
 | 
					 | 
				
			||||||
	cmd.AddCommand(
 | 
					 | 
				
			||||||
		debugShellCmd(dockerCli),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										96
									
								
								commands/debug/root.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								commands/debug/root.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					package debug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/console"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/controller"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/controller/control"
 | 
				
			||||||
 | 
						controllerapi "github.com/docker/buildx/controller/pb"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/monitor"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/progress"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command"
 | 
				
			||||||
 | 
						"github.com/moby/buildkit/util/progress/progressui"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DebugConfig is a user-specified configuration for the debugger.
 | 
				
			||||||
 | 
					type DebugConfig struct {
 | 
				
			||||||
 | 
						// InvokeFlag is a flag to configure the launched debugger and the commaned executed on the debugger.
 | 
				
			||||||
 | 
						InvokeFlag string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// OnFlag is a flag to configure the timing of launching the debugger.
 | 
				
			||||||
 | 
						OnFlag string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DebuggableCmd is a command that supports debugger with recognizing the user-specified DebugConfig.
 | 
				
			||||||
 | 
					type DebuggableCmd interface {
 | 
				
			||||||
 | 
						// NewDebugger returns the new *cobra.Command with support for the debugger with recognizing DebugConfig.
 | 
				
			||||||
 | 
						NewDebugger(*DebugConfig) *cobra.Command
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
 | 
				
			||||||
 | 
						var controlOptions control.ControlOptions
 | 
				
			||||||
 | 
						var progressMode string
 | 
				
			||||||
 | 
						var options DebugConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "debug",
 | 
				
			||||||
 | 
							Short: "Start debugger",
 | 
				
			||||||
 | 
							Args:  cobra.NoArgs,
 | 
				
			||||||
 | 
							Annotations: map[string]string{
 | 
				
			||||||
 | 
								"experimentalCLI": "",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							RunE: func(cmd *cobra.Command, args []string) error {
 | 
				
			||||||
 | 
								printer, err := progress.NewPrinter(context.TODO(), os.Stderr, progressui.DisplayMode(progressMode))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx := context.TODO()
 | 
				
			||||||
 | 
								c, err := controller.NewController(ctx, controlOptions, dockerCli, printer)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer func() {
 | 
				
			||||||
 | 
									if err := c.Close(); err != nil {
 | 
				
			||||||
 | 
										logrus.Warnf("failed to close server connection %v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}()
 | 
				
			||||||
 | 
								con := console.Current()
 | 
				
			||||||
 | 
								if err := con.SetRaw(); err != nil {
 | 
				
			||||||
 | 
									return errors.Errorf("failed to configure terminal: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = monitor.RunMonitor(ctx, "", nil, controllerapi.InvokeConfig{
 | 
				
			||||||
 | 
									Tty: true,
 | 
				
			||||||
 | 
								}, c, dockerCli.In(), os.Stdout, os.Stderr, printer)
 | 
				
			||||||
 | 
								con.Reset()
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flags := cmd.Flags()
 | 
				
			||||||
 | 
						flags.StringVar(&options.InvokeFlag, "invoke", "", "Launch a monitor with executing specified command")
 | 
				
			||||||
 | 
						flags.SetAnnotation("invoke", "experimentalCLI", nil)
 | 
				
			||||||
 | 
						flags.StringVar(&options.OnFlag, "on", "error", "When to launch the monitor ([always, error])")
 | 
				
			||||||
 | 
						flags.SetAnnotation("on", "experimentalCLI", nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flags.StringVar(&controlOptions.Root, "root", "", "Specify root directory of server to connect for the monitor")
 | 
				
			||||||
 | 
						flags.SetAnnotation("root", "experimentalCLI", nil)
 | 
				
			||||||
 | 
						flags.BoolVar(&controlOptions.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server for the monitor (supported only on linux)")
 | 
				
			||||||
 | 
						flags.SetAnnotation("detach", "experimentalCLI", nil)
 | 
				
			||||||
 | 
						flags.StringVar(&controlOptions.ServerConfig, "server-config", "", "Specify buildx server config file for the monitor (used only when launching new server)")
 | 
				
			||||||
 | 
						flags.SetAnnotation("server-config", "experimentalCLI", nil)
 | 
				
			||||||
 | 
						flags.StringVar(&progressMode, "progress", "auto", `Set type of progress output ("auto", "plain", "tty") for the monitor. Use plain to show container output`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, c := range children {
 | 
				
			||||||
 | 
							cmd.AddCommand(c.NewDebugger(&options))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,6 +3,7 @@ package commands
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						debugcmd "github.com/docker/buildx/commands/debug"
 | 
				
			||||||
	imagetoolscmd "github.com/docker/buildx/commands/imagetools"
 | 
						imagetoolscmd "github.com/docker/buildx/commands/imagetools"
 | 
				
			||||||
	"github.com/docker/buildx/controller/remote"
 | 
						"github.com/docker/buildx/controller/remote"
 | 
				
			||||||
	"github.com/docker/buildx/util/cobrautil/completion"
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
@@ -71,7 +72,7 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
 | 
				
			|||||||
	rootFlags(opts, cmd.PersistentFlags())
 | 
						rootFlags(opts, cmd.PersistentFlags())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd.AddCommand(
 | 
						cmd.AddCommand(
 | 
				
			||||||
		buildCmd(dockerCli, opts),
 | 
							buildCmd(dockerCli, opts, nil),
 | 
				
			||||||
		bakeCmd(dockerCli, opts),
 | 
							bakeCmd(dockerCli, opts),
 | 
				
			||||||
		createCmd(dockerCli),
 | 
							createCmd(dockerCli),
 | 
				
			||||||
		rmCmd(dockerCli, opts),
 | 
							rmCmd(dockerCli, opts),
 | 
				
			||||||
@@ -87,8 +88,10 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
 | 
				
			|||||||
		imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
 | 
							imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if isExperimental() {
 | 
						if isExperimental() {
 | 
				
			||||||
 | 
							cmd.AddCommand(debugcmd.RootCmd(dockerCli,
 | 
				
			||||||
 | 
								newDebuggableBuild(dockerCli, opts),
 | 
				
			||||||
 | 
							))
 | 
				
			||||||
		remote.AddControllerCommands(cmd, dockerCli)
 | 
							remote.AddControllerCommands(cmd, dockerCli)
 | 
				
			||||||
		addDebugShellCommand(cmd, dockerCli)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd.RegisterFlagCompletionFunc( //nolint:errcheck
 | 
						cmd.RegisterFlagCompletionFunc( //nolint:errcheck
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -137,8 +137,12 @@ func (m *Manager) StartProcess(pid string, resultCtx *build.ResultHandle, cfg *p
 | 
				
			|||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		if err = ctr.Exec(ctx, cfg, in.Stdin, in.Stdout, in.Stderr); err != nil {
 | 
							if err = ctr.Exec(ctx, cfg, in.Stdin, in.Stdout, in.Stderr); err != nil {
 | 
				
			||||||
 | 
								if errors.Is(err, context.Canceled) {
 | 
				
			||||||
 | 
									logrus.Debugf("process canceled: %v", err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
				logrus.Errorf("failed to exec process: %v", err)
 | 
									logrus.Errorf("failed to exec process: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		logrus.Debugf("finished process %s %v", pid, cfg.Entrypoint)
 | 
							logrus.Debugf("finished process %s %v", pid, cfg.Entrypoint)
 | 
				
			||||||
		m.processes.Delete(pid)
 | 
							m.processes.Delete(pid)
 | 
				
			||||||
		processCancelFunc()
 | 
							processCancelFunc()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,11 +19,13 @@ your environment.
 | 
				
			|||||||
$ export BUILDX_EXPERIMENTAL=1
 | 
					$ export BUILDX_EXPERIMENTAL=1
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To start a debug session for a build, you can use the `--invoke` flag with the
 | 
					To start a debug session for a build, you can use the `buildx debug` command with `--invoke` flag to specify a command to launch in the resulting image.
 | 
				
			||||||
build command to specify a command to launch in the resulting image.
 | 
					`buildx debug` command provides `buildx debug build` subcommand that provides the same features as the normal `buildx build` command but allows launching the debugger session after the build.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Arguments available after `buildx debug build` are the same as the normal `buildx build`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```console
 | 
					```console
 | 
				
			||||||
$ docker buildx build --invoke /bin/sh .
 | 
					$ docker buildx debug --invoke /bin/sh build .
 | 
				
			||||||
[+] Building 4.2s (19/19) FINISHED
 | 
					[+] Building 4.2s (19/19) FINISHED
 | 
				
			||||||
 => [internal] connecting to local controller                                                                                   0.0s
 | 
					 => [internal] connecting to local controller                                                                                   0.0s
 | 
				
			||||||
 => [internal] load build definition from Dockerfile                                                                            0.0s
 | 
					 => [internal] load build definition from Dockerfile                                                                            0.0s
 | 
				
			||||||
@@ -56,16 +58,16 @@ Supported keys are `args` (can be JSON array format), `entrypoint` (can be JSON
 | 
				
			|||||||
Example:
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
$ docker buildx build --invoke 'entrypoint=["sh"],"args=[""-c"", ""env | grep -e FOO -e AAA""]","env=[""FOO=bar"", ""AAA=bbb""]"' .
 | 
					$ docker buildx debug --invoke 'entrypoint=["sh"],"args=[""-c"", ""env | grep -e FOO -e AAA""]","env=[""FOO=bar"", ""AAA=bbb""]"' build .
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### `on-error`
 | 
					#### `on` flag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you want to start a debug session when a build fails, you can use
 | 
					If you want to start a debug session when a build fails, you can use
 | 
				
			||||||
`--invoke=on-error` to start a debug session when the build fails.
 | 
					`--on=error` to start a debug session when the build fails.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```console
 | 
					```console
 | 
				
			||||||
$ docker buildx build --invoke on-error .
 | 
					$ docker buildx debug --on=error build .
 | 
				
			||||||
[+] Building 4.2s (19/19) FINISHED
 | 
					[+] Building 4.2s (19/19) FINISHED
 | 
				
			||||||
 => [internal] connecting to local controller                                                                                   0.0s
 | 
					 => [internal] connecting to local controller                                                                                   0.0s
 | 
				
			||||||
 => [internal] load build definition from Dockerfile                                                                            0.0s
 | 
					 => [internal] load build definition from Dockerfile                                                                            0.0s
 | 
				
			||||||
@@ -85,13 +87,13 @@ Interactive container was restarted with process "edmzor60nrag7rh1mbi4o9lm8". Pr
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
This allows you to explore the state of the image when the build failed.
 | 
					This allows you to explore the state of the image when the build failed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### `debug-shell`
 | 
					#### Launch the debug session directly with `buildx debug` subcommand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you want to drop into a debug session without first starting the build, you
 | 
					If you want to drop into a debug session without first starting the build, you
 | 
				
			||||||
can use `--invoke=debug-shell` to start a debug session.
 | 
					can use `buildx debug` command to start a debug session.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
$ docker buildx build --invoke debug-shell .
 | 
					$ docker buildx debug
 | 
				
			||||||
[+] Building 4.2s (19/19) FINISHED
 | 
					[+] Building 4.2s (19/19) FINISHED
 | 
				
			||||||
 => [internal] connecting to local controller                                                                                   0.0s
 | 
					 => [internal] connecting to local controller                                                                                   0.0s
 | 
				
			||||||
(buildx)
 | 
					(buildx)
 | 
				
			||||||
@@ -135,15 +137,15 @@ To detach the build process from the CLI, you can use the `--detach=true` flag w
 | 
				
			|||||||
the build command.
 | 
					the build command.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```console
 | 
					```console
 | 
				
			||||||
$ docker buildx build --detach=true --invoke /bin/sh .
 | 
					$ docker buildx debug --invoke /bin/sh build --detach=true .
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you start a debugging session using the `--invoke` flag with a detached
 | 
					If you start a debugging session using the `--invoke` flag with a detached
 | 
				
			||||||
build, then you can attach to it using the `buildx debug-shell` subcommand to
 | 
					build, then you can attach to it using the `buildx debug` command to
 | 
				
			||||||
immediately enter the monitor mode.
 | 
					immediately enter the monitor mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```console
 | 
					```console
 | 
				
			||||||
$ docker buildx debug-shell
 | 
					$ docker buildx debug
 | 
				
			||||||
[+] Building 0.0s (1/1) FINISHED                                                                                                                                                                                
 | 
					[+] Building 0.0s (1/1) FINISHED                                                                                                                                                                                
 | 
				
			||||||
 => [internal] connecting to remote controller
 | 
					 => [internal] connecting to remote controller
 | 
				
			||||||
(buildx) list
 | 
					(buildx) list
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,11 +10,11 @@ Extended build capabilities with BuildKit
 | 
				
			|||||||
### Subcommands
 | 
					### Subcommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Name                                 | Description                            |
 | 
					| Name                                 | Description                            |
 | 
				
			||||||
|:---------------------------------------|:---------------------------------------|
 | 
					|:-------------------------------------|:---------------------------------------|
 | 
				
			||||||
| [`bake`](buildx_bake.md)             | Build from a file                      |
 | 
					| [`bake`](buildx_bake.md)             | Build from a file                      |
 | 
				
			||||||
| [`build`](buildx_build.md)           | Start a build                          |
 | 
					| [`build`](buildx_build.md)           | Start a build                          |
 | 
				
			||||||
| [`create`](buildx_create.md)         | Create a new builder instance          |
 | 
					| [`create`](buildx_create.md)         | Create a new builder instance          |
 | 
				
			||||||
| [`debug-shell`](buildx_debug-shell.md) | Start a monitor                        |
 | 
					| [`debug`](buildx_debug.md)           | Start debugger                         |
 | 
				
			||||||
| [`du`](buildx_du.md)                 | Disk usage                             |
 | 
					| [`du`](buildx_du.md)                 | Disk usage                             |
 | 
				
			||||||
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
 | 
					| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
 | 
				
			||||||
| [`inspect`](buildx_inspect.md)       | Inspect current builder instance       |
 | 
					| [`inspect`](buildx_inspect.md)       | Inspect current builder instance       |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,6 @@ Start a build
 | 
				
			|||||||
| `--detach`                                                                                                                                             |               |           | Detach buildx server (supported only on linux)                                                      |
 | 
					| `--detach`                                                                                                                                             |               |           | Detach buildx server (supported only on linux)                                                      |
 | 
				
			||||||
| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#file), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#file) | `string`      |           | Name of the Dockerfile (default: `PATH/Dockerfile`)                                                 |
 | 
					| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#file), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#file) | `string`      |           | Name of the Dockerfile (default: `PATH/Dockerfile`)                                                 |
 | 
				
			||||||
| `--iidfile`                                                                                                                                            | `string`      |           | Write the image ID to the file                                                                      |
 | 
					| `--iidfile`                                                                                                                                            | `string`      |           | Write the image ID to the file                                                                      |
 | 
				
			||||||
| `--invoke`                                                                                                                                             | `string`      |           | Invoke a command after the build                                                                    |
 | 
					 | 
				
			||||||
| `--label`                                                                                                                                              | `stringArray` |           | Set metadata for an image                                                                           |
 | 
					| `--label`                                                                                                                                              | `stringArray` |           | Set metadata for an image                                                                           |
 | 
				
			||||||
| [`--load`](#load)                                                                                                                                      |               |           | Shorthand for `--output=type=docker`                                                                |
 | 
					| [`--load`](#load)                                                                                                                                      |               |           | Shorthand for `--output=type=docker`                                                                |
 | 
				
			||||||
| [`--metadata-file`](#metadata-file)                                                                                                                    | `string`      |           | Write build result metadata to the file                                                             |
 | 
					| [`--metadata-file`](#metadata-file)                                                                                                                    | `string`      |           | Write build result metadata to the file                                                             |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
# docker buildx debug-shell
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<!---MARKER_GEN_START-->
 | 
					 | 
				
			||||||
Start a monitor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Options
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| Name              | Type     | Default | Description                                                                              |
 | 
					 | 
				
			||||||
|:------------------|:---------|:--------|:-----------------------------------------------------------------------------------------|
 | 
					 | 
				
			||||||
| `--builder`       | `string` |         | Override the configured builder instance                                                 |
 | 
					 | 
				
			||||||
| `--detach`        |          |         | Detach buildx server (supported only on linux)                                           |
 | 
					 | 
				
			||||||
| `--progress`      | `string` | `auto`  | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
 | 
					 | 
				
			||||||
| `--root`          | `string` |         | Specify root directory of server to connect                                              |
 | 
					 | 
				
			||||||
| `--server-config` | `string` |         | Specify buildx server config file (used only when launching new server)                  |
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<!---MARKER_GEN_END-->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										27
									
								
								docs/reference/buildx_debug.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								docs/reference/buildx_debug.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					# docker buildx debug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					Start debugger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Subcommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name                             | Description   |
 | 
				
			||||||
 | 
					|:---------------------------------|:--------------|
 | 
				
			||||||
 | 
					| [`build`](buildx_debug_build.md) | Start a build |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name              | Type     | Default | Description                                                                                              |
 | 
				
			||||||
 | 
					|:------------------|:---------|:--------|:---------------------------------------------------------------------------------------------------------|
 | 
				
			||||||
 | 
					| `--builder`       | `string` |         | Override the configured builder instance                                                                 |
 | 
				
			||||||
 | 
					| `--detach`        |          |         | Detach buildx server for the monitor (supported only on linux)                                           |
 | 
				
			||||||
 | 
					| `--invoke`        | `string` |         | Launch a monitor with executing specified command                                                        |
 | 
				
			||||||
 | 
					| `--on`            | `string` | `error` | When to launch the monitor ([always, error])                                                             |
 | 
				
			||||||
 | 
					| `--progress`      | `string` | `auto`  | Set type of progress output (`auto`, `plain`, `tty`) for the monitor. Use plain to show container output |
 | 
				
			||||||
 | 
					| `--root`          | `string` |         | Specify root directory of server to connect for the monitor                                              |
 | 
				
			||||||
 | 
					| `--server-config` | `string` |         | Specify buildx server config file for the monitor (used only when launching new server)                  |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										53
									
								
								docs/reference/buildx_debug_build.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								docs/reference/buildx_debug_build.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					# docker buildx debug build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					Start a build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Aliases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`docker buildx debug build`, `docker buildx debug b`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name                                                                                                                                                   | Type          | Default   | Description                                                                                         |
 | 
				
			||||||
 | 
					|:-------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------|:----------|:----------------------------------------------------------------------------------------------------|
 | 
				
			||||||
 | 
					| [`--add-host`](https://docs.docker.com/engine/reference/commandline/build/#add-host)                                                                   | `stringSlice` |           | Add a custom host-to-IP mapping (format: `host:ip`)                                                 |
 | 
				
			||||||
 | 
					| `--allow`                                                                                                                                              | `stringSlice` |           | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`)                      |
 | 
				
			||||||
 | 
					| `--annotation`                                                                                                                                         | `stringArray` |           | Add annotation to the image                                                                         |
 | 
				
			||||||
 | 
					| `--attest`                                                                                                                                             | `stringArray` |           | Attestation parameters (format: `type=sbom,generator=image`)                                        |
 | 
				
			||||||
 | 
					| `--build-arg`                                                                                                                                          | `stringArray` |           | Set build-time variables                                                                            |
 | 
				
			||||||
 | 
					| `--build-context`                                                                                                                                      | `stringArray` |           | Additional build contexts (e.g., name=path)                                                         |
 | 
				
			||||||
 | 
					| `--builder`                                                                                                                                            | `string`      |           | Override the configured builder instance                                                            |
 | 
				
			||||||
 | 
					| `--cache-from`                                                                                                                                         | `stringArray` |           | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`)                       |
 | 
				
			||||||
 | 
					| `--cache-to`                                                                                                                                           | `stringArray` |           | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`)                   |
 | 
				
			||||||
 | 
					| [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#cgroup-parent)                                                         | `string`      |           | Set the parent cgroup for the `RUN` instructions during build                                       |
 | 
				
			||||||
 | 
					| `--detach`                                                                                                                                             |               |           | Detach buildx server (supported only on linux)                                                      |
 | 
				
			||||||
 | 
					| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#file), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#file) | `string`      |           | Name of the Dockerfile (default: `PATH/Dockerfile`)                                                 |
 | 
				
			||||||
 | 
					| `--iidfile`                                                                                                                                            | `string`      |           | Write the image ID to the file                                                                      |
 | 
				
			||||||
 | 
					| `--label`                                                                                                                                              | `stringArray` |           | Set metadata for an image                                                                           |
 | 
				
			||||||
 | 
					| `--load`                                                                                                                                               |               |           | Shorthand for `--output=type=docker`                                                                |
 | 
				
			||||||
 | 
					| `--metadata-file`                                                                                                                                      | `string`      |           | Write build result metadata to the file                                                             |
 | 
				
			||||||
 | 
					| `--network`                                                                                                                                            | `string`      | `default` | Set the networking mode for the `RUN` instructions during build                                     |
 | 
				
			||||||
 | 
					| `--no-cache`                                                                                                                                           |               |           | Do not use cache when building the image                                                            |
 | 
				
			||||||
 | 
					| `--no-cache-filter`                                                                                                                                    | `stringArray` |           | Do not cache specified stages                                                                       |
 | 
				
			||||||
 | 
					| `-o`, `--output`                                                                                                                                       | `stringArray` |           | Output destination (format: `type=local,dest=path`)                                                 |
 | 
				
			||||||
 | 
					| `--platform`                                                                                                                                           | `stringArray` |           | Set target platform for build                                                                       |
 | 
				
			||||||
 | 
					| `--print`                                                                                                                                              | `string`      |           | Print result of information request (e.g., outline, targets)                                        |
 | 
				
			||||||
 | 
					| `--progress`                                                                                                                                           | `string`      | `auto`    | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output            |
 | 
				
			||||||
 | 
					| `--provenance`                                                                                                                                         | `string`      |           | Shorthand for `--attest=type=provenance`                                                            |
 | 
				
			||||||
 | 
					| `--pull`                                                                                                                                               |               |           | Always attempt to pull all referenced images                                                        |
 | 
				
			||||||
 | 
					| `--push`                                                                                                                                               |               |           | Shorthand for `--output=type=registry`                                                              |
 | 
				
			||||||
 | 
					| `-q`, `--quiet`                                                                                                                                        |               |           | Suppress the build output and print image ID on success                                             |
 | 
				
			||||||
 | 
					| `--root`                                                                                                                                               | `string`      |           | Specify root directory of server to connect                                                         |
 | 
				
			||||||
 | 
					| `--sbom`                                                                                                                                               | `string`      |           | Shorthand for `--attest=type=sbom`                                                                  |
 | 
				
			||||||
 | 
					| `--secret`                                                                                                                                             | `stringArray` |           | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`)                           |
 | 
				
			||||||
 | 
					| `--server-config`                                                                                                                                      | `string`      |           | Specify buildx server config file (used only when launching new server)                             |
 | 
				
			||||||
 | 
					| `--shm-size`                                                                                                                                           | `bytes`       | `0`       | Size of `/dev/shm`                                                                                  |
 | 
				
			||||||
 | 
					| `--ssh`                                                                                                                                                | `stringArray` |           | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
 | 
				
			||||||
 | 
					| [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag)    | `stringArray` |           | Name and optionally a tag (format: `name:tag`)                                                      |
 | 
				
			||||||
 | 
					| [`--target`](https://docs.docker.com/engine/reference/commandline/build/#target)                                                                       | `string`      |           | Set the target build stage to build                                                                 |
 | 
				
			||||||
 | 
					| `--ulimit`                                                                                                                                             | `ulimit`      |           | Ulimit options                                                                                      |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,6 +35,9 @@ COMMAND and ARG... will be executed in the container.
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cm *ExecCmd) Exec(ctx context.Context, args []string) error {
 | 
					func (cm *ExecCmd) Exec(ctx context.Context, args []string) error {
 | 
				
			||||||
 | 
						if ref := cm.m.AttachedSessionID(); ref == "" {
 | 
				
			||||||
 | 
							return errors.Errorf("no attaching session")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if len(args) < 2 {
 | 
						if len(args) < 2 {
 | 
				
			||||||
		return errors.Errorf("command must be passed")
 | 
							return errors.Errorf("command must be passed")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	controllerapi "github.com/docker/buildx/controller/pb"
 | 
						controllerapi "github.com/docker/buildx/controller/pb"
 | 
				
			||||||
	"github.com/docker/buildx/monitor/types"
 | 
						"github.com/docker/buildx/monitor/types"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type RollbackCmd struct {
 | 
					type RollbackCmd struct {
 | 
				
			||||||
@@ -37,6 +38,9 @@ COMMAND and ARG... will be executed in the container.
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cm *RollbackCmd) Exec(ctx context.Context, args []string) error {
 | 
					func (cm *RollbackCmd) Exec(ctx context.Context, args []string) error {
 | 
				
			||||||
 | 
						if ref := cm.m.AttachedSessionID(); ref == "" {
 | 
				
			||||||
 | 
							return errors.Errorf("no attaching session")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	cfg := cm.invokeConfig
 | 
						cfg := cm.invokeConfig
 | 
				
			||||||
	if len(args) >= 2 {
 | 
						if len(args) >= 2 {
 | 
				
			||||||
		cmds := args[1:]
 | 
							cmds := args[1:]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user