wrap compose cobra command to set exitcode according to metrics status

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2021-04-15 12:43:18 +02:00
parent 1fce1623ee
commit d8aa00a766
26 changed files with 117 additions and 79 deletions

View File

@ -46,7 +46,7 @@ func buildCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "build [SERVICE...]", Use: "build [SERVICE...]",
Short: "Build or rebuild services", Short: "Build or rebuild services",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
if opts.memory != "" { if opts.memory != "" {
fmt.Println("WARNING --memory is ignored as not supported in buildkit.") fmt.Println("WARNING --memory is ignored as not supported in buildkit.")
} }
@ -57,8 +57,8 @@ func buildCommand(p *projectOptions, backend compose.Service) *cobra.Command {
} }
os.Stdout = devnull os.Stdout = devnull
} }
return runBuild(cmd.Context(), backend, opts, args) return runBuild(ctx, backend, opts, args)
}, }),
} }
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT") cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
cmd.Flags().BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.") cmd.Flags().BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.")

View File

@ -17,12 +17,14 @@
package compose package compose
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/types"
dockercli "github.com/docker/cli/cli"
"github.com/morikuni/aec" "github.com/morikuni/aec"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -34,6 +36,24 @@ import (
"github.com/docker/compose-cli/cli/metrics" "github.com/docker/compose-cli/cli/metrics"
) )
//Command defines a compose CLI command as a func with args
type Command func(context.Context, []string) error
//Adapt a Command func to cobra library
func Adapt(fn Command) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
err := fn(cmd.Context(), args)
var composeErr metrics.ComposeError
if errors.As(err, &composeErr) {
err = dockercli.StatusError{
StatusCode: composeErr.GetMetricsFailureCategory().ExitCode,
Status: err.Error(),
}
}
return err
}
}
// Warning is a global warning to be displayed to user on command failure // Warning is a global warning to be displayed to user on command failure
var Warning string var Warning string
@ -105,8 +125,8 @@ func (o *projectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.Proj
cli.WithName(o.ProjectName))...) cli.WithName(o.ProjectName))...)
} }
// Command returns the compose command with its child commands // RootCommand returns the compose command with its child commands
func Command(contextType string, backend compose.Service) *cobra.Command { func RootCommand(contextType string, backend compose.Service) *cobra.Command {
opts := projectOptions{} opts := projectOptions{}
var ansi string var ansi string
var noAnsi bool var noAnsi bool
@ -120,7 +140,10 @@ func Command(contextType string, backend compose.Service) *cobra.Command {
return cmd.Help() return cmd.Help()
} }
_ = cmd.Help() _ = cmd.Help()
return fmt.Errorf("unknown docker command: %q", "compose "+args[0]) return dockercli.StatusError{
StatusCode: metrics.CommandSyntaxFailure.ExitCode,
Status: fmt.Sprintf("unknown docker command: %q", "compose "+args[0]),
}
}, },
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
parent := cmd.Root() parent := cmd.Root()

View File

@ -58,7 +58,7 @@ func convertCommand(p *projectOptions, backend compose.Service) *cobra.Command {
Aliases: []string{"config"}, Aliases: []string{"config"},
Use: "convert SERVICES", Use: "convert SERVICES",
Short: "Converts the compose file to platform's canonical format", Short: "Converts the compose file to platform's canonical format",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
if opts.quiet { if opts.quiet {
devnull, err := os.Open(os.DevNull) devnull, err := os.Open(os.DevNull)
if err != nil { if err != nil {
@ -79,8 +79,8 @@ func convertCommand(p *projectOptions, backend compose.Service) *cobra.Command {
return runProfiles(opts, args) return runProfiles(opts, args)
} }
return runConvert(cmd.Context(), backend, opts, args) return runConvert(ctx, backend, opts, args)
}, }),
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]") flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")

View File

@ -17,6 +17,7 @@
package compose package compose
import ( import (
"context"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -37,14 +38,14 @@ func createCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create [SERVICE...]", Use: "create [SERVICE...]",
Short: "Creates containers for a service.", Short: "Creates containers for a service.",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
if opts.Build && opts.noBuild { if opts.Build && opts.noBuild {
return fmt.Errorf("--build and --no-build are incompatible") return fmt.Errorf("--build and --no-build are incompatible")
} }
if opts.forceRecreate && opts.noRecreate { if opts.forceRecreate && opts.noRecreate {
return fmt.Errorf("--force-recreate and --no-recreate are incompatible") return fmt.Errorf("--force-recreate and --no-recreate are incompatible")
} }
return runCreateStart(cmd.Context(), backend, upOptions{ return runCreateStart(ctx, backend, upOptions{
composeOptions: &composeOptions{ composeOptions: &composeOptions{
projectOptions: p, projectOptions: p,
Build: opts.Build, Build: opts.Build,
@ -54,7 +55,7 @@ func createCommand(p *projectOptions, backend compose.Service) *cobra.Command {
forceRecreate: opts.forceRecreate, forceRecreate: opts.forceRecreate,
noRecreate: opts.noRecreate, noRecreate: opts.noRecreate,
}, args) }, args)
}, }),
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.") flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.")

View File

@ -45,15 +45,17 @@ func downCommand(p *projectOptions, contextType string, backend compose.Service)
downCmd := &cobra.Command{ downCmd := &cobra.Command{
Use: "down", Use: "down",
Short: "Stop and remove containers, networks", Short: "Stop and remove containers, networks",
RunE: func(cmd *cobra.Command, args []string) error { PreRun: func(cmd *cobra.Command, args []string) {
opts.timeChanged = cmd.Flags().Changed("timeout") opts.timeChanged = cmd.Flags().Changed("timeout")
},
RunE: Adapt(func(ctx context.Context, args []string) error {
if opts.images != "" { if opts.images != "" {
if opts.images != "all" && opts.images != "local" { if opts.images != "all" && opts.images != "local" {
return fmt.Errorf("invalid value for --rmi: %q", opts.images) return fmt.Errorf("invalid value for --rmi: %q", opts.images)
} }
} }
return runDown(cmd.Context(), backend, opts) return runDown(ctx, backend, opts)
}, }),
} }
flags := downCmd.Flags() flags := downCmd.Flags()
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.") flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")

View File

@ -40,9 +40,9 @@ func eventsCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "events [options] [--] [SERVICE...]", Use: "events [options] [--] [SERVICE...]",
Short: "Receive real time events from containers.", Short: "Receive real time events from containers.",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runEvents(cmd.Context(), backend, opts, args) return runEvents(ctx, backend, opts, args)
}, }),
} }
cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects") cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects")

View File

@ -52,13 +52,13 @@ func execCommand(p *projectOptions, backend compose.Service) *cobra.Command {
Use: "exec [options] [-e KEY=VAL...] [--] SERVICE COMMAND [ARGS...]", Use: "exec [options] [-e KEY=VAL...] [--] SERVICE COMMAND [ARGS...]",
Short: "Execute a command in a running container.", Short: "Execute a command in a running container.",
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
if len(args) > 1 { if len(args) > 1 {
opts.command = args[1:] opts.command = args[1:]
} }
opts.service = args[0] opts.service = args[0]
return runExec(cmd.Context(), backend, opts) return runExec(ctx, backend, opts)
}, }),
} }
runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.") runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.")

View File

@ -46,9 +46,9 @@ func imagesCommand(p *projectOptions, backend compose.Service) *cobra.Command {
imgCmd := &cobra.Command{ imgCmd := &cobra.Command{
Use: "images [SERVICE...]", Use: "images [SERVICE...]",
Short: "List images used by the created containers", Short: "List images used by the created containers",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runImages(cmd.Context(), backend, opts, args) return runImages(ctx, backend, opts, args)
}, }),
} }
imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
return imgCmd return imgCmd

View File

@ -36,9 +36,9 @@ func killCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "kill [options] [SERVICE...]", Use: "kill [options] [SERVICE...]",
Short: "Force stop service containers.", Short: "Force stop service containers.",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runKill(cmd.Context(), backend, opts, args) return runKill(ctx, backend, opts, args)
}, }),
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -43,9 +43,9 @@ func listCommand(contextType string, backend compose.Service) *cobra.Command {
lsCmd := &cobra.Command{ lsCmd := &cobra.Command{
Use: "ls", Use: "ls",
Short: "List running compose projects", Short: "List running compose projects",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runList(cmd.Context(), backend, opts) return runList(ctx, backend, opts)
}, }),
} }
lsCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].") lsCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].")
lsCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs.") lsCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs.")

View File

@ -44,9 +44,9 @@ func logsCommand(p *projectOptions, contextType string, backend compose.Service)
logsCmd := &cobra.Command{ logsCmd := &cobra.Command{
Use: "logs [service...]", Use: "logs [service...]",
Short: "View output from containers", Short: "View output from containers",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runLogs(cmd.Context(), backend, opts, args) return runLogs(ctx, backend, opts, args)
}, }),
} }
flags := logsCmd.Flags() flags := logsCmd.Flags()
flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.") flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.")

View File

@ -36,9 +36,9 @@ func pauseCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "pause [SERVICE...]", Use: "pause [SERVICE...]",
Short: "pause services", Short: "pause services",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runPause(cmd.Context(), backend, opts, args) return runPause(ctx, backend, opts, args)
}, }),
} }
return cmd return cmd
} }
@ -68,9 +68,9 @@ func unpauseCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "unpause [SERVICE...]", Use: "unpause [SERVICE...]",
Short: "unpause services", Short: "unpause services",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runUnPause(cmd.Context(), backend, opts, args) return runUnPause(ctx, backend, opts, args)
}, }),
} }
return cmd return cmd
} }

View File

@ -40,13 +40,13 @@ func portCommand(p *projectOptions, backend compose.Service) *cobra.Command {
Use: "port [options] [--] SERVICE PRIVATE_PORT", Use: "port [options] [--] SERVICE PRIVATE_PORT",
Short: "Print the public port for a port binding.", Short: "Print the public port for a port binding.",
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
port, err := strconv.Atoi(args[1]) port, err := strconv.Atoi(args[1])
if err != nil { if err != nil {
return err return err
} }
return runPort(cmd.Context(), backend, opts, args[0], port) return runPort(ctx, backend, opts, args[0], port)
}, }),
} }
cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp") cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp")
cmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if service has multiple replicas") cmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if service has multiple replicas")

View File

@ -46,9 +46,9 @@ func psCommand(p *projectOptions, backend compose.Service) *cobra.Command {
psCmd := &cobra.Command{ psCmd := &cobra.Command{
Use: "ps", Use: "ps",
Short: "List containers", Short: "List containers",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runPs(cmd.Context(), backend, opts) return runPs(ctx, backend, opts)
}, }),
} }
psCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].") psCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].")
psCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") psCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")

View File

@ -46,12 +46,12 @@ func pullCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "pull [SERVICE...]", Use: "pull [SERVICE...]",
Short: "Pull service images", Short: "Pull service images",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
if opts.noParallel { if opts.noParallel {
fmt.Fprint(os.Stderr, aec.Apply("option '--no-parallel' is DEPRECATED and will be ignored.\n", aec.RedF)) fmt.Fprint(os.Stderr, aec.Apply("option '--no-parallel' is DEPRECATED and will be ignored.\n", aec.RedF))
} }
return runPull(cmd.Context(), backend, opts, args) return runPull(ctx, backend, opts, args)
}, }),
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information")

View File

@ -39,9 +39,9 @@ func pushCommand(p *projectOptions, backend compose.Service) *cobra.Command {
pushCmd := &cobra.Command{ pushCmd := &cobra.Command{
Use: "push [SERVICE...]", Use: "push [SERVICE...]",
Short: "Push service images", Short: "Push service images",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runPush(cmd.Context(), backend, opts, args) return runPush(ctx, backend, opts, args)
}, }),
} }
pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures") pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures")

View File

@ -48,9 +48,9 @@ By default, anonymous volumes attached to containers will not be removed. You
can override this with -v. To list all volumes, use "docker volume ls". can override this with -v. To list all volumes, use "docker volume ls".
Any data which is not in a volume will be lost.`, Any data which is not in a volume will be lost.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runRemove(cmd.Context(), backend, opts, args) return runRemove(ctx, backend, opts, args)
}, }),
} }
f := cmd.Flags() f := cmd.Flags()
f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal") f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal")

View File

@ -38,9 +38,9 @@ func restartCommand(p *projectOptions, backend compose.Service) *cobra.Command {
restartCmd := &cobra.Command{ restartCmd := &cobra.Command{
Use: "restart", Use: "restart",
Short: "Restart containers", Short: "Restart containers",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runRestart(cmd.Context(), backend, opts, args) return runRestart(ctx, backend, opts, args)
}, }),
} }
flags := restartCmd.Flags() flags := restartCmd.Flags()
flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds") flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")

View File

@ -109,7 +109,7 @@ func runCommand(p *projectOptions, backend compose.Service) *cobra.Command {
Use: "run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] SERVICE [COMMAND] [ARGS...]", Use: "run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] SERVICE [COMMAND] [ARGS...]",
Short: "Run a one-off command on a service.", Short: "Run a one-off command on a service.",
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
if len(args) > 1 { if len(args) > 1 {
opts.Command = args[1:] opts.Command = args[1:]
} }
@ -117,8 +117,8 @@ func runCommand(p *projectOptions, backend compose.Service) *cobra.Command {
if len(opts.publish) > 0 && opts.servicePorts { if len(opts.publish) > 0 && opts.servicePorts {
return fmt.Errorf("--service-ports and --publish are incompatible") return fmt.Errorf("--service-ports and --publish are incompatible")
} }
return runRun(cmd.Context(), backend, opts) return runRun(ctx, backend, opts)
}, }),
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVarP(&opts.Detach, "detach", "d", false, "Run container in background and print container ID") flags.BoolVarP(&opts.Detach, "detach", "d", false, "Run container in background and print container ID")

View File

@ -36,9 +36,9 @@ func startCommand(p *projectOptions, backend compose.Service) *cobra.Command {
startCmd := &cobra.Command{ startCmd := &cobra.Command{
Use: "start [SERVICE...]", Use: "start [SERVICE...]",
Short: "Start services", Short: "Start services",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runStart(cmd.Context(), backend, opts, args) return runStart(ctx, backend, opts, args)
}, }),
} }
return startCmd return startCmd
} }

View File

@ -39,10 +39,12 @@ func stopCommand(p *projectOptions, backend compose.Service) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "stop [SERVICE...]", Use: "stop [SERVICE...]",
Short: "Stop services", Short: "Stop services",
RunE: func(cmd *cobra.Command, args []string) error { PreRun: func(cmd *cobra.Command, args []string) {
opts.timeChanged = cmd.Flags().Changed("timeout") opts.timeChanged = cmd.Flags().Changed("timeout")
return runStop(cmd.Context(), backend, opts, args)
}, },
RunE: Adapt(func(ctx context.Context, args []string) error {
return runStop(ctx, backend, opts, args)
}),
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds") flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")

View File

@ -41,9 +41,9 @@ func topCommand(p *projectOptions, backend compose.Service) *cobra.Command {
topCmd := &cobra.Command{ topCmd := &cobra.Command{
Use: "top", Use: "top",
Short: "Display the running processes", Short: "Display the running processes",
RunE: func(cmd *cobra.Command, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runTop(cmd.Context(), backend, opts, args) return runTop(ctx, backend, opts, args)
}, }),
} }
return topCmd return topCmd
} }

View File

@ -148,8 +148,10 @@ func upCommand(p *projectOptions, contextType string, backend compose.Service) *
upCmd := &cobra.Command{ upCmd := &cobra.Command{
Use: "up [SERVICE...]", Use: "up [SERVICE...]",
Short: "Create and start containers", Short: "Create and start containers",
RunE: func(cmd *cobra.Command, args []string) error { PreRun: func(cmd *cobra.Command, args []string) {
opts.timeChanged = cmd.Flags().Changed("timeout") opts.timeChanged = cmd.Flags().Changed("timeout")
},
RunE: Adapt(func(ctx context.Context, args []string) error {
switch contextType { switch contextType {
case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType: case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType:
if opts.exitCodeFrom != "" { if opts.exitCodeFrom != "" {
@ -167,11 +169,11 @@ func upCommand(p *projectOptions, contextType string, backend compose.Service) *
if opts.recreateDeps && opts.noRecreate { if opts.recreateDeps && opts.noRecreate {
return fmt.Errorf("--always-recreate-deps and --no-recreate are incompatible") return fmt.Errorf("--always-recreate-deps and --no-recreate are incompatible")
} }
return runCreateStart(cmd.Context(), backend, opts, args) return runCreateStart(ctx, backend, opts, args)
default: default:
return runUp(cmd.Context(), backend, opts, args) return runUp(ctx, backend, opts, args)
} }
}, }),
} }
flags := upCmd.Flags() flags := upCmd.Flags()
flags.StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables") flags.StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")

View File

@ -218,7 +218,7 @@ func main() {
root.AddCommand( root.AddCommand(
run.Command(ctype), run.Command(ctype),
compose.Command(ctype, service.ComposeService()), compose.RootCommand(ctype, service.ComposeService()),
volume.Command(ctype), volume.Command(ctype),
) )
@ -294,7 +294,7 @@ func exit(ctx string, err error, ctype string) {
if errors.Is(err, errdefs.ErrNotImplemented) { if errors.Is(err, errdefs.ErrNotImplemented) {
name := metrics.GetCommand(os.Args[1:]) name := metrics.GetCommand(os.Args[1:])
fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx) fmt.Fprintf(os.Stderr, "RootCommand %q not available in current context (%s)\n", name, ctx)
os.Exit(1) os.Exit(1)
} }
@ -314,7 +314,7 @@ func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string
dockerCommand := string(submatch[1]) dockerCommand := string(submatch[1])
if mobycli.IsDefaultContextCommand(dockerCommand) { if mobycli.IsDefaultContextCommand(dockerCommand) {
fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext) fmt.Fprintf(os.Stderr, "RootCommand %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
metrics.Track(contextType, os.Args[1:], metrics.FailureStatus) metrics.Track(contextType, os.Args[1:], metrics.FailureStatus)
os.Exit(1) os.Exit(1)
} }

View File

@ -35,7 +35,7 @@ const descriptionSourcePath = "docs/reference/"
func generateCliYaml(opts *options) error { func generateCliYaml(opts *options) error {
cmd := &cobra.Command{Use: "docker"} cmd := &cobra.Command{Use: "docker"}
cmd.AddCommand(compose.Command("local", nil)) cmd.AddCommand(compose.RootCommand("local", nil))
disableFlagsInUseLine(cmd) disableFlagsInUseLine(cmd)
source := filepath.Join(opts.source, descriptionSourcePath) source := filepath.Join(opts.source, descriptionSourcePath)
if err := loadLongDescription(cmd, source); err != nil { if err := loadLongDescription(cmd, source); err != nil {

14
main.go
View File

@ -19,14 +19,16 @@ package main
import ( import (
"strings" "strings"
"github.com/spf13/cobra" dockercli "github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli-plugins/plugin" "github.com/docker/cli/cli-plugins/plugin"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
api "github.com/docker/compose-cli/api/compose" api "github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/api/context/store"
"github.com/docker/compose-cli/cli/cmd/compose" "github.com/docker/compose-cli/cli/cmd/compose"
"github.com/docker/compose-cli/cli/metrics"
"github.com/docker/compose-cli/internal" "github.com/docker/compose-cli/internal"
impl "github.com/docker/compose-cli/local/compose" impl "github.com/docker/compose-cli/local/compose"
) )
@ -36,7 +38,7 @@ func main() {
lazyInit := api.ServiceDelegator{ lazyInit := api.ServiceDelegator{
Delegate: api.NoImpl{}, Delegate: api.NoImpl{},
} }
cmd := compose.Command(store.DefaultContextType, &lazyInit) cmd := compose.RootCommand(store.DefaultContextType, &lazyInit)
originalPreRun := cmd.PersistentPreRunE originalPreRun := cmd.PersistentPreRunE
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if err := plugin.PersistentPreRunE(cmd, args); err != nil { if err := plugin.PersistentPreRunE(cmd, args); err != nil {
@ -48,6 +50,12 @@ func main() {
} }
return nil return nil
} }
cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
return dockercli.StatusError{
StatusCode: metrics.CommandSyntaxFailure.ExitCode,
Status: err.Error(),
}
})
return cmd return cmd
}, },
manager.Metadata{ manager.Metadata{