Skip to content

Commit 890302a

Browse files
authored
Support usage as plugin for tools like kubectl (#2018)
In this case the executable is `kubectl-plugin`, but we run it as: kubectl plugin And the help text should reflect the actual usage of the command. To create a plugin, add the cobra.CommandDisplayNameAnnotation: rootCmd := &cobra.Command{ Use: "plugin", Annotations: map[string]string{ cobra.CommandDisplayNameAnnotation: "kubectl plugin", } } Internally this change modifies CommandPath() for the root command to return the command display name instead of the command name. This is used for error messages, help text generation, and completions. CommandPath() is expected to have spaces and code using it already handle spaces (e.g replacing with _), so hopefully this does not break anything. Fixes: #2017 Signed-off-by: Nir Soffer <nsoffer@redhat.com>
1 parent 48cea5c commit 890302a

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

command.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import (
3030
flag "github.com/spf13/pflag"
3131
)
3232

33-
const FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
33+
const (
34+
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
35+
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
36+
)
3437

3538
// FParseErrWhitelist configures Flag parse errors to be ignored
3639
type FParseErrWhitelist flag.ParseErrorsWhitelist
@@ -99,7 +102,7 @@ type Command struct {
99102
Deprecated string
100103

101104
// Annotations are key/value pairs that can be used by applications to identify or
102-
// group commands.
105+
// group commands or set special options.
103106
Annotations map[string]string
104107

105108
// Version defines the version for this command. If this value is non-empty and the command does not
@@ -1424,6 +1427,9 @@ func (c *Command) CommandPath() string {
14241427
if c.HasParent() {
14251428
return c.Parent().CommandPath() + " " + c.Name()
14261429
}
1430+
if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok {
1431+
return displayName
1432+
}
14271433
return c.Name()
14281434
}
14291435

command_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,36 @@ func TestAliasPrefixMatching(t *testing.T) {
366366
EnablePrefixMatching = defaultPrefixMatching
367367
}
368368

369+
// TestPlugin checks usage as plugin for another command such as kubectl. The
370+
// executable is `kubectl-plugin`, but we run it as `kubectl plugin`. The help
371+
// text should reflect the way we run the command.
372+
func TestPlugin(t *testing.T) {
373+
rootCmd := &Command{
374+
Use: "plugin",
375+
Args: NoArgs,
376+
Annotations: map[string]string{
377+
CommandDisplayNameAnnotation: "kubectl plugin",
378+
},
379+
}
380+
381+
subCmd := &Command{Use: "sub [flags]", Args: NoArgs, Run: emptyRun}
382+
rootCmd.AddCommand(subCmd)
383+
384+
rootHelp, err := executeCommand(rootCmd, "-h")
385+
if err != nil {
386+
t.Errorf("Unexpected error: %v", err)
387+
}
388+
389+
checkStringContains(t, rootHelp, "kubectl plugin [command]")
390+
391+
childHelp, err := executeCommand(rootCmd, "sub", "-h")
392+
if err != nil {
393+
t.Errorf("Unexpected error: %v", err)
394+
}
395+
396+
checkStringContains(t, childHelp, "kubectl plugin sub [flags]")
397+
}
398+
369399
// TestChildSameName checks the correct behaviour of cobra in cases,
370400
// when an application with name "foo" and with subcommand "foo"
371401
// is executed with args "foo foo".

0 commit comments

Comments
 (0)