Merge pull request #1773 from ulyssessouza/completion

This commit is contained in:
Nicolas De loof 2021-07-26 08:23:13 +02:00 committed by GitHub
commit 3d83989f5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 91 additions and 18 deletions

View File

@ -68,6 +68,9 @@ var (
"serve": {}, "serve": {},
"version": {}, "version": {},
"backend-metadata": {}, "backend-metadata": {},
// Special hidden commands used by cobra for completion
"__complete": {},
"__completeNoDesc": {},
} }
unknownCommandRegexp = regexp.MustCompile(`unknown docker command: "([^"]*)"`) unknownCommandRegexp = regexp.MustCompile(`unknown docker command: "([^"]*)"`)
) )
@ -167,7 +170,7 @@ func main() {
}) })
// populate the opts with the global flags // populate the opts with the global flags
flags.Parse(os.Args[1:]) //nolint: errcheck flags.Parse(os.Args[1:]) // nolint: errcheck
level, err := logrus.ParseLevel(opts.LogLevel) level, err := logrus.ParseLevel(opts.LogLevel)
if err != nil { if err != nil {
@ -223,18 +226,16 @@ func main() {
volume.Command(ctype), volume.Command(ctype),
) )
if ctype != store.DefaultContextType { // On default context, "compose" is implemented by CLI Plugin
// On default context, "compose" is implemented by CLI Plugin proxy := api.NewServiceProxy().WithService(service.ComposeService())
proxy := api.NewServiceProxy().WithService(service.ComposeService()) command := compose2.RootCommand(ctype, proxy)
command := compose2.RootCommand(ctype, proxy)
if ctype == store.AciContextType { if ctype == store.AciContextType {
customizeCliForACI(command, proxy) customizeCliForACI(command, proxy)
}
root.AddCommand(command)
} }
root.AddCommand(command)
if err = root.ExecuteContext(ctx); err != nil { if err = root.ExecuteContext(ctx); err != nil {
handleError(ctx, err, ctype, currentContext, cc, root) handleError(ctx, err, ctype, currentContext, cc, root)
} }

View File

@ -61,6 +61,7 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runBuild(ctx, backend, opts, args) 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().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.")

48
cmd/compose/completion.go Normal file
View File

@ -0,0 +1,48 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"strings"
"github.com/spf13/cobra"
)
// 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
}
var serviceNames []string
for _, s := range project.ServiceNames() {
if toComplete == "" || strings.HasPrefix(s, toComplete) {
serviceNames = append(serviceNames, s)
}
}
return serviceNames, cobra.ShellCompDirectiveNoFileComp
}
}

View File

@ -41,10 +41,10 @@ import (
"github.com/docker/compose-cli/pkg/compose" "github.com/docker/compose-cli/pkg/compose"
) )
//Command defines a compose CLI command as a func with args // Command defines a compose CLI command as a func with args
type Command func(context.Context, []string) error type Command func(context.Context, []string) error
//Adapt a Command func to cobra library // Adapt a Command func to cobra library
func Adapt(fn Command) func(cmd *cobra.Command, args []string) error { func Adapt(fn Command) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()

View File

@ -86,6 +86,7 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
return runConvert(ctx, backend, opts, args) return runConvert(ctx, backend, opts, args)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
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

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

View File

@ -64,6 +64,7 @@ func createCommand(p *projectOptions, backend api.Service) *cobra.Command {
QuietPull: false, QuietPull: false,
}) })
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
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

@ -57,6 +57,7 @@ func downCommand(p *projectOptions, backend api.Service) *cobra.Command {
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runDown(ctx, backend, opts) return runDown(ctx, backend, opts)
}), }),
ValidArgsFunction: noCompletion(),
} }
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

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

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

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

@ -33,6 +33,7 @@ func killCommand(p *projectOptions, backend api.Service) *cobra.Command {
RunE: p.WithProject(func(ctx context.Context, project *types.Project) error { RunE: p.WithProject(func(ctx context.Context, project *types.Project) error {
return backend.Kill(ctx, project, opts) return backend.Kill(ctx, project, opts)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -46,6 +46,7 @@ func listCommand(contextType string, backend api.Service) *cobra.Command {
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runList(ctx, backend, opts) return runList(ctx, backend, opts)
}), }),
ValidArgsFunction: noCompletion(),
} }
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,11 +44,12 @@ func logsCommand(p *projectOptions, contextType string, backend api.Service) *co
projectOptions: p, projectOptions: p,
} }
logsCmd := &cobra.Command{ logsCmd := &cobra.Command{
Use: "logs [service...]", Use: "logs [SERVICE...]",
Short: "View output from containers", Short: "View output from containers",
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runLogs(ctx, backend, opts, args) return runLogs(ctx, backend, opts, args)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
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

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

View File

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

@ -67,8 +67,8 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
opts := psOptions{ opts := psOptions{
projectOptions: p, projectOptions: p,
} }
cmd := &cobra.Command{ psCmd := &cobra.Command{
Use: "ps [options] [SERVICE...]", Use: "ps [SERVICE...]",
Short: "List containers", Short: "List containers",
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
return opts.parseFilter() return opts.parseFilter()
@ -76,8 +76,9 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runPs(ctx, backend, args, opts) return runPs(ctx, backend, args, opts)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
flags := cmd.Flags() flags := psCmd.Flags()
flags.StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json]") 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.Filter, "filter", "", "Filter services by a property")
flags.StringVar(&opts.Status, "status", "", "Filter services by status") flags.StringVar(&opts.Status, "status", "", "Filter services by status")
@ -85,7 +86,7 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
flags.BoolVar(&opts.Services, "services", false, "Display services") 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.BoolVarP(&opts.All, "all", "a", false, "Show all stopped containers (including those created by the run command)")
flags.Lookup("filter").Hidden = true flags.Lookup("filter").Hidden = true
return cmd return psCmd
} }
func runPs(ctx context.Context, backend api.Service, services []string, opts psOptions) error { func runPs(ctx context.Context, backend api.Service, services []string, opts psOptions) error {

View File

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

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

View File

@ -46,6 +46,7 @@ Any data which is not in a volume will be lost.`,
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runRemove(ctx, backend, opts, args) return runRemove(ctx, backend, opts, args)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
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

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

@ -126,6 +126,7 @@ func runCommand(p *projectOptions, backend api.Service) *cobra.Command {
} }
return runRun(ctx, backend, project, opts) return runRun(ctx, backend, project, opts)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
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

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

View File

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

@ -39,11 +39,12 @@ func topCommand(p *projectOptions, backend api.Service) *cobra.Command {
projectOptions: p, projectOptions: p,
} }
topCmd := &cobra.Command{ topCmd := &cobra.Command{
Use: "top", Use: "top [SERVICES...]",
Short: "Display the running processes", Short: "Display the running processes",
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {
return runTop(ctx, backend, opts, args) return runTop(ctx, backend, opts, args)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
return topCmd return topCmd
} }

View File

@ -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 { RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
return runUp(ctx, backend, create, up, project, services) return runUp(ctx, backend, create, up, project, services)
}), }),
ValidArgsFunction: serviceCompletion(p),
} }
flags := upCmd.Flags() flags := upCmd.Flags()
flags.StringArrayVarP(&up.Environment, "environment", "e", []string{}, "Environment variables") flags.StringArrayVarP(&up.Environment, "environment", "e", []string{}, "Environment variables")