diff --git a/bake/bake.go b/bake/bake.go index aea0832b..e98201e0 100644 --- a/bake/bake.go +++ b/bake/bake.go @@ -85,6 +85,21 @@ func ReadLocalFiles(names []string) ([]File, error) { return out, nil } +func ListTargets(files []File) ([]string, error) { + c, err := ParseFiles(files, nil) + if err != nil { + return nil, err + } + var targets []string + for _, g := range c.Groups { + targets = append(targets, g.Name) + } + for _, t := range c.Targets { + targets = append(targets, t.Name) + } + return dedupSlice(targets), nil +} + func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string) (map[string]*Target, map[string]*Group, error) { c, err := ParseFiles(files, defaults) if err != nil { diff --git a/commands/bake.go b/commands/bake.go index 7e31c4fd..eadddd7d 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -11,6 +11,7 @@ import ( "github.com/docker/buildx/build" "github.com/docker/buildx/builder" "github.com/docker/buildx/util/buildflags" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/confutil" "github.com/docker/buildx/util/dockerutil" "github.com/docker/buildx/util/progress" @@ -229,6 +230,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { // Other common flags (noCache, pull and progress) are processed in runBake function. return runBake(dockerCli, args, options, cFlags) }, + ValidArgsFunction: completion.BakeTargets(options.files), } flags := cmd.Flags() diff --git a/commands/build.go b/commands/build.go index 086dab14..b18a17cb 100644 --- a/commands/build.go +++ b/commands/build.go @@ -416,6 +416,9 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { } return runBuild(dockerCli, options) }, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveFilterDirs + }, } var platformsDefault []string diff --git a/commands/create.go b/commands/create.go index e1def29a..522a1432 100644 --- a/commands/create.go +++ b/commands/create.go @@ -18,6 +18,7 @@ import ( "github.com/docker/buildx/store" "github.com/docker/buildx/store/storeutil" "github.com/docker/buildx/util/cobrautil" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/confutil" "github.com/docker/buildx/util/dockerutil" "github.com/docker/cli/cli" @@ -327,6 +328,7 @@ func createCmd(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runCreate(dockerCli, options, args) }, + ValidArgsFunction: completion.Disable, } flags := cmd.Flags() diff --git a/commands/diskusage.go b/commands/diskusage.go index d4bd7dda..87ad8f8d 100644 --- a/commands/diskusage.go +++ b/commands/diskusage.go @@ -9,6 +9,7 @@ import ( "time" "github.com/docker/buildx/builder" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/opts" @@ -115,6 +116,7 @@ func duCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { options.builder = rootOpts.builder return runDiskUsage(dockerCli, options) }, + ValidArgsFunction: completion.Disable, } flags := cmd.Flags() diff --git a/commands/imagetools/create.go b/commands/imagetools/create.go index 84c07aaf..15ecd889 100644 --- a/commands/imagetools/create.go +++ b/commands/imagetools/create.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/docker/buildx/builder" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/imagetools" "github.com/docker/buildx/util/progress" "github.com/docker/cli/cli/command" @@ -273,6 +274,7 @@ func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command { options.builder = *opts.Builder return runCreate(dockerCli, options, args) }, + ValidArgsFunction: completion.Disable, } flags := cmd.Flags() diff --git a/commands/imagetools/inspect.go b/commands/imagetools/inspect.go index 68fed667..ad1e6438 100644 --- a/commands/imagetools/inspect.go +++ b/commands/imagetools/inspect.go @@ -2,6 +2,7 @@ package commands import ( "github.com/docker/buildx/builder" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/imagetools" "github.com/docker/cli-docs-tool/annotation" "github.com/docker/cli/cli" @@ -52,6 +53,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command { options.builder = *rootOpts.Builder return runInspect(dockerCli, options, args[0]) }, + ValidArgsFunction: completion.Disable, } flags := cmd.Flags() diff --git a/commands/imagetools/root.go b/commands/imagetools/root.go index 6c6ede2f..f5ec0160 100644 --- a/commands/imagetools/root.go +++ b/commands/imagetools/root.go @@ -1,6 +1,7 @@ package commands import ( + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/cli/cli/command" "github.com/spf13/cobra" ) @@ -11,8 +12,9 @@ type RootOptions struct { func RootCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command { cmd := &cobra.Command{ - Use: "imagetools", - Short: "Commands to work on images in registry", + Use: "imagetools", + Short: "Commands to work on images in registry", + ValidArgsFunction: completion.Disable, } cmd.AddCommand( diff --git a/commands/inspect.go b/commands/inspect.go index fc4530b7..5d222237 100644 --- a/commands/inspect.go +++ b/commands/inspect.go @@ -9,6 +9,7 @@ import ( "time" "github.com/docker/buildx/builder" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/platformutil" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -112,6 +113,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { } return runInspect(dockerCli, options) }, + ValidArgsFunction: completion.BuilderNames(dockerCli), } flags := cmd.Flags() diff --git a/commands/install.go b/commands/install.go index e760a749..9c400dfe 100644 --- a/commands/install.go +++ b/commands/install.go @@ -4,6 +4,7 @@ import ( "os" "github.com/docker/buildx/util/cobrautil" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/config" @@ -46,7 +47,8 @@ func installCmd(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runInstall(dockerCli, options) }, - Hidden: true, + Hidden: true, + ValidArgsFunction: completion.Disable, } // hide builder persistent flag for this command diff --git a/commands/ls.go b/commands/ls.go index 428de1b6..66eae51e 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -11,6 +11,7 @@ import ( "github.com/docker/buildx/builder" "github.com/docker/buildx/store/storeutil" "github.com/docker/buildx/util/cobrautil" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/platformutil" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -126,6 +127,7 @@ func lsCmd(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runLs(dockerCli, options) }, + ValidArgsFunction: completion.Disable, } // hide builder persistent flag for this command diff --git a/commands/prune.go b/commands/prune.go index 653ffd84..67df4b2a 100644 --- a/commands/prune.go +++ b/commands/prune.go @@ -8,6 +8,7 @@ import ( "time" "github.com/docker/buildx/builder" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/opts" @@ -139,6 +140,7 @@ func pruneCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { options.builder = rootOpts.builder return runPrune(dockerCli, options) }, + ValidArgsFunction: completion.Disable, } flags := cmd.Flags() diff --git a/commands/rm.go b/commands/rm.go index fcd4b5e9..9f30d6b5 100644 --- a/commands/rm.go +++ b/commands/rm.go @@ -8,6 +8,7 @@ import ( "github.com/docker/buildx/builder" "github.com/docker/buildx/store" "github.com/docker/buildx/store/storeutil" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/moby/buildkit/util/appcontext" @@ -92,6 +93,7 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { } return runRm(dockerCli, options) }, + ValidArgsFunction: completion.BuilderNames(dockerCli), } flags := cmd.Flags() diff --git a/commands/root.go b/commands/root.go index f0cbb06d..4dd5fc8a 100644 --- a/commands/root.go +++ b/commands/root.go @@ -5,6 +5,7 @@ import ( imagetoolscmd "github.com/docker/buildx/commands/imagetools" "github.com/docker/buildx/controller/remote" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/logutil" "github.com/docker/cli-docs-tool/annotation" "github.com/docker/cli/cli" @@ -23,6 +24,9 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman Annotations: map[string]string{ annotation.CodeDelimiter: `"`, }, + CompletionOptions: cobra.CompletionOptions{ + HiddenDefaultCmd: true, + }, } if isPlugin { cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { @@ -91,6 +95,11 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) { remote.AddControllerCommands(cmd, dockerCli) addDebugShellCommand(cmd, dockerCli) } + + cmd.RegisterFlagCompletionFunc( //nolint:errcheck + "builder", + completion.BuilderNames(dockerCli), + ) } func rootFlags(options *rootOptions, flags *pflag.FlagSet) { diff --git a/commands/stop.go b/commands/stop.go index cebf1c2a..2af78c89 100644 --- a/commands/stop.go +++ b/commands/stop.go @@ -4,6 +4,7 @@ import ( "context" "github.com/docker/buildx/builder" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/moby/buildkit/util/appcontext" @@ -46,6 +47,7 @@ func stopCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { } return runStop(dockerCli, options) }, + ValidArgsFunction: completion.BuilderNames(dockerCli), } return cmd diff --git a/commands/uninstall.go b/commands/uninstall.go index f5b000eb..6bc6bf33 100644 --- a/commands/uninstall.go +++ b/commands/uninstall.go @@ -4,6 +4,7 @@ import ( "os" "github.com/docker/buildx/util/cobrautil" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/config" @@ -52,7 +53,8 @@ func uninstallCmd(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runUninstall(dockerCli, options) }, - Hidden: true, + Hidden: true, + ValidArgsFunction: completion.Disable, } // hide builder persistent flag for this command diff --git a/commands/use.go b/commands/use.go index 7e52ce85..cc5cfe0e 100644 --- a/commands/use.go +++ b/commands/use.go @@ -4,6 +4,7 @@ import ( "os" "github.com/docker/buildx/store/storeutil" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/util/dockerutil" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -78,6 +79,7 @@ func useCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { } return runUse(dockerCli, options) }, + ValidArgsFunction: completion.BuilderNames(dockerCli), } flags := cmd.Flags() diff --git a/commands/version.go b/commands/version.go index 91cfe136..b65cc4db 100644 --- a/commands/version.go +++ b/commands/version.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/docker/buildx/util/cobrautil" + "github.com/docker/buildx/util/cobrautil/completion" "github.com/docker/buildx/version" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -23,6 +24,7 @@ func versionCmd(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runVersion(dockerCli) }, + ValidArgsFunction: completion.Disable, } // hide builder persistent flag for this command diff --git a/util/cobrautil/completion/completion.go b/util/cobrautil/completion/completion.go new file mode 100644 index 00000000..928be174 --- /dev/null +++ b/util/cobrautil/completion/completion.go @@ -0,0 +1,62 @@ +package completion + +import ( + "strings" + + "github.com/docker/buildx/bake" + "github.com/docker/buildx/builder" + "github.com/docker/buildx/store/storeutil" + "github.com/docker/cli/cli/command" + "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 Disable(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return nil, cobra.ShellCompDirectiveNoSpace +} + +func BakeTargets(files []string) ValidArgsFn { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + f, err := bake.ReadLocalFiles(files) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + tgts, err := bake.ListTargets(f) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var filtered []string + if toComplete == "" { + return tgts, cobra.ShellCompDirectiveNoFileComp + } + for _, tgt := range tgts { + if strings.HasPrefix(tgt, toComplete) { + filtered = append(filtered, tgt) + } + } + return filtered, cobra.ShellCompDirectiveNoFileComp + } +} + +func BuilderNames(dockerCli command.Cli) ValidArgsFn { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + txn, release, err := storeutil.GetStore(dockerCli) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + defer release() + builders, err := builder.GetBuilders(dockerCli, txn) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var filtered []string + for _, b := range builders { + if toComplete == "" || strings.HasPrefix(b.Name, toComplete) { + filtered = append(filtered, b.Name) + } + } + return filtered, cobra.ShellCompDirectiveNoFileComp + } +}