Răsfoiți Sursa

cli: add shell completion function (#9269)

Integrates PR #9462 with additional fixes/changes.

Additional changes will be required to utilize this.

Co-authored-by: Nicolas De Loof <[email protected]>
Signed-off-by: Ulysses Souza <[email protected]>
Ulysses Souza 3 ani în urmă
părinte
comite
140dc519d3

+ 11 - 0
cmd/compatibility/convert.go

@@ -23,6 +23,13 @@ import (
 	"github.com/docker/compose/v2/cmd/compose"
 )
 
+func getCompletionCommands() []string {
+	return []string{
+		"__complete",
+		"__completeNoDesc",
+	}
+}
+
 func getBoolFlags() []string {
 	return []string{
 		"--debug", "-D",
@@ -50,6 +57,10 @@ func Convert(args []string) []string {
 	l := len(args)
 	for i := 0; i < l; i++ {
 		arg := args[i]
+		if contains(getCompletionCommands(), arg) {
+			command = append([]string{arg}, command...)
+			continue
+		}
 		if len(arg) > 0 && arg[0] != '-' {
 			// not a top-level flag anymore, keep the rest of the command unmodified
 			if arg == compose.PluginName {

+ 1 - 1
cmd/compose/build.go

@@ -102,7 +102,7 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
 			}
 			return runBuild(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	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.")

+ 21 - 2
cmd/compose/completion.go

@@ -19,6 +19,7 @@ package compose
 import (
 	"strings"
 
+	"github.com/docker/compose/v2/pkg/api"
 	"github.com/spf13/cobra"
 )
 
@@ -27,11 +28,11 @@ type validArgsFn func(cmd *cobra.Command, args []string, toComplete string) ([]s
 
 func noCompletion() validArgsFn {
 	return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-		return nil, cobra.ShellCompDirectiveNoFileComp
+		return []string{}, cobra.ShellCompDirectiveNoSpace
 	}
 }
 
-func serviceCompletion(p *projectOptions) validArgsFn {
+func completeServiceNames(p *projectOptions) validArgsFn {
 	return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		project, err := p.toProject(nil)
 		if err != nil {
@@ -46,3 +47,21 @@ func serviceCompletion(p *projectOptions) validArgsFn {
 		return serviceNames, cobra.ShellCompDirectiveNoFileComp
 	}
 }
+
+func completeProjectNames(backend api.Service) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+	return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+		list, err := backend.List(cmd.Context(), api.ListOptions{
+			All: true,
+		})
+		if err != nil {
+			return nil, cobra.ShellCompDirectiveError
+		}
+		var values []string
+		for _, stack := range list {
+			if strings.HasPrefix(stack.Name, toComplete) {
+				values = append(values, stack.Name)
+			}
+		}
+		return values, cobra.ShellCompDirectiveNoFileComp
+	}
+}

+ 11 - 0
cmd/compose/compose.go

@@ -358,6 +358,17 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
 	)
 	c.Flags().SetInterspersed(false)
 	opts.addProjectFlags(c.Flags())
+	c.RegisterFlagCompletionFunc( //nolint:errcheck
+		"project-name",
+		completeProjectNames(backend),
+	)
+	c.RegisterFlagCompletionFunc( //nolint:errcheck
+		"file",
+		func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+			return []string{"yaml", "yml"}, cobra.ShellCompDirectiveFilterFileExt
+		},
+	)
+
 	c.Flags().StringVar(&ansi, "ansi", "auto", `Control when to print ANSI control characters ("never"|"always"|"auto")`)
 	c.Flags().BoolVarP(&version, "version", "v", false, "Show the Docker Compose version information")
 	c.Flags().MarkHidden("version") //nolint:errcheck

+ 1 - 1
cmd/compose/convert.go

@@ -93,7 +93,7 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
 
 			return runConvert(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := cmd.Flags()
 	flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")

+ 1 - 1
cmd/compose/cp.go

@@ -60,7 +60,7 @@ func copyCommand(p *projectOptions, backend api.Service) *cobra.Command {
 			opts.destination = args[1]
 			return runCopy(ctx, backend, opts)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 
 	flags := copyCmd.Flags()

+ 1 - 1
cmd/compose/create.go

@@ -70,7 +70,7 @@ func createCommand(p *projectOptions, backend api.Service) *cobra.Command {
 				QuietPull:            false,
 			})
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := cmd.Flags()
 	flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.")

+ 1 - 1
cmd/compose/events.go

@@ -43,7 +43,7 @@ func eventsCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runEvents(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 
 	cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects")

+ 1 - 1
cmd/compose/exec.go

@@ -61,7 +61,7 @@ func execCommand(p *projectOptions, dockerCli command.Cli, backend api.Service)
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runExec(ctx, backend, opts)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 
 	runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.")

+ 1 - 1
cmd/compose/images.go

@@ -48,7 +48,7 @@ func imagesCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runImages(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
 	return imgCmd

+ 1 - 1
cmd/compose/kill.go

@@ -42,7 +42,7 @@ func killCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runKill(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 
 	flags := cmd.Flags()

+ 1 - 1
cmd/compose/logs.go

@@ -49,7 +49,7 @@ func logsCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runLogs(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := logsCmd.Flags()
 	flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.")

+ 2 - 2
cmd/compose/pause.go

@@ -38,7 +38,7 @@ func pauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runPause(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	return cmd
 }
@@ -69,7 +69,7 @@ func unpauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runUnPause(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	return cmd
 }

+ 1 - 1
cmd/compose/port.go

@@ -52,7 +52,7 @@ func portCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runPort(ctx, backend, opts, args[0])
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	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")

+ 1 - 1
cmd/compose/ps.go

@@ -78,7 +78,7 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runPs(ctx, backend, args, opts)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := psCmd.Flags()
 	flags.StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json]")

+ 1 - 1
cmd/compose/pull.go

@@ -54,7 +54,7 @@ func pullCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runPull(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := cmd.Flags()
 	flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information")

+ 1 - 1
cmd/compose/push.go

@@ -41,7 +41,7 @@ func pushCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runPush(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures")
 

+ 1 - 1
cmd/compose/remove.go

@@ -46,7 +46,7 @@ Any data which is not in a volume will be lost.`,
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runRemove(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	f := cmd.Flags()
 	f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal")

+ 1 - 1
cmd/compose/restart.go

@@ -40,7 +40,7 @@ func restartCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runRestart(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := restartCmd.Flags()
 	flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")

+ 1 - 1
cmd/compose/run.go

@@ -143,7 +143,7 @@ func runCommand(p *projectOptions, dockerCli command.Cli, backend api.Service) *
 			opts.ignoreOrphans = strings.ToLower(ignore) == "true"
 			return runRun(ctx, backend, project, opts)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := cmd.Flags()
 	flags.BoolVarP(&opts.Detach, "detach", "d", false, "Run container in background and print container ID")

+ 1 - 1
cmd/compose/start.go

@@ -37,7 +37,7 @@ func startCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runStart(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	return startCmd
 }

+ 1 - 1
cmd/compose/stop.go

@@ -44,7 +44,7 @@ func stopCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runStop(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := cmd.Flags()
 	flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")

+ 1 - 1
cmd/compose/top.go

@@ -44,7 +44,7 @@ func topCommand(p *projectOptions, backend api.Service) *cobra.Command {
 		RunE: Adapt(func(ctx context.Context, args []string) error {
 			return runTop(ctx, backend, opts, args)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	return topCmd
 }

+ 1 - 1
cmd/compose/up.go

@@ -109,7 +109,7 @@ func upCommand(p *projectOptions, backend api.Service) *cobra.Command {
 			}
 			return runUp(ctx, backend, create, up, project, services)
 		}),
-		ValidArgsFunction: serviceCompletion(p),
+		ValidArgsFunction: completeServiceNames(p),
 	}
 	flags := upCmd.Flags()
 	flags.BoolVarP(&up.Detach, "detach", "d", false, "Detached mode: Run containers in the background")

+ 2 - 3
cmd/main.go

@@ -34,14 +34,13 @@ import (
 
 func pluginMain() {
 	plugin.Run(func(dockerCli command.Cli) *cobra.Command {
-		lazyInit := api.NewServiceProxy()
-		cmd := commands.RootCommand(dockerCli, lazyInit)
+		serviceProxy := api.NewServiceProxy().WithService(compose.NewComposeService(dockerCli))
+		cmd := commands.RootCommand(dockerCli, serviceProxy)
 		originalPreRun := cmd.PersistentPreRunE
 		cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
 			if err := plugin.PersistentPreRunE(cmd, args); err != nil {
 				return err
 			}
-			lazyInit.WithService(compose.NewComposeService(dockerCli))
 			if originalPreRun != nil {
 				return originalPreRun(cmd, args)
 			}