diff --git a/app.go b/app.go index dd8f1deb39..d0c8f84e2c 100644 --- a/app.go +++ b/app.go @@ -43,8 +43,11 @@ type App struct { Flags []Flag // Boolean to enable bash completion commands EnableBashCompletion bool - // Boolean to hide built-in help command + // Boolean to hide built-in help command and help flag HideHelp bool + // Boolean to hide built-in help command but keep help flag. + // Ignored if HideHelp is true. + HideHelpCommand bool // Boolean to hide built-in version flag and the VERSION section of help HideVersion bool // categories contains the categorized commands and is populated on app startup @@ -170,7 +173,9 @@ func (a *App) Setup() { a.Commands = newCommands if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.appendCommand(helpCommand) + if !a.HideHelpCommand { + a.appendCommand(helpCommand) + } if HelpFlag != nil { a.appendFlag(HelpFlag) @@ -328,19 +333,9 @@ func (a *App) RunAndExitOnError() { // RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to // generate command-specific flags func (a *App) RunAsSubcommand(ctx *Context) (err error) { + // Setup also handles HideHelp and HideHelpCommand a.Setup() - // append help to commands - if len(a.Commands) > 0 { - if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.appendCommand(helpCommand) - - if HelpFlag != nil { - a.appendFlag(HelpFlag) - } - } - } - var newCmds []*Command for _, c := range a.Commands { if c.HelpName == "" { diff --git a/command.go b/command.go index db6c8024ba..95840f32e8 100644 --- a/command.go +++ b/command.go @@ -41,8 +41,11 @@ type Command struct { Flags []Flag // Treat all flags as normal arguments if true SkipFlagParsing bool - // Boolean to hide built-in help command + // Boolean to hide built-in help command and help flag HideHelp bool + // Boolean to hide built-in help command but keep help flag + // Ignored if HideHelp is true. + HideHelpCommand bool // Boolean to hide this command from help or completion Hidden bool // Boolean to enable short-option handling so user can combine several @@ -236,6 +239,7 @@ func (c *Command) startApp(ctx *Context) error { app.Commands = c.Subcommands app.Flags = c.Flags app.HideHelp = c.HideHelp + app.HideHelpCommand = c.HideHelpCommand app.Version = ctx.App.Version app.HideVersion = ctx.App.HideVersion diff --git a/flag.go b/flag.go index 712eca9e22..ad97c2d058 100644 --- a/flag.go +++ b/flag.go @@ -36,7 +36,7 @@ var VersionFlag Flag = &BoolFlag{ // HelpFlag prints the help for all commands and subcommands. // Set to nil to disable the flag. The subcommand -// will still be added unless HideHelp is set to true. +// will still be added unless HideHelp or HideHelpCommand is set to true. var HelpFlag Flag = &BoolFlag{ Name: "help", Aliases: []string{"h"}, diff --git a/help_test.go b/help_test.go index 9f182e89ab..5f292b77e5 100644 --- a/help_test.go +++ b/help_test.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "io" + "io/ioutil" "runtime" "strings" "testing" @@ -762,3 +763,147 @@ VERSION: t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String()) } } + +func TestHideHelpCommand(t *testing.T) { + app := &App{ + HideHelpCommand: true, + Writer: ioutil.Discard, + } + + err := app.Run([]string{"foo", "help"}) + if err == nil { + t.Fatalf("expected a non-nil error") + } + if !strings.Contains(err.Error(), "No help topic for 'help'") { + t.Errorf("Run returned unexpected error: %v", err) + } + + err = app.Run([]string{"foo", "--help"}) + if err != nil { + t.Errorf("Run returned unexpected error: %v", err) + } +} + +func TestHideHelpCommand_False(t *testing.T) { + app := &App{ + HideHelpCommand: false, + Writer: ioutil.Discard, + } + + err := app.Run([]string{"foo", "help"}) + if err != nil { + t.Errorf("Run returned unexpected error: %v", err) + } + + err = app.Run([]string{"foo", "--help"}) + if err != nil { + t.Errorf("Run returned unexpected error: %v", err) + } +} + +func TestHideHelpCommand_WithHideHelp(t *testing.T) { + app := &App{ + HideHelp: true, // effective (hides both command and flag) + HideHelpCommand: true, // ignored + Writer: ioutil.Discard, + } + + err := app.Run([]string{"foo", "help"}) + if err == nil { + t.Fatalf("expected a non-nil error") + } + if !strings.Contains(err.Error(), "No help topic for 'help'") { + t.Errorf("Run returned unexpected error: %v", err) + } + + err = app.Run([]string{"foo", "--help"}) + if err == nil { + t.Fatalf("expected a non-nil error") + } + if !strings.Contains(err.Error(), "flag: help requested") { + t.Errorf("Run returned unexpected error: %v", err) + } +} + +func newContextFromStringSlice(ss []string) *Context { + set := flag.NewFlagSet("", flag.ContinueOnError) + _ = set.Parse(ss) + return &Context{flagSet: set} +} + +func TestHideHelpCommand_RunAsSubcommand(t *testing.T) { + app := &App{ + HideHelpCommand: true, + Writer: ioutil.Discard, + Commands: []*Command{ + { + Name: "dummy", + }, + }, + } + + err := app.RunAsSubcommand(newContextFromStringSlice([]string{"", "help"})) + if err == nil { + t.Fatalf("expected a non-nil error") + } + if !strings.Contains(err.Error(), "No help topic for 'help'") { + t.Errorf("Run returned unexpected error: %v", err) + } + + err = app.RunAsSubcommand(newContextFromStringSlice([]string{"", "--help"})) + if err != nil { + t.Errorf("Run returned unexpected error: %v", err) + } +} + +func TestHideHelpCommand_RunAsSubcommand_False(t *testing.T) { + app := &App{ + HideHelpCommand: false, + Writer: ioutil.Discard, + Commands: []*Command{ + { + Name: "dummy", + }, + }, + } + + err := app.RunAsSubcommand(newContextFromStringSlice([]string{"", "help"})) + if err != nil { + t.Errorf("Run returned unexpected error: %v", err) + } + + err = app.RunAsSubcommand(newContextFromStringSlice([]string{"", "--help"})) + if err != nil { + t.Errorf("Run returned unexpected error: %v", err) + } +} + +func TestHideHelpCommand_WithSubcommands(t *testing.T) { + app := &App{ + Writer: ioutil.Discard, + Commands: []*Command{ + { + Name: "dummy", + Subcommands: []*Command{ + { + Name: "dummy2", + }, + }, + HideHelpCommand: true, + }, + }, + } + + err := app.Run([]string{"foo", "dummy", "help"}) + if err == nil { + t.Fatalf("expected a non-nil error") + } + if !strings.Contains(err.Error(), "No help topic for 'help'") { + t.Errorf("Run returned unexpected error: %v", err) + } + + err = app.Run([]string{"foo", "dummy", "--help"}) + if err != nil { + t.Errorf("Run returned unexpected error: %v", err) + } +}