Skip to content

Commit cbec75e

Browse files
ndeloofthaJeztah
authored andcommitted
Adopt Cobra completion v2 to support completion by CLI plugins
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent 2b4ffb3 commit cbec75e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+538
-61
lines changed

cli-plugins/manager/cobra.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package manager
22

33
import (
4+
"fmt"
5+
"os"
6+
47
"github.com/docker/cli/cli/command"
58
"github.com/spf13/cobra"
69
)
@@ -31,12 +34,13 @@ const (
3134
// AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid
3235
// plugin. The command stubs will have several annotations added, see
3336
// `CommandAnnotationPlugin*`.
34-
func AddPluginCommandStubs(dockerCli command.Cli, cmd *cobra.Command) error {
35-
plugins, err := ListPlugins(dockerCli, cmd)
37+
func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) error {
38+
plugins, err := ListPlugins(dockerCli, rootCmd)
3639
if err != nil {
3740
return err
3841
}
3942
for _, p := range plugins {
43+
p := p
4044
vendor := p.Vendor
4145
if vendor == "" {
4246
vendor = "unknown"
@@ -49,11 +53,41 @@ func AddPluginCommandStubs(dockerCli command.Cli, cmd *cobra.Command) error {
4953
if p.Err != nil {
5054
annotations[CommandAnnotationPluginInvalid] = p.Err.Error()
5155
}
52-
cmd.AddCommand(&cobra.Command{
53-
Use: p.Name,
54-
Short: p.ShortDescription,
55-
Run: func(_ *cobra.Command, _ []string) {},
56-
Annotations: annotations,
56+
rootCmd.AddCommand(&cobra.Command{
57+
Use: p.Name,
58+
Short: p.ShortDescription,
59+
Run: func(_ *cobra.Command, _ []string) {},
60+
Annotations: annotations,
61+
DisableFlagParsing: true,
62+
RunE: func(cmd *cobra.Command, args []string) error {
63+
flags := rootCmd.PersistentFlags()
64+
flags.SetOutput(nil)
65+
err := flags.Parse(args)
66+
if err != nil {
67+
return err
68+
}
69+
if flags.Changed("help") {
70+
cmd.HelpFunc()(rootCmd, args)
71+
return nil
72+
}
73+
return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'", cmd.Name())
74+
},
75+
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
76+
// Delegate completion to plugin
77+
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
78+
cargs = append(cargs, args...)
79+
cargs = append(cargs, toComplete)
80+
os.Args = cargs
81+
runCommand, err := PluginRunCommand(dockerCli, p.Name, cmd)
82+
if err != nil {
83+
return nil, cobra.ShellCompDirectiveError
84+
}
85+
err = runCommand.Run()
86+
if err == nil {
87+
os.Exit(0) // plugin already rendered complete data
88+
}
89+
return nil, cobra.ShellCompDirectiveError
90+
},
5791
})
5892
}
5993
return nil

cli-plugins/plugin/plugin.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
125125
},
126126
TraverseChildren: true,
127127
DisableFlagsInUseLine: true,
128+
CompletionOptions: cobra.CompletionOptions{
129+
DisableDefaultCmd: false,
130+
HiddenDefaultCmd: true,
131+
DisableDescriptions: true,
132+
},
128133
}
129134
opts, flags := cli.SetupPluginRootCommand(cmd)
130135

cli/command/builder/prune.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/docker/cli/cli"
99
"github.com/docker/cli/cli/command"
10+
"github.com/docker/cli/cli/command/completion"
1011
"github.com/docker/cli/opts"
1112
"github.com/docker/docker/api/types"
1213
units "github.com/docker/go-units"
@@ -39,7 +40,8 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
3940
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
4041
return nil
4142
},
42-
Annotations: map[string]string{"version": "1.39"},
43+
Annotations: map[string]string{"version": "1.39"},
44+
ValidArgsFunction: completion.NoComplete,
4345
}
4446

4547
flags := cmd.Flags()

cli/command/checkpoint/list.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/docker/cli/cli"
77
"github.com/docker/cli/cli/command"
8+
"github.com/docker/cli/cli/command/completion"
89
"github.com/docker/cli/cli/command/formatter"
910
"github.com/docker/docker/api/types"
1011
"github.com/spf13/cobra"
@@ -25,6 +26,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
2526
RunE: func(cmd *cobra.Command, args []string) error {
2627
return runList(dockerCli, args[0], opts)
2728
},
29+
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
2830
}
2931

3032
flags := cmd.Flags()

cli/command/completion/functions.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package completion
2+
3+
import (
4+
"os"
5+
6+
"github.com/docker/cli/cli/command"
7+
"github.com/docker/cli/cli/command/formatter"
8+
"github.com/docker/docker/api/types"
9+
"github.com/docker/docker/api/types/filters"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
// ValidArgsFn a function to be used by cobra command as `ValidArgsFunction` to offer command line completion
14+
type ValidArgsFn func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
15+
16+
// ImageNames offers completion for images present within the local store
17+
func ImageNames(dockerCli command.Cli) ValidArgsFn {
18+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
19+
list, err := dockerCli.Client().ImageList(cmd.Context(), types.ImageListOptions{})
20+
if err != nil {
21+
return nil, cobra.ShellCompDirectiveError
22+
}
23+
var names []string
24+
for _, image := range list {
25+
names = append(names, image.RepoTags...)
26+
}
27+
return names, cobra.ShellCompDirectiveNoFileComp
28+
}
29+
}
30+
31+
// ContainerNames offers completion for container names and IDs
32+
// By default, only names are returned.
33+
// Set DOCKER_COMPLETION_SHOW_CONTAINER_IDS=yes to also complete IDs.
34+
func ContainerNames(dockerCli command.Cli, all bool, filters ...func(types.Container) bool) ValidArgsFn {
35+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
36+
list, err := dockerCli.Client().ContainerList(cmd.Context(), types.ContainerListOptions{
37+
All: all,
38+
})
39+
if err != nil {
40+
return nil, cobra.ShellCompDirectiveError
41+
}
42+
43+
showContainerIDs := os.Getenv("DOCKER_COMPLETION_SHOW_CONTAINER_IDS") == "yes"
44+
45+
var names []string
46+
for _, container := range list {
47+
skip := false
48+
for _, fn := range filters {
49+
if !fn(container) {
50+
skip = true
51+
break
52+
}
53+
}
54+
if skip {
55+
continue
56+
}
57+
if showContainerIDs {
58+
names = append(names, container.ID)
59+
}
60+
names = append(names, formatter.StripNamePrefix(container.Names)...)
61+
}
62+
return names, cobra.ShellCompDirectiveNoFileComp
63+
}
64+
}
65+
66+
// VolumeNames offers completion for volumes
67+
func VolumeNames(dockerCli command.Cli) ValidArgsFn {
68+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
69+
list, err := dockerCli.Client().VolumeList(cmd.Context(), filters.Args{})
70+
if err != nil {
71+
return nil, cobra.ShellCompDirectiveError
72+
}
73+
var names []string
74+
for _, volume := range list.Volumes {
75+
names = append(names, volume.Name)
76+
}
77+
return names, cobra.ShellCompDirectiveNoFileComp
78+
}
79+
}
80+
81+
// NetworkNames offers completion for networks
82+
func NetworkNames(dockerCli command.Cli) ValidArgsFn {
83+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
84+
list, err := dockerCli.Client().NetworkList(cmd.Context(), types.NetworkListOptions{})
85+
if err != nil {
86+
return nil, cobra.ShellCompDirectiveError
87+
}
88+
var names []string
89+
for _, network := range list {
90+
names = append(names, network.Name)
91+
}
92+
return names, cobra.ShellCompDirectiveNoFileComp
93+
}
94+
}
95+
96+
// NoComplete is used for commands where there's no relevant completion
97+
func NoComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
98+
return nil, cobra.ShellCompDirectiveNoFileComp
99+
}

cli/command/config/cmd.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package config
22

33
import (
4-
"github.com/spf13/cobra"
5-
64
"github.com/docker/cli/cli"
75
"github.com/docker/cli/cli/command"
6+
"github.com/docker/cli/cli/command/completion"
7+
"github.com/docker/docker/api/types"
8+
"github.com/spf13/cobra"
89
)
910

1011
// NewConfigCommand returns a cobra command for `config` subcommands
@@ -27,3 +28,18 @@ func NewConfigCommand(dockerCli command.Cli) *cobra.Command {
2728
)
2829
return cmd
2930
}
31+
32+
// completeNames offers completion for swarm configs
33+
func completeNames(dockerCli command.Cli) completion.ValidArgsFn {
34+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
35+
list, err := dockerCli.Client().ConfigList(cmd.Context(), types.ConfigListOptions{})
36+
if err != nil {
37+
return nil, cobra.ShellCompDirectiveError
38+
}
39+
var names []string
40+
for _, config := range list {
41+
names = append(names, config.ID)
42+
}
43+
return names, cobra.ShellCompDirectiveNoFileComp
44+
}
45+
}

cli/command/config/inspect.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ func newConfigInspectCommand(dockerCli command.Cli) *cobra.Command {
2929
opts.Names = args
3030
return RunConfigInspect(dockerCli, opts)
3131
},
32+
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
33+
return completeNames(dockerCli)(cmd, args, toComplete)
34+
},
3235
}
3336

3437
cmd.Flags().StringVarP(&opts.Format, "format", "f", "", flagsHelper.InspectFormatHelp)

cli/command/config/ls.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/docker/cli/cli"
88
"github.com/docker/cli/cli/command"
9+
"github.com/docker/cli/cli/command/completion"
910
"github.com/docker/cli/cli/command/formatter"
1011
flagsHelper "github.com/docker/cli/cli/flags"
1112
"github.com/docker/cli/opts"
@@ -32,6 +33,7 @@ func newConfigListCommand(dockerCli command.Cli) *cobra.Command {
3233
RunE: func(cmd *cobra.Command, args []string) error {
3334
return RunConfigList(dockerCli, listOpts)
3435
},
36+
ValidArgsFunction: completion.NoComplete,
3537
}
3638

3739
flags := cmd.Flags()

cli/command/config/remove.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ func newConfigRemoveCommand(dockerCli command.Cli) *cobra.Command {
2828
}
2929
return RunConfigRemove(dockerCli, opts)
3030
},
31+
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
32+
return completeNames(dockerCli)(cmd, args, toComplete)
33+
},
3134
}
3235
}
3336

cli/command/container/attach.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/docker/cli/cli"
99
"github.com/docker/cli/cli/command"
10+
"github.com/docker/cli/cli/command/completion"
1011
"github.com/docker/docker/api/types"
1112
"github.com/docker/docker/api/types/container"
1213
"github.com/docker/docker/client"
@@ -54,6 +55,7 @@ func NewAttachCommand(dockerCli command.Cli) *cobra.Command {
5455
opts.container = args[0]
5556
return runAttach(dockerCli, &opts)
5657
},
58+
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
5759
}
5860

5961
flags := cmd.Flags()

0 commit comments

Comments
 (0)