diff --git a/cmd/fluxctl/completion_cmd.go b/cmd/fluxctl/completion_cmd.go new file mode 100644 index 000000000..fd5cf1d86 --- /dev/null +++ b/cmd/fluxctl/completion_cmd.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "io" + "strings" + + "github.com/spf13/cobra" +) + +var ( + completionShells = map[string]func(out io.Writer, cmd *cobra.Command) error{ + "bash": runCompletionBash, + "zsh": runCompletionZsh, + } +) + +func newCompletionCommand() *cobra.Command { + shells := []string{} + for s := range completionShells { + shells = append(shells, s) + } + + return &cobra.Command{ + Use: "completion SHELL", + DisableFlagsInUseLine: true, + Short: "Output shell completion code for the specified shell (bash or zsh)", + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return newUsageError("please specify a shell") + } + + if len(args) > 1 { + return newUsageError(fmt.Sprintf("please specify one of the following shells: %s", strings.Join(shells, " "))) + } + + run, found := completionShells[args[0]] + if !found { + return newUsageError(fmt.Sprintf("unsupported shell type %q.", args[0])) + } + + return run(cmd.OutOrStdout(), cmd.Parent()) + }, + ValidArgs: shells, + } +} + +func runCompletionBash(out io.Writer, fluxctl *cobra.Command) error { + return fluxctl.GenBashCompletion(out) +} + +func runCompletionZsh(out io.Writer, fluxctl *cobra.Command) error { + return fluxctl.GenZshCompletion(out) +} diff --git a/cmd/fluxctl/completion_cmd_test.go b/cmd/fluxctl/completion_cmd_test.go new file mode 100644 index 000000000..cbb142dbf --- /dev/null +++ b/cmd/fluxctl/completion_cmd_test.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "testing" +) + +func TestCompletionCommand_InputFailure(t *testing.T) { + for k, v := range [][]string{ + {}, + {"foo"}, + {"bash", "zsh"}, + } { + t.Run(fmt.Sprintf("%d", k), func(t *testing.T) { + cmd := newCompletionCommand() + cmd.SetArgs(v) + if err := cmd.Execute(); err == nil { + t.Fatalf("Expecting error: command is expecting either bash or zsh") + } + }) + } +} + +func TestCompletionCommand_Success(t *testing.T) { + for k, v := range [][]string{ + {"bash"}, + {"zsh"}, + } { + t.Run(fmt.Sprintf("%d", k), func(t *testing.T) { + parentCmd := newRoot().Command() + cmd := newCompletionCommand() + parentCmd.AddCommand(cmd) + cmd.SetArgs(v) + if err := cmd.Execute(); err != nil { + t.Fatalf("Expecting nil, got error (%s)", err.Error()) + } + }) + } +} diff --git a/cmd/fluxctl/root_cmd.go b/cmd/fluxctl/root_cmd.go index a9b7d8ae3..82892e6ea 100644 --- a/cmd/fluxctl/root_cmd.go +++ b/cmd/fluxctl/root_cmd.go @@ -97,6 +97,7 @@ func (opts *rootOpts) Command() *cobra.Command { newIdentity(opts).Command(), newSync(opts).Command(), newInstall().Command(), + newCompletionCommand(), ) return cmd @@ -105,7 +106,7 @@ func (opts *rootOpts) Command() *cobra.Command { func (opts *rootOpts) PersistentPreRunE(cmd *cobra.Command, _ []string) error { // skip port forward for certain commands switch cmd.Use { - case "version": + case "version", "completion SHELL": fallthrough case "install": return nil