mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-10 05:27:07 +08:00
Add flags and subcommand
Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
This commit is contained in:
@ -18,6 +18,7 @@ import (
|
|||||||
"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"
|
||||||
|
controllererrors "github.com/docker/buildx/controller/errdefs"
|
||||||
controllerapi "github.com/docker/buildx/controller/pb"
|
controllerapi "github.com/docker/buildx/controller/pb"
|
||||||
"github.com/docker/buildx/monitor"
|
"github.com/docker/buildx/monitor"
|
||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
@ -66,7 +67,8 @@ type buildOptions struct {
|
|||||||
target string
|
target string
|
||||||
ulimits *dockeropts.UlimitOpt
|
ulimits *dockeropts.UlimitOpt
|
||||||
|
|
||||||
invoke string
|
invoke *invokeConfig
|
||||||
|
noBuild bool
|
||||||
|
|
||||||
attests []string
|
attests []string
|
||||||
sbom string
|
sbom string
|
||||||
@ -228,6 +230,7 @@ func runBuild(dockerCli command.Cli, in buildOptions) error {
|
|||||||
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||||
options := buildOptions{}
|
options := buildOptions{}
|
||||||
cFlags := &commonFlags{}
|
cFlags := &commonFlags{}
|
||||||
|
var invokeFlag string
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "build [OPTIONS] PATH | URL | -",
|
Use: "build [OPTIONS] PATH | URL | -",
|
||||||
@ -249,6 +252,14 @@ 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 isExperimental() {
|
if isExperimental() {
|
||||||
|
if invokeFlag != "" {
|
||||||
|
invokeConfig, err := parseInvokeConfig(invokeFlag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
options.invoke = &invokeConfig
|
||||||
|
options.noBuild = invokeFlag == "debug-shell"
|
||||||
|
}
|
||||||
return launchControllerAndRunBuild(dockerCli, options)
|
return launchControllerAndRunBuild(dockerCli, options)
|
||||||
}
|
}
|
||||||
return runBuild(dockerCli, options)
|
return runBuild(dockerCli, options)
|
||||||
@ -323,7 +334,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
flags.StringVar(&options.provenance, "provenance", "", `Shortand for "--attest=type=provenance"`)
|
flags.StringVar(&options.provenance, "provenance", "", `Shortand for "--attest=type=provenance"`)
|
||||||
|
|
||||||
if isExperimental() {
|
if isExperimental() {
|
||||||
flags.StringVar(&options.invoke, "invoke", "", "Invoke a command after the build [experimental]")
|
flags.StringVar(&invokeFlag, "invoke", "", "Invoke a command after the build [experimental]")
|
||||||
flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect [experimental]")
|
flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect [experimental]")
|
||||||
flags.BoolVar(&options.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server (supported only on linux) [experimental]")
|
flags.BoolVar(&options.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server (supported only on linux) [experimental]")
|
||||||
flags.StringVar(&options.ServerConfig, "server-config", "", "Specify buildx server config file (used only when launching new server) [experimental]")
|
flags.StringVar(&options.ServerConfig, "server-config", "", "Specify buildx server config file (used only when launching new server) [experimental]")
|
||||||
@ -485,18 +496,10 @@ func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
|
|||||||
func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) error {
|
func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
if options.invoke != "" && (options.dockerfileName == "-" || options.contextPath == "-") {
|
if options.invoke != nil && (options.dockerfileName == "-" || options.contextPath == "-") {
|
||||||
// stdin must be usable for monitor
|
// stdin must be usable for monitor
|
||||||
return errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
|
return errors.Errorf("Dockerfile or context from stdin is not supported with invoke")
|
||||||
}
|
}
|
||||||
var invokeConfig controllerapi.InvokeConfig
|
|
||||||
if inv := options.invoke; inv != "" {
|
|
||||||
var err error
|
|
||||||
invokeConfig, err = parseInvokeConfig(inv)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := controller.NewController(ctx, options.ControlOptions, dockerCli)
|
c, err := controller.NewController(ctx, options.ControlOptions, dockerCli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -508,15 +511,7 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
f := ioset.NewSingleForwarder()
|
// Start build
|
||||||
pr, pw := io.Pipe()
|
|
||||||
f.SetWriter(pw, func() io.WriteCloser {
|
|
||||||
pw.Close() // propagate EOF
|
|
||||||
logrus.Debug("propagating stdin close")
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
f.SetReader(os.Stdin)
|
|
||||||
|
|
||||||
opts, err := options.toControllerOptions()
|
opts, err := options.toControllerOptions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -526,14 +521,6 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid leaving a stale file if we eventually fail
|
|
||||||
if options.imageIDFile != "" {
|
|
||||||
if err := os.Remove(options.imageIDFile); err != nil && !os.IsNotExist(err) {
|
|
||||||
return errors.Wrap(err, "removing image ID file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start build
|
|
||||||
// NOTE: buildx server has the current working directory different from the client
|
// NOTE: buildx server has the current working directory different from the client
|
||||||
// so we need to resolve paths to abosolute ones in the client.
|
// so we need to resolve paths to abosolute ones in the client.
|
||||||
optsP, err := resolvePaths(&opts)
|
optsP, err := resolvePaths(&opts)
|
||||||
@ -541,30 +528,60 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
opts = *optsP
|
opts = *optsP
|
||||||
ref, resp, err := c.Build(ctx, opts, pr, os.Stdout, os.Stderr, progress)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to build") // TODO: allow invoke even on error
|
|
||||||
}
|
|
||||||
if err := pw.Close(); err != nil {
|
|
||||||
logrus.Debug("failed to close stdin pipe writer")
|
|
||||||
}
|
|
||||||
if err := pr.Close(); err != nil {
|
|
||||||
logrus.Debug("failed to close stdin pipe reader")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.quiet {
|
var ref string
|
||||||
fmt.Println(resp.ExporterResponse[exptypes.ExporterImageDigestKey])
|
var retErr error
|
||||||
}
|
f := ioset.NewSingleForwarder()
|
||||||
if options.imageIDFile != "" {
|
f.SetReader(os.Stdin)
|
||||||
dgst := resp.ExporterResponse[exptypes.ExporterImageDigestKey]
|
if !options.noBuild {
|
||||||
if v, ok := resp.ExporterResponse[exptypes.ExporterImageConfigDigestKey]; ok {
|
pr, pw := io.Pipe()
|
||||||
dgst = v
|
f.SetWriter(pw, func() io.WriteCloser {
|
||||||
|
pw.Close() // propagate EOF
|
||||||
|
logrus.Debug("propagating stdin close")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Avoid leaving a stale file if we eventually fail
|
||||||
|
if options.imageIDFile != "" {
|
||||||
|
if err := os.Remove(options.imageIDFile); err != nil && !os.IsNotExist(err) {
|
||||||
|
return errors.Wrap(err, "removing image ID file")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return os.WriteFile(options.imageIDFile, []byte(dgst), 0644)
|
|
||||||
|
var resp *client.SolveResponse
|
||||||
|
ref, resp, err = c.Build(ctx, opts, pr, os.Stdout, os.Stderr, progress)
|
||||||
|
if err != nil {
|
||||||
|
var be *controllererrors.BuildError
|
||||||
|
if errors.As(err, &be) {
|
||||||
|
ref = be.Ref
|
||||||
|
retErr = err
|
||||||
|
// We can proceed to monitor
|
||||||
|
} else {
|
||||||
|
return errors.Wrapf(err, "failed to build")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := pw.Close(); err != nil {
|
||||||
|
logrus.Debug("failed to close stdin pipe writer")
|
||||||
|
}
|
||||||
|
if err := pr.Close(); err != nil {
|
||||||
|
logrus.Debug("failed to close stdin pipe reader")
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.quiet {
|
||||||
|
fmt.Println(resp.ExporterResponse[exptypes.ExporterImageDigestKey])
|
||||||
|
}
|
||||||
|
if options.imageIDFile != "" {
|
||||||
|
dgst := resp.ExporterResponse[exptypes.ExporterImageDigestKey]
|
||||||
|
if v, ok := resp.ExporterResponse[exptypes.ExporterImageConfigDigestKey]; ok {
|
||||||
|
dgst = v
|
||||||
|
}
|
||||||
|
return os.WriteFile(options.imageIDFile, []byte(dgst), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// post-build operations
|
// post-build operations
|
||||||
if options.invoke != "" {
|
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
|
||||||
@ -577,7 +594,7 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
|
|||||||
}
|
}
|
||||||
return errors.Errorf("failed to configure terminal: %v", err)
|
return errors.Errorf("failed to configure terminal: %v", err)
|
||||||
}
|
}
|
||||||
err = monitor.RunMonitor(ctx, ref, opts, invokeConfig, c, options.progress, pr2, os.Stdout, os.Stderr)
|
err = monitor.RunMonitor(ctx, ref, &opts, options.invoke.InvokeConfig, c, progress, pr2, os.Stdout, os.Stderr)
|
||||||
con.Reset()
|
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")
|
||||||
@ -593,9 +610,33 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseInvokeConfig(invoke string) (cfg controllerapi.InvokeConfig, err error) {
|
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.Tty = true
|
||||||
if invoke == "default" {
|
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"}
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
63
commands/debug-shell.go
Normal file
63
commands/debug-shell.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
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/cli/cli/command"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func debugShellCmd(dockerCli command.Cli) *cobra.Command {
|
||||||
|
var options control.ControlOptions
|
||||||
|
var progress string
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "debug-shell",
|
||||||
|
Short: "Start a monitor",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
c, err := controller.NewController(ctx, options, dockerCli)
|
||||||
|
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, progress, os.Stdin, os.Stdout, os.Stderr)
|
||||||
|
con.Reset()
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect [experimental]")
|
||||||
|
flags.BoolVar(&options.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server (supported only on linux) [experimental]")
|
||||||
|
flags.StringVar(&options.ServerConfig, "server-config", "", "Specify buildx server config file (used only when launching new server) [experimental]")
|
||||||
|
flags.StringVar(&progress, "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),
|
||||||
|
)
|
||||||
|
}
|
@ -89,6 +89,7 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
|
|||||||
)
|
)
|
||||||
if isExperimental() {
|
if isExperimental() {
|
||||||
remote.AddControllerCommands(cmd, dockerCli)
|
remote.AddControllerCommands(cmd, dockerCli)
|
||||||
|
addDebugShellCommand(cmd, dockerCli)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ Extended build capabilities with BuildKit
|
|||||||
| [`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 |
|
||||||
| [`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 |
|
||||||
|
18
docs/reference/buildx_debug-shell.md
Normal file
18
docs/reference/buildx_debug-shell.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# 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) [experimental] |
|
||||||
|
| `--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 [experimental] |
|
||||||
|
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) [experimental] |
|
||||||
|
|
||||||
|
|
||||||
|
<!---MARKER_GEN_END-->
|
||||||
|
|
Reference in New Issue
Block a user