Merge pull request #399 from codegangsta/exit-nonzero-for-unknown-subcommand

Exit non-zero if a unknown subcommand is given
This commit is contained in:
Dan Buch 2016-05-09 01:05:34 -04:00
commit 63ed8b0bde
3 changed files with 80 additions and 19 deletions

View File

@ -11,11 +11,15 @@
### Changed ### Changed
- `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer - `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer
quoted in help text output. quoted in help text output.
- All flag types now include `(default: {value})` strings following usage when a - All flag types now include `(default: {value})` strings following usage when a
default value can be (reasonably) detected. default value can be (reasonably) detected.
- `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent - `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent
with non-slice flag types with non-slice flag types
- Apps now exit with a code of 3 if an unknown subcommand is specified
(previously they printed "No help topic for...", but still exited 0. This
makes it easier to script around apps built using `cli` since they can trust
that a 0 exit code indicated a successful execution.
## [1.16.0] - 2016-05-02 ## [1.16.0] - 2016-05-02
### Added ### Added

32
help.go
View File

@ -82,10 +82,10 @@ var helpCommand = Command{
Action: func(c *Context) error { Action: func(c *Context) error {
args := c.Args() args := c.Args()
if args.Present() { if args.Present() {
ShowCommandHelp(c, args.First()) return ShowCommandHelp(c, args.First())
} else {
ShowAppHelp(c)
} }
ShowAppHelp(c)
return nil return nil
}, },
} }
@ -98,11 +98,10 @@ var helpSubcommand = Command{
Action: func(c *Context) error { Action: func(c *Context) error {
args := c.Args() args := c.Args()
if args.Present() { if args.Present() {
ShowCommandHelp(c, args.First()) return ShowCommandHelp(c, args.First())
} else {
ShowSubcommandHelp(c)
} }
return nil
return ShowSubcommandHelp(c)
}, },
} }
@ -131,30 +130,31 @@ func DefaultAppComplete(c *Context) {
} }
// Prints help for the given command // Prints help for the given command
func ShowCommandHelp(ctx *Context, command string) { func ShowCommandHelp(ctx *Context, command string) error {
// show the subcommand help for a command with subcommands // show the subcommand help for a command with subcommands
if command == "" { if command == "" {
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
return return nil
} }
for _, c := range ctx.App.Commands { for _, c := range ctx.App.Commands {
if c.HasName(command) { if c.HasName(command) {
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
return return nil
} }
} }
if ctx.App.CommandNotFound != nil { if ctx.App.CommandNotFound == nil {
ctx.App.CommandNotFound(ctx, command) return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3)
} else {
fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command)
} }
ctx.App.CommandNotFound(ctx, command)
return nil
} }
// Prints help for the given subcommand // Prints help for the given subcommand
func ShowSubcommandHelp(c *Context) { func ShowSubcommandHelp(c *Context) error {
ShowCommandHelp(c, c.Command.Name) return ShowCommandHelp(c, c.Command.Name)
} }
// Prints the version number of the App // Prints the version number of the App

View File

@ -2,6 +2,7 @@ package cli
import ( import (
"bytes" "bytes"
"flag"
"strings" "strings"
"testing" "testing"
) )
@ -112,6 +113,62 @@ func Test_Version_Custom_Flags(t *testing.T) {
} }
} }
func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) {
app := NewApp()
set := flag.NewFlagSet("test", 0)
set.Parse([]string{"foo"})
c := NewContext(app, set, nil)
err := helpCommand.Action.(func(*Context) error)(c)
if err == nil {
t.Fatalf("expected error from helpCommand.Action(), but got nil")
}
exitErr, ok := err.(*ExitError)
if !ok {
t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error())
}
if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
}
if exitErr.exitCode != 3 {
t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
}
}
func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) {
app := NewApp()
set := flag.NewFlagSet("test", 0)
set.Parse([]string{"foo"})
c := NewContext(app, set, nil)
err := helpSubcommand.Action.(func(*Context) error)(c)
if err == nil {
t.Fatalf("expected error from helpCommand.Action(), but got nil")
}
exitErr, ok := err.(*ExitError)
if !ok {
t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error())
}
if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
}
if exitErr.exitCode != 3 {
t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
}
}
func TestShowAppHelp_HiddenCommand(t *testing.T) { func TestShowAppHelp_HiddenCommand(t *testing.T) {
app := &App{ app := &App{
Commands: []Command{ Commands: []Command{