Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HideHelpCommand #1083

Merged
merged 1 commit into from Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 8 additions & 13 deletions app.go
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
}
Comment on lines -333 to -342
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(blocking question) this code is being removed because it was duplicated in Setup above, yeah?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and because of the duplication, this code path was not executed and could not be covered with tests: #1083 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find!


var newCmds []*Command
for _, c := range a.Commands {
if c.HelpName == "" {
Expand Down
6 changes: 5 additions & 1 deletion command.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion flag.go
Expand Up @@ -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"},
Expand Down
145 changes: 145 additions & 0 deletions help_test.go
Expand Up @@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"runtime"
"strings"
"testing"
Expand Down Expand Up @@ -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)
}
}