diff --git a/cmd/compose/build.go b/cmd/compose/build.go index bd1a41196..aefe3d0fd 100644 --- a/cmd/compose/build.go +++ b/cmd/compose/build.go @@ -61,6 +61,7 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command { RunE: Adapt(func(ctx context.Context, args []string) error { return runBuild(ctx, backend, opts, args) }), + ValidArgsFunction: serviceCompletion(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.") diff --git a/cmd/compose/completion.go b/cmd/compose/completion.go index 4d6b5b92f..e3bd75ef8 100644 --- a/cmd/compose/completion.go +++ b/cmd/compose/completion.go @@ -82,5 +82,25 @@ PowerShell: } return err }, + Hidden: true, + } +} + +// validArgsFn defines a completion func to be returned to fetch completion options +type validArgsFn func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) + +func noCompletion() validArgsFn { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoFileComp + } +} + +func serviceCompletion(p *projectOptions) validArgsFn { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + project, err := p.toProject(nil) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + return project.ServiceNames(), cobra.ShellCompDirectiveNoFileComp } } diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go index c0404be2a..6d292b81d 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -44,9 +44,6 @@ import ( // Command defines a compose CLI command as a func with args type Command func(context.Context, []string) error -// ValidArgsFn defines a completion func to be returned to fetch completion options -type ValidArgsFn func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) - // 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 { diff --git a/cmd/compose/convert.go b/cmd/compose/convert.go index 8dd606aa0..e7fedd1f6 100644 --- a/cmd/compose/convert.go +++ b/cmd/compose/convert.go @@ -85,6 +85,7 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command { return runConvert(ctx, backend, opts, args) }), + ValidArgsFunction: serviceCompletion(p), } flags := cmd.Flags() flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]") diff --git a/cmd/compose/cp.go b/cmd/compose/cp.go index 7a84209c9..99f0952fd 100644 --- a/cmd/compose/cp.go +++ b/cmd/compose/cp.go @@ -60,6 +60,7 @@ func copyCommand(p *projectOptions, backend api.Service) *cobra.Command { opts.destination = args[1] return runCopy(ctx, backend, opts) }), + ValidArgsFunction: serviceCompletion(p), } flags := copyCmd.Flags() diff --git a/cmd/compose/create.go b/cmd/compose/create.go index 04dd31250..77c3087c4 100644 --- a/cmd/compose/create.go +++ b/cmd/compose/create.go @@ -64,6 +64,7 @@ func createCommand(p *projectOptions, backend api.Service) *cobra.Command { QuietPull: false, }) }), + ValidArgsFunction: serviceCompletion(p), } flags := cmd.Flags() flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.") diff --git a/cmd/compose/down.go b/cmd/compose/down.go index a4ca24a8b..e851fb8c3 100644 --- a/cmd/compose/down.go +++ b/cmd/compose/down.go @@ -57,6 +57,7 @@ func downCommand(p *projectOptions, backend api.Service) *cobra.Command { RunE: Adapt(func(ctx context.Context, args []string) error { return runDown(ctx, backend, opts) }), + ValidArgsFunction: noCompletion(), } flags := downCmd.Flags() flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.") diff --git a/cmd/compose/events.go b/cmd/compose/events.go index dec921e71..6f7368e95 100644 --- a/cmd/compose/events.go +++ b/cmd/compose/events.go @@ -43,6 +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), } cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects") diff --git a/cmd/compose/exec.go b/cmd/compose/exec.go index ce6220f6e..653a44417 100644 --- a/cmd/compose/exec.go +++ b/cmd/compose/exec.go @@ -61,6 +61,7 @@ func execCommand(p *projectOptions, backend api.Service) *cobra.Command { RunE: Adapt(func(ctx context.Context, args []string) error { return runExec(ctx, backend, opts) }), + ValidArgsFunction: serviceCompletion(p), } runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.") diff --git a/cmd/compose/images.go b/cmd/compose/images.go index ea77e9122..4da71f7eb 100644 --- a/cmd/compose/images.go +++ b/cmd/compose/images.go @@ -48,6 +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), } imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") return imgCmd diff --git a/cmd/compose/kill.go b/cmd/compose/kill.go index 977cd88ae..d4b33a115 100644 --- a/cmd/compose/kill.go +++ b/cmd/compose/kill.go @@ -33,6 +33,7 @@ func killCommand(p *projectOptions, backend api.Service) *cobra.Command { RunE: p.WithProject(func(ctx context.Context, project *types.Project) error { return backend.Kill(ctx, project, opts) }), + ValidArgsFunction: serviceCompletion(p), } flags := cmd.Flags() diff --git a/cmd/compose/list.go b/cmd/compose/list.go index 56ba30dee..83b51ceb9 100644 --- a/cmd/compose/list.go +++ b/cmd/compose/list.go @@ -46,6 +46,7 @@ func listCommand(contextType string, backend api.Service) *cobra.Command { RunE: Adapt(func(ctx context.Context, args []string) error { return runList(ctx, backend, opts) }), + ValidArgsFunction: noCompletion(), } lsCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].") lsCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs.") diff --git a/cmd/compose/logs.go b/cmd/compose/logs.go index 44040fa55..88ee5d5ba 100644 --- a/cmd/compose/logs.go +++ b/cmd/compose/logs.go @@ -44,11 +44,12 @@ func logsCommand(p *projectOptions, contextType string, backend api.Service) *co projectOptions: p, } logsCmd := &cobra.Command{ - Use: "logs [service...]", + Use: "logs [SERVICE...]", Short: "View output from containers", RunE: Adapt(func(ctx context.Context, args []string) error { return runLogs(ctx, backend, opts, args) }), + ValidArgsFunction: serviceCompletion(p), } flags := logsCmd.Flags() flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.") diff --git a/cmd/compose/pause.go b/cmd/compose/pause.go index 66ec751e6..52c7fbb6a 100644 --- a/cmd/compose/pause.go +++ b/cmd/compose/pause.go @@ -38,6 +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), } return cmd } @@ -67,6 +68,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), } return cmd } diff --git a/cmd/compose/port.go b/cmd/compose/port.go index e1556d96d..37ce0019b 100644 --- a/cmd/compose/port.go +++ b/cmd/compose/port.go @@ -52,6 +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), } 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") diff --git a/cmd/compose/ps.go b/cmd/compose/ps.go index e5310aad0..70a99789b 100644 --- a/cmd/compose/ps.go +++ b/cmd/compose/ps.go @@ -67,8 +67,8 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command { opts := psOptions{ projectOptions: p, } - cmd := &cobra.Command{ - Use: "ps [options] [SERVICE...]", + psCmd := &cobra.Command{ + Use: "ps [SERVICE...]", Short: "List containers", PreRunE: func(cmd *cobra.Command, args []string) error { return opts.parseFilter() @@ -76,9 +76,9 @@ 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: psCompletion(p), + ValidArgsFunction: serviceCompletion(p), } - flags := cmd.Flags() + flags := psCmd.Flags() flags.StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json]") flags.StringVar(&opts.Filter, "filter", "", "Filter services by a property") flags.StringVar(&opts.Status, "status", "", "Filter services by status") @@ -86,7 +86,7 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command { flags.BoolVar(&opts.Services, "services", false, "Display services") flags.BoolVarP(&opts.All, "all", "a", false, "Show all stopped containers (including those created by the run command)") flags.Lookup("filter").Hidden = true - return cmd + return psCmd } func runPs(ctx context.Context, backend api.Service, services []string, opts psOptions) error { @@ -186,7 +186,7 @@ func filterByStatus(containers []api.ContainerSummary, status string) []api.Cont return filtered } -func psCompletion(p *projectOptions) ValidArgsFn { +func psCompletion(p *projectOptions) validArgsFn { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { project, err := p.toProject(nil) if err != nil { diff --git a/cmd/compose/pull.go b/cmd/compose/pull.go index 6d3417ca2..c7213cc6c 100644 --- a/cmd/compose/pull.go +++ b/cmd/compose/pull.go @@ -54,6 +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), } flags := cmd.Flags() flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information") diff --git a/cmd/compose/push.go b/cmd/compose/push.go index 5632bb649..906578dd3 100644 --- a/cmd/compose/push.go +++ b/cmd/compose/push.go @@ -41,6 +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), } pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures") diff --git a/cmd/compose/remove.go b/cmd/compose/remove.go index a52b4f715..924cdab23 100644 --- a/cmd/compose/remove.go +++ b/cmd/compose/remove.go @@ -46,6 +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), } f := cmd.Flags() f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal") diff --git a/cmd/compose/restart.go b/cmd/compose/restart.go index 891c2f0b9..8a022ed9d 100644 --- a/cmd/compose/restart.go +++ b/cmd/compose/restart.go @@ -40,6 +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), } flags := restartCmd.Flags() flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds") diff --git a/cmd/compose/run.go b/cmd/compose/run.go index 0b79927aa..cdaeeab08 100644 --- a/cmd/compose/run.go +++ b/cmd/compose/run.go @@ -126,6 +126,7 @@ func runCommand(p *projectOptions, backend api.Service) *cobra.Command { } return runRun(ctx, backend, project, opts) }), + ValidArgsFunction: serviceCompletion(p), } flags := cmd.Flags() flags.BoolVarP(&opts.Detach, "detach", "d", false, "Run container in background and print container ID") diff --git a/cmd/compose/start.go b/cmd/compose/start.go index 57e48f811..d3d46245b 100644 --- a/cmd/compose/start.go +++ b/cmd/compose/start.go @@ -37,6 +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), } return startCmd } diff --git a/cmd/compose/stop.go b/cmd/compose/stop.go index d8a08c2f4..81b90ad93 100644 --- a/cmd/compose/stop.go +++ b/cmd/compose/stop.go @@ -44,6 +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), } flags := cmd.Flags() flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds") diff --git a/cmd/compose/top.go b/cmd/compose/top.go index ab81dd64c..441deeaa2 100644 --- a/cmd/compose/top.go +++ b/cmd/compose/top.go @@ -39,11 +39,12 @@ func topCommand(p *projectOptions, backend api.Service) *cobra.Command { projectOptions: p, } topCmd := &cobra.Command{ - Use: "top", + Use: "top [SERVICES...]", Short: "Display the running processes", RunE: Adapt(func(ctx context.Context, args []string) error { return runTop(ctx, backend, opts, args) }), + ValidArgsFunction: serviceCompletion(p), } return topCmd } diff --git a/cmd/compose/up.go b/cmd/compose/up.go index a1e7fcbf9..4cdffa747 100644 --- a/cmd/compose/up.go +++ b/cmd/compose/up.go @@ -120,6 +120,7 @@ func upCommand(p *projectOptions, backend api.Service) *cobra.Command { RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error { return runUp(ctx, backend, create, up, project, services) }), + ValidArgsFunction: serviceCompletion(p), } flags := upCmd.Flags() flags.StringArrayVarP(&up.Environment, "environment", "e", []string{}, "Environment variables")