Repository: spf13/cobra
Pull request #143 (2015-09-04 21:17:12)

Feature/hidden commands
It would be nice if there was a quick way to designate a command as "for internal use only" (hidden/private) so that consumers would never see the command but cobra could still run it as any other command.

This implementation is very simple and similar in design to the "deprecated" field.

eparis: Personally I like `hidden` rather than `private`, but that's bikeshedding, I know.

You need to update `HasNonHelpSubCommands` (maybe rename that func?) So that it won't print `Available Commands` if you only have a single subcommand which is a private command? (Basically everywhere we check `deprecated` you want to do the same thing)

Also add some _test.go so we know that private/hidden commands work, but aren't showing up in any of the doc types?
sdomino: Note that one of the tests is commented out. When run, that particular test passes, however it has some associated output that shows up as part of the execution, and I wasn't sure if that wanted to be seen when running the test suite.

Patch:
diff --git a/command.go b/command.go
index bf642b5..2996fd5 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
 }
 


Test patch:
diff --git a/cobra_test.go b/cobra_test.go
index 3aed7dd..03d8104 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_test.go b/command_test.go
index 477d84e..43ed7a3 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