From 6d2f1d2fa796b1787d2273e12788e05c10c8aa9b Mon Sep 17 00:00:00 2001 From: Steve Domino Date: Fri, 4 Sep 2015 14:34:51 -0600 Subject: [PATCH] Ability to hide commands from usage/help text Added the ability to have hidden commands that cobra will still run as intended, however they won't show up in any usage/help text adding internal field to command private is a better name hiding private commands in default help/usage opting for 'hidden' over 'private' updating all 'help command' checks to exclude hidden commands updating how commands are displayed in usage/help text by updating/adding some methods. added tests for hidden/deprecated commands making command hidden when testing hidden command execution test now leverage the included suite and are much less custom. also removed deprecation tests, once I discovered them in cobra_test.go updating hidden command test to be more reliable removing unnecessary () when checking len(c.Deprecated) updating command comments to be godoc friendly --- cobra_test.go | 20 ++++++++++++++----- command.go | 53 ++++++++++++++++++++++++++++++++++++------------- command_test.go | 24 ++++++++++++++++++++++ 3 files changed, 78 insertions(+), 19 deletions(-) diff --git a/cobra_test.go b/cobra_test.go index 3aed7dd6..03d81042 100644 --- a/cobra_test.go +++ b/cobra_test.go @@ -19,7 +19,7 @@ var _ = os.Stderr var tp, te, tt, t1, tr []string var rootPersPre, echoPre, echoPersPre, timesPersPre []string var flagb1, flagb2, flagb3, flagbr, flagbp bool -var flags1, flags2a, flags2b, flags3 string +var flags1, flags2a, flags2b, flags3, outs string var flagi1, flagi2, flagi3, flagir int var globalFlag1 bool var flagEcho, rootcalled bool @@ -28,6 +28,16 @@ var versionUsed int const strtwoParentHelp = "help message for parent flag strtwo" const strtwoChildHelp = "help message for child flag strtwo" +var cmdHidden = &Command{ + Use: "hide [secret string to print]", + Short: "Print anything to screen (if command is known)", + Long: `an absolutely utterly useless command for testing.`, + Run: func(cmd *Command, args []string) { + outs = "hidden" + }, + Hidden: true, +} + var cmdPrint = &Command{ Use: "print [string to print]", Short: "Print anything to the screen", @@ -976,15 +986,15 @@ func TestFlagOnPflagCommandLine(t *testing.T) { func TestAddTemplateFunctions(t *testing.T) { AddTemplateFunc("t", func() bool { return true }) AddTemplateFuncs(template.FuncMap{ - "f": func() bool { return false }, - "h": func() string { return "Hello," }, + "f": func() bool { return false }, + "h": func() string { return "Hello," }, "w": func() string { return "world." }}) const usage = "Hello, world." - + c := &Command{} c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`) - + if us := c.UsageString(); us != usage { t.Errorf("c.UsageString() != \"%s\", is \"%s\"", usage, us) } diff --git a/command.go b/command.go index bf642b53..2996fd5f 100644 --- a/command.go +++ b/command.go @@ -51,6 +51,8 @@ type Command struct { BashCompletionFunction string // Is this command deprecated and should print this string when used? Deprecated string + // Is this command hidden and should NOT show up in the list of available commands? + Hidden bool // Full set of flags flags *flag.FlagSet // Set of flags childrens of this command will inherit @@ -256,9 +258,9 @@ Aliases: {{end}}{{if .HasExample}} Examples: -{{ .Example }}{{end}}{{ if .HasNonHelpSubCommands}} +{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}} -Available Commands: {{range .Commands}}{{if (not .IsHelpCommand)}} +Available Commands: {{range .Commands}}{{if .IsAvailableCommand}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasLocalFlags}} Flags: @@ -850,42 +852,65 @@ func (c *Command) HasSubCommands() bool { return len(c.commands) > 0 } +// IsAvailableCommand determines if a command is available as a non-help command +// (this includes all non deprecated/hidden commands) +func (c *Command) IsAvailableCommand() bool { + + // a command is 'available' if it is runnable and is not deprecated/hidden + return c.Runnable() && len(c.Deprecated) == 0 && !c.Hidden +} + +// IsHelpCommand determines if a command is a 'help' command; a help command is +// determined by the fact that it is NOT runnable/hidden/deprecated, and has no +// sub commands that are runnable/hidden/deprecated func (c *Command) IsHelpCommand() bool { - if c.Runnable() { + + // if a command is runnable, deprecated, or hidden it is not a 'help' command + if c.Runnable() || len(c.Deprecated) != 0 || c.Hidden { return false } + + // if any non-help sub commands are found, the command is not a 'help' command for _, sub := range c.commands { - if len(sub.Deprecated) != 0 { - continue - } if !sub.IsHelpCommand() { return false } } + + // the command either has no sub commands, or no non-help sub commands return true } +// HasHelpSubCommands determines if a command has any avilable 'help' sub commands +// that need to be shown in the usage/help default template under 'additional help +// topics' func (c *Command) HasHelpSubCommands() bool { + + // return true on the first found available 'help' sub command for _, sub := range c.commands { - if len(sub.Deprecated) != 0 { - continue - } if sub.IsHelpCommand() { return true } } + + // the command either has no sub commands, or no available 'help' sub commands return false } -func (c *Command) HasNonHelpSubCommands() bool { +// HasAvailableSubCommands determines if a command has available sub commands that +// need to be shown in the usage/help default template under 'available commands' +func (c *Command) HasAvailableSubCommands() bool { + + // return true on the first found available (non deprecated/help/hidden) + // sub command for _, sub := range c.commands { - if len(sub.Deprecated) != 0 { - continue - } - if !sub.IsHelpCommand() { + if sub.IsAvailableCommand() { return true } } + + // the command either has no sub comamnds, or no available (non deprecated/help/hidden) + // sub commands return false } diff --git a/command_test.go b/command_test.go index 477d84e7..43ed7a34 100644 --- a/command_test.go +++ b/command_test.go @@ -5,6 +5,30 @@ import ( "testing" ) +// test to ensure hidden commands run as intended +func TestHiddenCommandExecutes(t *testing.T) { + + // ensure that outs does not already equal what the command will be setting it + // to, if it did this test would not actually be testing anything... + if outs == "hidden" { + t.Errorf("outs should NOT EQUAL hidden") + } + + cmdHidden.Execute() + + // upon running the command, the value of outs should now be 'hidden' + if outs != "hidden" { + t.Errorf("Hidden command failed to run!") + } +} + +// test to ensure hidden commands do not show up in usage/help text +func TestHiddenCommandIsHidden(t *testing.T) { + if cmdHidden.IsAvailableCommand() { + t.Errorf("Hidden command found!") + } +} + func TestStripFlags(t *testing.T) { tests := []struct { input []string