Merge branch 'develop' of https://github.com/txgruppi/cli into txgruppi-develop

This commit is contained in:
Dan Buch 2016-04-25 18:10:10 -04:00
commit 10c8309d84
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
11 changed files with 346 additions and 197 deletions

View File

@ -255,8 +255,8 @@ Initialization must also occur for these flags. Below is an example initializing
command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
``` ```
The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context. The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context.
It will then use that file name to initialize the yaml input source for any flags that are defined on that command. It will then use that file name to initialize the yaml input source for any flags that are defined on that command.
As a note the "load" flag used would also have to be defined on the command flags in order for this code snipped to work. As a note the "load" flag used would also have to be defined on the command flags in order for this code snipped to work.
Currently only YAML files are supported but developers can add support for other input sources by implementing the Currently only YAML files are supported but developers can add support for other input sources by implementing the
@ -265,20 +265,20 @@ altsrc.InputSourceContext for their given sources.
Here is a more complete sample of a command using YAML support: Here is a more complete sample of a command using YAML support:
``` go ``` go
command := &cli.Command{ command := &cli.Command{
Name: "test-cmd", Name: "test-cmd",
Aliases: []string{"tc"}, Aliases: []string{"tc"},
Usage: "this is for testing", Usage: "this is for testing",
Description: "testing", Description: "testing",
Action: func(c *cli.Context) { Action: func(c *cli.Context) {
// Action to run // Action to run
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}), NewIntFlag(cli.IntFlag{Name: "test"}),
cli.StringFlag{Name: "load"}}, cli.StringFlag{Name: "load"}},
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c) err := command.Run(c)
``` ```
### Subcommands ### Subcommands
@ -339,19 +339,19 @@ E.g.
```go ```go
... ...
app.Commands = []cli.Command{ app.Commands = []cli.Command{
{ {
Name: "noop", Name: "noop",
}, },
{ {
Name: "add", Name: "add",
Category: "template", Category: "template",
}, },
{ {
Name: "remove", Name: "remove",
Category: "template", Category: "template",
}, },
} }
... ...
``` ```
@ -368,6 +368,28 @@ COMMANDS:
... ...
``` ```
### Exit code
It is your responsibility to call `os.Exit` with the exit code returned by
`app.Run`, e.g.:
```go
package main
import (
"os"
"github.com/codegangsta/cli"
)
func main() {
exitCode, err := cli.NewApp().Run(os.Args)
if err != nil {
log.Println(err)
}
os.Exit(exitCode)
}
```
### Bash Completion ### Bash Completion
You can enable completion commands by setting the `EnableBashCompletion` You can enable completion commands by setting the `EnableBashCompletion`

View File

@ -38,28 +38,28 @@ func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSource
// InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new // InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
// input source based on the func provided. If there is no error it will then apply the new input source to any flags // input source based on the func provided. If there is no error it will then apply the new input source to any flags
// that are supported by the input source // that are supported by the input source
func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) func(context *cli.Context) error { func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFn {
return func(context *cli.Context) error { return func(context *cli.Context) (int, error) {
inputSource, err := createInputSource() inputSource, err := createInputSource()
if err != nil { if err != nil {
return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error()) return cli.DefaultErrorExitCode, fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error())
} }
return ApplyInputSourceValues(context, inputSource, flags) return cli.DefaultSuccessExitCode, ApplyInputSourceValues(context, inputSource, flags)
} }
} }
// InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new // InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new
// input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is // input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is
// no error it will then apply the new input source to any flags that are supported by the input source // no error it will then apply the new input source to any flags that are supported by the input source
func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) func(context *cli.Context) error { func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFn {
return func(context *cli.Context) error { return func(context *cli.Context) (int, error) {
inputSource, err := createInputSource(context) inputSource, err := createInputSource(context)
if err != nil { if err != nil {
return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error()) return cli.DefaultErrorExitCode, fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error())
} }
return ApplyInputSourceValues(context, inputSource, flags) return cli.DefaultSuccessExitCode, ApplyInputSourceValues(context, inputSource, flags)
} }
} }

View File

@ -29,16 +29,17 @@ func TestCommandYamlFileTest(t *testing.T) {
Aliases: []string{"tc"}, Aliases: []string{"tc"},
Usage: "this is for testing", Usage: "this is for testing",
Description: "testing", Description: "testing",
Action: func(c *cli.Context) { Action: func(c *cli.Context) int {
val := c.Int("test") val := c.Int("test")
expect(t, val, 15) expect(t, val, 15)
return 0
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}), NewIntFlag(cli.IntFlag{Name: "test"}),
cli.StringFlag{Name: "load"}}, cli.StringFlag{Name: "load"}},
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c) _, err := command.Run(c)
expect(t, err, nil) expect(t, err, nil)
} }
@ -61,9 +62,10 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
Aliases: []string{"tc"}, Aliases: []string{"tc"},
Usage: "this is for testing", Usage: "this is for testing",
Description: "testing", Description: "testing",
Action: func(c *cli.Context) { Action: func(c *cli.Context) int {
val := c.Int("test") val := c.Int("test")
expect(t, val, 10) expect(t, val, 10)
return 0
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}),
@ -71,7 +73,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c) _, err := command.Run(c)
expect(t, err, nil) expect(t, err, nil)
} }
@ -92,9 +94,10 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
Aliases: []string{"tc"}, Aliases: []string{"tc"},
Usage: "this is for testing", Usage: "this is for testing",
Description: "testing", Description: "testing",
Action: func(c *cli.Context) { Action: func(c *cli.Context) int {
val := c.Int("test") val := c.Int("test")
expect(t, val, 7) expect(t, val, 7)
return 0
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}), NewIntFlag(cli.IntFlag{Name: "test"}),
@ -102,7 +105,7 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c) _, err := command.Run(c)
expect(t, err, nil) expect(t, err, nil)
} }
@ -123,9 +126,10 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
Aliases: []string{"tc"}, Aliases: []string{"tc"},
Usage: "this is for testing", Usage: "this is for testing",
Description: "testing", Description: "testing",
Action: func(c *cli.Context) { Action: func(c *cli.Context) int {
val := c.Int("test") val := c.Int("test")
expect(t, val, 15) expect(t, val, 15)
return 0
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), NewIntFlag(cli.IntFlag{Name: "test", Value: 7}),
@ -133,7 +137,7 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c) _, err := command.Run(c)
expect(t, err, nil) expect(t, err, nil)
} }
@ -157,16 +161,17 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T
Aliases: []string{"tc"}, Aliases: []string{"tc"},
Usage: "this is for testing", Usage: "this is for testing",
Description: "testing", Description: "testing",
Action: func(c *cli.Context) { Action: func(c *cli.Context) int {
val := c.Int("test") val := c.Int("test")
expect(t, val, 11) expect(t, val, 11)
return 0
}, },
Flags: []cli.Flag{ Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}),
cli.StringFlag{Name: "load"}}, cli.StringFlag{Name: "load"}},
} }
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c) _, err := command.Run(c)
expect(t, err, nil) expect(t, err, nil)
} }

79
app.go
View File

@ -10,6 +10,15 @@ import (
"time" "time"
) )
var (
// DefaultSuccessExitCode is the default for use with os.Exit intended to
// indicate success
DefaultSuccessExitCode = 0
// DefaultErrorExitCode is the default for use with os.Exit intended to
// indicate an error
DefaultErrorExitCode = 1
)
// App is the main structure of a cli application. It is recommended that // App is the main structure of a cli application. It is recommended that
// an app be created with the cli.NewApp() function // an app be created with the cli.NewApp() function
type App struct { type App struct {
@ -38,17 +47,17 @@ type App struct {
// Populate on app startup, only gettable throught method Categories() // Populate on app startup, only gettable throught method Categories()
categories CommandCategories categories CommandCategories
// An action to execute when the bash-completion flag is set // An action to execute when the bash-completion flag is set
BashComplete func(context *Context) BashComplete BashCompleteFn
// An action to execute before any subcommands are run, but after the context is ready // An action to execute before any subcommands are run, but after the context is ready
// If a non-nil error is returned, no subcommands are run // If a non-nil error is returned, no subcommands are run
Before func(context *Context) error Before BeforeFn
// An action to execute after any subcommands are run, but after the subcommand has finished // An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics // It is run even if Action() panics
After func(context *Context) error After AfterFn
// The action to execute when no subcommands are specified // The action to execute when no subcommands are specified
Action func(context *Context) Action ActionFn
// Execute this function if the proper command cannot be found // Execute this function if the proper command cannot be found
CommandNotFound func(context *Context, command string) CommandNotFound CommandNotFoundFn
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages. // Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
// This function is able to replace the original error messages. // This function is able to replace the original error messages.
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted. // If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
@ -93,7 +102,7 @@ func NewApp() *App {
} }
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) { func (a *App) Run(arguments []string) (ec int, err error) {
if a.Author != "" || a.Email != "" { if a.Author != "" || a.Email != "" {
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
} }
@ -139,52 +148,57 @@ func (a *App) Run(arguments []string) (err error) {
if nerr != nil { if nerr != nil {
fmt.Fprintln(a.Writer, nerr) fmt.Fprintln(a.Writer, nerr)
ShowAppHelp(context) ShowAppHelp(context)
return nerr return DefaultErrorExitCode, nerr
} }
if checkCompletions(context) { if checkCompletions(context) {
return nil return DefaultSuccessExitCode, nil
} }
if err != nil { if err != nil {
if a.OnUsageError != nil { if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false) err := a.OnUsageError(context, err, false)
return err if err != nil {
return DefaultErrorExitCode, err
}
return DefaultSuccessExitCode, err
} else { } else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowAppHelp(context) ShowAppHelp(context)
return err return DefaultErrorExitCode, err
} }
} }
if !a.HideHelp && checkHelp(context) { if !a.HideHelp && checkHelp(context) {
ShowAppHelp(context) ShowAppHelp(context)
return nil return DefaultSuccessExitCode, nil
} }
if !a.HideVersion && checkVersion(context) { if !a.HideVersion && checkVersion(context) {
ShowVersion(context) ShowVersion(context)
return nil return DefaultSuccessExitCode, nil
} }
if a.After != nil { if a.After != nil {
defer func() { defer func() {
if afterErr := a.After(context); afterErr != nil { afterEc, afterErr := a.After(context)
if afterErr != nil {
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
} else { } else {
err = afterErr err = afterErr
} }
} }
ec = afterEc
}() }()
} }
if a.Before != nil { if a.Before != nil {
err = a.Before(context) ec, err = a.Before(context)
if err != nil { if err != nil {
fmt.Fprintf(a.Writer, "%v\n\n", err) fmt.Fprintf(a.Writer, "%v\n\n", err)
ShowAppHelp(context) ShowAppHelp(context)
return err return ec, err
} }
} }
@ -198,20 +212,19 @@ func (a *App) Run(arguments []string) (err error) {
} }
// Run default Action // Run default Action
a.Action(context) return a.Action(context), nil
return nil
} }
// Another entry point to the cli app, takes care of passing arguments and error handling // Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() { func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil { if exitCode, err := a.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(exitCode)
} }
} }
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags // Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) { func (a *App) RunAsSubcommand(ctx *Context) (ec int, err error) {
// append help to commands // append help to commands
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
if a.Command(helpCommand.Name) == nil && !a.HideHelp { if a.Command(helpCommand.Name) == nil && !a.HideHelp {
@ -251,37 +264,40 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} else { } else {
ShowCommandHelp(ctx, context.Args().First()) ShowCommandHelp(ctx, context.Args().First())
} }
return nerr return DefaultErrorExitCode, nerr
} }
if checkCompletions(context) { if checkCompletions(context) {
return nil return DefaultSuccessExitCode, nil
} }
if err != nil { if err != nil {
if a.OnUsageError != nil { if a.OnUsageError != nil {
err = a.OnUsageError(context, err, true) err = a.OnUsageError(context, err, true)
return err if err != nil {
return DefaultErrorExitCode, err
}
return DefaultSuccessExitCode, err
} else { } else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowSubcommandHelp(context) ShowSubcommandHelp(context)
return err return DefaultErrorExitCode, err
} }
} }
if len(a.Commands) > 0 { if len(a.Commands) > 0 {
if checkSubcommandHelp(context) { if checkSubcommandHelp(context) {
return nil return DefaultSuccessExitCode, nil
} }
} else { } else {
if checkCommandHelp(ctx, context.Args().First()) { if checkCommandHelp(ctx, context.Args().First()) {
return nil return DefaultSuccessExitCode, nil
} }
} }
if a.After != nil { if a.After != nil {
defer func() { defer func() {
afterErr := a.After(context) afterEc, afterErr := a.After(context)
if afterErr != nil { if afterErr != nil {
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
@ -289,13 +305,14 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
err = afterErr err = afterErr
} }
} }
ec = afterEc
}() }()
} }
if a.Before != nil { if a.Before != nil {
err := a.Before(context) ec, err = a.Before(context)
if err != nil { if err != nil {
return err return ec, err
} }
} }
@ -309,9 +326,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} }
// Run default Action // Run default Action
a.Action(context) return a.Action(context), nil
return nil
} }
// Returns the named command on App. Returns nil if the command does not exist // Returns the named command on App. Returns nil if the command does not exist

View File

@ -22,8 +22,9 @@ func ExampleApp_Run() {
app.Flags = []Flag{ app.Flags = []Flag{
StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
} }
app.Action = func(c *Context) { app.Action = func(c *Context) int {
fmt.Printf("Hello %v\n", c.String("name")) fmt.Printf("Hello %v\n", c.String("name"))
return 0
} }
app.UsageText = "app [first_arg] [second_arg]" app.UsageText = "app [first_arg] [second_arg]"
app.Author = "Harrison" app.Author = "Harrison"
@ -58,8 +59,9 @@ func ExampleApp_Run_subcommand() {
Usage: "Name of the person to greet", Usage: "Name of the person to greet",
}, },
}, },
Action: func(c *Context) { Action: func(c *Context) int {
fmt.Println("Hello,", c.String("name")) fmt.Println("Hello,", c.String("name"))
return 0
}, },
}, },
}, },
@ -86,8 +88,9 @@ func ExampleApp_Run_help() {
Aliases: []string{"d"}, Aliases: []string{"d"},
Usage: "use it to see a description", Usage: "use it to see a description",
Description: "This is how we describe describeit the function", Description: "This is how we describe describeit the function",
Action: func(c *Context) { Action: func(c *Context) int {
fmt.Printf("i like to describe things") fmt.Printf("i like to describe things")
return 0
}, },
}, },
} }
@ -116,15 +119,17 @@ func ExampleApp_Run_bashComplete() {
Aliases: []string{"d"}, Aliases: []string{"d"},
Usage: "use it to see a description", Usage: "use it to see a description",
Description: "This is how we describe describeit the function", Description: "This is how we describe describeit the function",
Action: func(c *Context) { Action: func(c *Context) int {
fmt.Printf("i like to describe things") fmt.Printf("i like to describe things")
return 0
}, },
}, { }, {
Name: "next", Name: "next",
Usage: "next example", Usage: "next example",
Description: "more stuff to see when generating bash completion", Description: "more stuff to see when generating bash completion",
Action: func(c *Context) { Action: func(c *Context) int {
fmt.Printf("the next example") fmt.Printf("the next example")
return 0
}, },
}, },
} }
@ -142,14 +147,17 @@ func TestApp_Run(t *testing.T) {
s := "" s := ""
app := NewApp() app := NewApp()
app.Action = func(c *Context) { app.Action = func(c *Context) int {
s = s + c.Args().First() s = s + c.Args().First()
return 0
} }
err := app.Run([]string{"command", "foo"}) ec, err := app.Run([]string{"command", "foo"})
expect(t, err, nil) expect(t, err, nil)
err = app.Run([]string{"command", "bar"}) expect(t, ec, 0)
ec, err = app.Run([]string{"command", "bar"})
expect(t, err, nil) expect(t, err, nil)
expect(t, ec, 0)
expect(t, s, "foobar") expect(t, s, "foobar")
} }
@ -188,9 +196,10 @@ func TestApp_CommandWithArgBeforeFlags(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringFlag{Name: "option", Value: "", Usage: "some option"}, StringFlag{Name: "option", Value: "", Usage: "some option"},
}, },
Action: func(c *Context) { Action: func(c *Context) int {
parsedOption = c.String("option") parsedOption = c.String("option")
firstArg = c.Args().First() firstArg = c.Args().First()
return 0
}, },
} }
app.Commands = []Command{command} app.Commands = []Command{command}
@ -208,8 +217,9 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
a.Commands = []Command{ a.Commands = []Command{
{ {
Name: "foo", Name: "foo",
Action: func(c *Context) { Action: func(c *Context) int {
context = c context = c
return 0
}, },
Flags: []Flag{ Flags: []Flag{
StringFlag{ StringFlag{
@ -218,7 +228,7 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
Usage: "language for the greeting", Usage: "language for the greeting",
}, },
}, },
Before: func(_ *Context) error { return nil }, Before: func(_ *Context) (int, error) { return 0, nil },
}, },
} }
a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})
@ -237,9 +247,10 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringFlag{Name: "option", Value: "", Usage: "some option"}, StringFlag{Name: "option", Value: "", Usage: "some option"},
}, },
Action: func(c *Context) { Action: func(c *Context) int {
parsedOption = c.String("option") parsedOption = c.String("option")
args = c.Args() args = c.Args()
return 0
}, },
} }
app.Commands = []Command{command} app.Commands = []Command{command}
@ -258,8 +269,9 @@ func TestApp_CommandWithDash(t *testing.T) {
app := NewApp() app := NewApp()
command := Command{ command := Command{
Name: "cmd", Name: "cmd",
Action: func(c *Context) { Action: func(c *Context) int {
args = c.Args() args = c.Args()
return 0
}, },
} }
app.Commands = []Command{command} app.Commands = []Command{command}
@ -276,8 +288,9 @@ func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
app := NewApp() app := NewApp()
command := Command{ command := Command{
Name: "cmd", Name: "cmd",
Action: func(c *Context) { Action: func(c *Context) int {
args = c.Args() args = c.Args()
return 0
}, },
} }
app.Commands = []Command{command} app.Commands = []Command{command}
@ -296,8 +309,9 @@ func TestApp_Float64Flag(t *testing.T) {
app.Flags = []Flag{ app.Flags = []Flag{
Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"},
} }
app.Action = func(c *Context) { app.Action = func(c *Context) int {
meters = c.Float64("height") meters = c.Float64("height")
return 0
} }
app.Run([]string{"", "--height", "1.93"}) app.Run([]string{"", "--height", "1.93"})
@ -316,11 +330,12 @@ func TestApp_ParseSliceFlags(t *testing.T) {
IntSliceFlag{Name: "p", Value: &IntSlice{}, Usage: "set one or more ip addr"}, IntSliceFlag{Name: "p", Value: &IntSlice{}, Usage: "set one or more ip addr"},
StringSliceFlag{Name: "ip", Value: &StringSlice{}, Usage: "set one or more ports to open"}, StringSliceFlag{Name: "ip", Value: &StringSlice{}, Usage: "set one or more ports to open"},
}, },
Action: func(c *Context) { Action: func(c *Context) int {
parsedIntSlice = c.IntSlice("p") parsedIntSlice = c.IntSlice("p")
parsedStringSlice = c.StringSlice("ip") parsedStringSlice = c.StringSlice("ip")
parsedOption = c.String("option") parsedOption = c.String("option")
firstArg = c.Args().First() firstArg = c.Args().First()
return 0
}, },
} }
app.Commands = []Command{command} app.Commands = []Command{command}
@ -373,9 +388,10 @@ func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) {
IntSliceFlag{Name: "a", Usage: "set numbers"}, IntSliceFlag{Name: "a", Usage: "set numbers"},
StringSliceFlag{Name: "str", Usage: "set strings"}, StringSliceFlag{Name: "str", Usage: "set strings"},
}, },
Action: func(c *Context) { Action: func(c *Context) int {
parsedIntSlice = c.IntSlice("a") parsedIntSlice = c.IntSlice("a")
parsedStringSlice = c.StringSlice("str") parsedStringSlice = c.StringSlice("str")
return 0
}, },
} }
app.Commands = []Command{command} app.Commands = []Command{command}
@ -427,7 +443,7 @@ func TestApp_SetStdout(t *testing.T) {
app.Name = "test" app.Name = "test"
app.Writer = w app.Writer = w
err := app.Run([]string{"help"}) _, err := app.Run([]string{"help"})
if err != nil { if err != nil {
t.Fatalf("Run error: %s", err) t.Fatalf("Run error: %s", err)
@ -445,21 +461,22 @@ func TestApp_BeforeFunc(t *testing.T) {
app := NewApp() app := NewApp()
app.Before = func(c *Context) error { app.Before = func(c *Context) (int, error) {
beforeRun = true beforeRun = true
s := c.String("opt") s := c.String("opt")
if s == "fail" { if s == "fail" {
return beforeError return DefaultErrorExitCode, beforeError
} }
return nil return DefaultSuccessExitCode, nil
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ Command{
Name: "sub", Name: "sub",
Action: func(c *Context) { Action: func(c *Context) int {
subcommandRun = true subcommandRun = true
return DefaultSuccessExitCode
}, },
}, },
} }
@ -469,7 +486,7 @@ func TestApp_BeforeFunc(t *testing.T) {
} }
// run with the Before() func succeeding // run with the Before() func succeeding
err = app.Run([]string{"command", "--opt", "succeed", "sub"}) ec, err := app.Run([]string{"command", "--opt", "succeed", "sub"})
if err != nil { if err != nil {
t.Fatalf("Run error: %s", err) t.Fatalf("Run error: %s", err)
@ -483,11 +500,15 @@ func TestApp_BeforeFunc(t *testing.T) {
t.Errorf("Subcommand not executed when expected") t.Errorf("Subcommand not executed when expected")
} }
if ec != DefaultSuccessExitCode {
t.Errorf("Expected exit code to be %d but got %d", DefaultSuccessExitCode, ec)
}
// reset // reset
beforeRun, subcommandRun = false, false beforeRun, subcommandRun = false, false
// run with the Before() func failing // run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"}) ec, err = app.Run([]string{"command", "--opt", "fail", "sub"})
// should be the same error produced by the Before func // should be the same error produced by the Before func
if err != beforeError { if err != beforeError {
@ -502,6 +523,9 @@ func TestApp_BeforeFunc(t *testing.T) {
t.Errorf("Subcommand executed when NOT expected") t.Errorf("Subcommand executed when NOT expected")
} }
if ec != DefaultErrorExitCode {
t.Errorf("Expected exit code to be %d but got %d", DefaultErrorExitCode, ec)
}
} }
func TestApp_AfterFunc(t *testing.T) { func TestApp_AfterFunc(t *testing.T) {
@ -511,21 +535,22 @@ func TestApp_AfterFunc(t *testing.T) {
app := NewApp() app := NewApp()
app.After = func(c *Context) error { app.After = func(c *Context) (int, error) {
afterRun = true afterRun = true
s := c.String("opt") s := c.String("opt")
if s == "fail" { if s == "fail" {
return afterError return DefaultErrorExitCode, afterError
} }
return nil return DefaultSuccessExitCode, nil
} }
app.Commands = []Command{ app.Commands = []Command{
Command{ Command{
Name: "sub", Name: "sub",
Action: func(c *Context) { Action: func(c *Context) int {
subcommandRun = true subcommandRun = true
return DefaultSuccessExitCode
}, },
}, },
} }
@ -535,7 +560,7 @@ func TestApp_AfterFunc(t *testing.T) {
} }
// run with the After() func succeeding // run with the After() func succeeding
err = app.Run([]string{"command", "--opt", "succeed", "sub"}) ec, err := app.Run([]string{"command", "--opt", "succeed", "sub"})
if err != nil { if err != nil {
t.Fatalf("Run error: %s", err) t.Fatalf("Run error: %s", err)
@ -549,11 +574,15 @@ func TestApp_AfterFunc(t *testing.T) {
t.Errorf("Subcommand not executed when expected") t.Errorf("Subcommand not executed when expected")
} }
if ec != DefaultSuccessExitCode {
t.Errorf("Expected exit code to be %d but got %d", DefaultSuccessExitCode, ec)
}
// reset // reset
afterRun, subcommandRun = false, false afterRun, subcommandRun = false, false
// run with the Before() func failing // run with the Before() func failing
err = app.Run([]string{"command", "--opt", "fail", "sub"}) ec, err = app.Run([]string{"command", "--opt", "fail", "sub"})
// should be the same error produced by the Before func // should be the same error produced by the Before func
if err != afterError { if err != afterError {
@ -567,6 +596,10 @@ func TestApp_AfterFunc(t *testing.T) {
if subcommandRun == false { if subcommandRun == false {
t.Errorf("Subcommand not executed when expected") t.Errorf("Subcommand not executed when expected")
} }
if ec != DefaultErrorExitCode {
t.Errorf("Expected exit code to be %d but got %d", DefaultErrorExitCode, ec)
}
} }
func TestAppNoHelpFlag(t *testing.T) { func TestAppNoHelpFlag(t *testing.T) {
@ -579,7 +612,7 @@ func TestAppNoHelpFlag(t *testing.T) {
app := NewApp() app := NewApp()
app.Writer = ioutil.Discard app.Writer = ioutil.Discard
err := app.Run([]string{"test", "-h"}) _, err := app.Run([]string{"test", "-h"})
if err != flag.ErrHelp { if err != flag.ErrHelp {
t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err) t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err)
@ -636,8 +669,9 @@ func TestAppCommandNotFound(t *testing.T) {
app.Commands = []Command{ app.Commands = []Command{
Command{ Command{
Name: "bar", Name: "bar",
Action: func(c *Context) { Action: func(c *Context) int {
subcommandRun = true subcommandRun = true
return 0
}, },
}, },
} }
@ -655,9 +689,10 @@ func TestGlobalFlag(t *testing.T) {
app.Flags = []Flag{ app.Flags = []Flag{
StringFlag{Name: "global, g", Usage: "global"}, StringFlag{Name: "global, g", Usage: "global"},
} }
app.Action = func(c *Context) { app.Action = func(c *Context) int {
globalFlag = c.GlobalString("global") globalFlag = c.GlobalString("global")
globalFlagSet = c.GlobalIsSet("global") globalFlagSet = c.GlobalIsSet("global")
return 0
} }
app.Run([]string{"command", "-g", "foo"}) app.Run([]string{"command", "-g", "foo"})
expect(t, globalFlag, "foo") expect(t, globalFlag, "foo")
@ -683,13 +718,14 @@ func TestGlobalFlagsInSubcommands(t *testing.T) {
Subcommands: []Command{ Subcommands: []Command{
{ {
Name: "bar", Name: "bar",
Action: func(c *Context) { Action: func(c *Context) int {
if c.GlobalBool("debug") { if c.GlobalBool("debug") {
subcommandRun = true subcommandRun = true
} }
if c.GlobalBool("parent") { if c.GlobalBool("parent") {
parentFlag = true parentFlag = true
} }
return 0
}, },
}, },
}, },
@ -731,7 +767,7 @@ func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) {
} }
app.Commands = []Command{cmd} app.Commands = []Command{cmd}
err := app.Run(flagSet) _, err := app.Run(flagSet)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -772,7 +808,7 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) {
} }
app.Commands = []Command{cmd} app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"}) _, err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -803,7 +839,7 @@ func TestApp_Run_SubcommandHelpName(t *testing.T) {
} }
app.Commands = []Command{cmd} app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"}) _, err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -834,7 +870,7 @@ func TestApp_Run_CommandHelpName(t *testing.T) {
} }
app.Commands = []Command{cmd} app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"}) _, err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -865,7 +901,7 @@ func TestApp_Run_CommandSubcommandHelpName(t *testing.T) {
} }
app.Commands = []Command{cmd} app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "--help"}) _, err := app.Run([]string{"command", "foo", "--help"})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -891,11 +927,12 @@ func TestApp_Run_Help(t *testing.T) {
app.Name = "boom" app.Name = "boom"
app.Usage = "make an explosive entrance" app.Usage = "make an explosive entrance"
app.Writer = buf app.Writer = buf
app.Action = func(c *Context) { app.Action = func(c *Context) int {
buf.WriteString("boom I say!") buf.WriteString("boom I say!")
return 0
} }
err := app.Run(args) _, err := app.Run(args)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -922,11 +959,12 @@ func TestApp_Run_Version(t *testing.T) {
app.Usage = "make an explosive entrance" app.Usage = "make an explosive entrance"
app.Version = "0.1.0" app.Version = "0.1.0"
app.Writer = buf app.Writer = buf
app.Action = func(c *Context) { app.Action = func(c *Context) int {
buf.WriteString("boom I say!") buf.WriteString("boom I say!")
return 0
} }
err := app.Run(args) _, err := app.Run(args)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -991,11 +1029,11 @@ func TestApp_Run_Categories(t *testing.T) {
func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp() app := NewApp()
app.Action = func(c *Context) {} app.Action = func(c *Context) int { return 0 }
app.Before = func(c *Context) error { return fmt.Errorf("before error") } app.Before = func(c *Context) (int, error) { return 1, fmt.Errorf("before error") }
app.After = func(c *Context) error { return fmt.Errorf("after error") } app.After = func(c *Context) (int, error) { return 2, fmt.Errorf("after error") }
err := app.Run([]string{"foo"}) ec, err := app.Run([]string{"foo"})
if err == nil { if err == nil {
t.Fatalf("expected to receive error from Run, got none") t.Fatalf("expected to receive error from Run, got none")
} }
@ -1006,6 +1044,10 @@ func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
if !strings.Contains(err.Error(), "after error") { if !strings.Contains(err.Error(), "after error") {
t.Errorf("expected text of error from After method, but got none in \"%v\"", err) t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
} }
if ec != 2 {
t.Errorf("Expected exit code to be %d but got %d", 2, ec)
}
} }
func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) { func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
@ -1018,12 +1060,12 @@ func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
}, },
}, },
Name: "bar", Name: "bar",
Before: func(c *Context) error { return fmt.Errorf("before error") }, Before: func(c *Context) (int, error) { return 1, fmt.Errorf("before error") },
After: func(c *Context) error { return fmt.Errorf("after error") }, After: func(c *Context) (int, error) { return 2, fmt.Errorf("after error") },
}, },
} }
err := app.Run([]string{"foo", "bar"}) ec, err := app.Run([]string{"foo", "bar"})
if err == nil { if err == nil {
t.Fatalf("expected to receive error from Run, got none") t.Fatalf("expected to receive error from Run, got none")
} }
@ -1034,6 +1076,10 @@ func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
if !strings.Contains(err.Error(), "after error") { if !strings.Contains(err.Error(), "after error") {
t.Errorf("expected text of error from After method, but got none in \"%v\"", err) t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
} }
if ec != 2 {
t.Errorf("Expected exit code to be %d but got %d", 2, ec)
}
} }
func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) { func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) {
@ -1056,7 +1102,7 @@ func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) {
}, },
} }
err := app.Run([]string{"foo", "--flag=wrong"}) _, err := app.Run([]string{"foo", "--flag=wrong"})
if err == nil { if err == nil {
t.Fatalf("expected to receive error from Run, got none") t.Fatalf("expected to receive error from Run, got none")
} }
@ -1086,7 +1132,7 @@ func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
}, },
} }
err := app.Run([]string{"foo", "--flag=wrong", "bar"}) _, err := app.Run([]string{"foo", "--flag=wrong", "bar"})
if err == nil { if err == nil {
t.Fatalf("expected to receive error from Run, got none") t.Fatalf("expected to receive error from Run, got none")
} }

View File

@ -26,15 +26,15 @@ type Command struct {
// The category the command is part of // The category the command is part of
Category string Category string
// The function to call when checking for bash command completions // The function to call when checking for bash command completions
BashComplete func(context *Context) BashComplete BashCompleteFn
// An action to execute before any sub-subcommands are run, but after the context is ready // An action to execute before any sub-subcommands are run, but after the context is ready
// If a non-nil error is returned, no sub-subcommands are run // If a non-nil error is returned, no sub-subcommands are run
Before func(context *Context) error Before BeforeFn
// An action to execute after any subcommands are run, but before the subcommand has finished // An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics // It is run even if Action() panics
After func(context *Context) error After AfterFn
// The function to call when this command is invoked // The function to call when this command is invoked
Action func(context *Context) Action ActionFn
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages. // Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
// This function is able to replace the original error messages. // This function is able to replace the original error messages.
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted. // If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
@ -65,7 +65,7 @@ func (c Command) FullName() string {
type Commands []Command type Commands []Command
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags // Invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) (err error) { func (c Command) Run(ctx *Context) (ec int, err error) {
if len(c.Subcommands) > 0 { if len(c.Subcommands) > 0 {
return c.startApp(ctx) return c.startApp(ctx)
} }
@ -126,12 +126,15 @@ func (c Command) Run(ctx *Context) (err error) {
if err != nil { if err != nil {
if c.OnUsageError != nil { if c.OnUsageError != nil {
err := c.OnUsageError(ctx, err) err := c.OnUsageError(ctx, err)
return err if err != nil {
return DefaultErrorExitCode, err
}
return DefaultSuccessExitCode, err
} else { } else {
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
return err return DefaultErrorExitCode, err
} }
} }
@ -140,44 +143,47 @@ func (c Command) Run(ctx *Context) (err error) {
fmt.Fprintln(ctx.App.Writer, nerr) fmt.Fprintln(ctx.App.Writer, nerr)
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
return nerr return DefaultErrorExitCode, nerr
} }
context := NewContext(ctx.App, set, ctx) context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) { if checkCommandCompletions(context, c.Name) {
return nil return DefaultSuccessExitCode, nil
} }
if checkCommandHelp(context, c.Name) { if checkCommandHelp(context, c.Name) {
return nil return DefaultSuccessExitCode, nil
} }
if c.After != nil { if c.After != nil {
defer func() { defer func() {
afterErr := c.After(context) afterEc, afterErr := c.After(context)
if afterErr != nil { if afterErr != nil {
if err != nil { if err != nil {
err = NewMultiError(err, afterErr) err = NewMultiError(err, afterErr)
} else { } else {
err = afterErr err = afterErr
} }
ec = afterEc
} }
}() }()
} }
if c.Before != nil { if c.Before != nil {
err := c.Before(context) ec, err = c.Before(context)
if err != nil { if err != nil {
fmt.Fprintln(ctx.App.Writer, err) fmt.Fprintln(ctx.App.Writer, err)
fmt.Fprintln(ctx.App.Writer) fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name) ShowCommandHelp(ctx, c.Name)
return err return ec, err
} }
} }
context.Command = c context.Command = c
c.Action(context) ec = c.Action(context)
return nil return ec, err
} }
func (c Command) Names() []string { func (c Command) Names() []string {
@ -200,7 +206,7 @@ func (c Command) HasName(name string) bool {
return false return false
} }
func (c Command) startApp(ctx *Context) error { func (c Command) startApp(ctx *Context) (int, error) {
app := NewApp() app := NewApp()
// set the name and usage // set the name and usage

View File

@ -34,12 +34,12 @@ func TestCommandFlagParsing(t *testing.T) {
Aliases: []string{"tc"}, Aliases: []string{"tc"},
Usage: "this is for testing", Usage: "this is for testing",
Description: "testing", Description: "testing",
Action: func(_ *Context) {}, Action: func(_ *Context) int { return 0 },
} }
command.SkipFlagParsing = c.skipFlagParsing command.SkipFlagParsing = c.skipFlagParsing
err := command.Run(context) _, err := command.Run(context)
expect(t, err, c.expectedErr) expect(t, err, c.expectedErr)
expect(t, []string(context.Args()), c.testArgs) expect(t, []string(context.Args()), c.testArgs)
@ -50,13 +50,17 @@ func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp() app := NewApp()
app.Commands = []Command{ app.Commands = []Command{
Command{ Command{
Name: "bar", Name: "bar",
Before: func(c *Context) error { return fmt.Errorf("before error") }, Before: func(c *Context) (int, error) {
After: func(c *Context) error { return fmt.Errorf("after error") }, return 1, fmt.Errorf("before error")
},
After: func(c *Context) (int, error) {
return 1, fmt.Errorf("after error")
},
}, },
} }
err := app.Run([]string{"foo", "bar"}) _, err := app.Run([]string{"foo", "bar"})
if err == nil { if err == nil {
t.Fatalf("expected to receive error from Run, got none") t.Fatalf("expected to receive error from Run, got none")
} }
@ -73,7 +77,7 @@ func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
app := NewApp() app := NewApp()
app.Commands = []Command{ app.Commands = []Command{
Command{ Command{
Name: "bar", Name: "bar",
Flags: []Flag{ Flags: []Flag{
IntFlag{Name: "flag"}, IntFlag{Name: "flag"},
}, },
@ -86,7 +90,7 @@ func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
}, },
} }
err := app.Run([]string{"foo", "bar", "--flag=wrong"}) _, err := app.Run([]string{"foo", "bar", "--flag=wrong"})
if err == nil { if err == nil {
t.Fatalf("expected to receive error from Run, got none") t.Fatalf("expected to receive error from Run, got none")
} }

View File

@ -4,9 +4,9 @@ import (
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
"runtime"
"strings" "strings"
"testing" "testing"
"runtime"
) )
var boolFlagTests = []struct { var boolFlagTests = []struct {
@ -64,7 +64,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_FOO%]" expectedSuffix = " [%APP_FOO%]"
} }
if !strings.HasSuffix(output, expectedSuffix) { if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with" + expectedSuffix, output) t.Errorf("%s does not end with"+expectedSuffix, output)
} }
} }
} }
@ -120,7 +120,7 @@ func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_QWWX%]" expectedSuffix = " [%APP_QWWX%]"
} }
if !strings.HasSuffix(output, expectedSuffix) { if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%q does not end with" + expectedSuffix, output) t.Errorf("%q does not end with"+expectedSuffix, output)
} }
} }
} }
@ -157,7 +157,7 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_BAR%]" expectedSuffix = " [%APP_BAR%]"
} }
if !strings.HasSuffix(output, expectedSuffix) { if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with" + expectedSuffix, output) t.Errorf("%s does not end with"+expectedSuffix, output)
} }
} }
} }
@ -194,7 +194,7 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_BAR%]" expectedSuffix = " [%APP_BAR%]"
} }
if !strings.HasSuffix(output, expectedSuffix) { if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with" + expectedSuffix, output) t.Errorf("%s does not end with"+expectedSuffix, output)
} }
} }
} }
@ -238,7 +238,7 @@ func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_SMURF%]" expectedSuffix = " [%APP_SMURF%]"
} }
if !strings.HasSuffix(output, expectedSuffix) { if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%q does not end with" + expectedSuffix, output) t.Errorf("%q does not end with"+expectedSuffix, output)
} }
} }
} }
@ -275,7 +275,7 @@ func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_BAZ%]" expectedSuffix = " [%APP_BAZ%]"
} }
if !strings.HasSuffix(output, expectedSuffix) { if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with" + expectedSuffix, output) t.Errorf("%s does not end with"+expectedSuffix, output)
} }
} }
} }
@ -313,7 +313,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_ZAP%]" expectedSuffix = " [%APP_ZAP%]"
} }
if !strings.HasSuffix(output, expectedSuffix) { if !strings.HasSuffix(output, expectedSuffix) {
t.Errorf("%s does not end with" + expectedSuffix, output) t.Errorf("%s does not end with"+expectedSuffix, output)
} }
} }
} }
@ -323,13 +323,14 @@ func TestParseMultiString(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringFlag{Name: "serve, s"}, StringFlag{Name: "serve, s"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.String("serve") != "10" { if ctx.String("serve") != "10" {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.String("s") != "10" { if ctx.String("s") != "10" {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
}).Run([]string{"run", "-s", "10"}) }).Run([]string{"run", "-s", "10"})
} }
@ -343,10 +344,11 @@ func TestParseDestinationString(t *testing.T) {
Destination: &dest, Destination: &dest,
}, },
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if dest != "10" { if dest != "10" {
t.Errorf("expected destination String 10") t.Errorf("expected destination String 10")
} }
return 0
}, },
} }
a.Run([]string{"run", "--dest", "10"}) a.Run([]string{"run", "--dest", "10"})
@ -359,13 +361,14 @@ func TestParseMultiStringFromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, StringFlag{Name: "count, c", EnvVar: "APP_COUNT"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.String("count") != "20" { if ctx.String("count") != "20" {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.String("c") != "20" { if ctx.String("c") != "20" {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
@ -377,13 +380,14 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.String("count") != "20" { if ctx.String("count") != "20" {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.String("c") != "20" { if ctx.String("c") != "20" {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
@ -393,13 +397,14 @@ func TestParseMultiStringSlice(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringSliceFlag{Name: "serve, s", Value: &StringSlice{}}, StringSliceFlag{Name: "serve, s", Value: &StringSlice{}},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
}).Run([]string{"run", "-s", "10", "-s", "20"}) }).Run([]string{"run", "-s", "10", "-s", "20"})
} }
@ -412,13 +417,14 @@ func TestParseMultiStringSliceFromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"}, StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
@ -431,13 +437,14 @@ func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
@ -447,13 +454,14 @@ func TestParseMultiInt(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
IntFlag{Name: "serve, s"}, IntFlag{Name: "serve, s"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Int("serve") != 10 { if ctx.Int("serve") != 10 {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.Int("s") != 10 { if ctx.Int("s") != 10 {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run", "-s", "10"}) a.Run([]string{"run", "-s", "10"})
@ -468,10 +476,11 @@ func TestParseDestinationInt(t *testing.T) {
Destination: &dest, Destination: &dest,
}, },
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if dest != 10 { if dest != 10 {
t.Errorf("expected destination Int 10") t.Errorf("expected destination Int 10")
} }
return 0
}, },
} }
a.Run([]string{"run", "--dest", "10"}) a.Run([]string{"run", "--dest", "10"})
@ -484,13 +493,14 @@ func TestParseMultiIntFromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Int("timeout") != 10 { if ctx.Int("timeout") != 10 {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.Int("t") != 10 { if ctx.Int("t") != 10 {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -503,13 +513,14 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Int("timeout") != 10 { if ctx.Int("timeout") != 10 {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.Int("t") != 10 { if ctx.Int("t") != 10 {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -520,13 +531,14 @@ func TestParseMultiIntSlice(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
IntSliceFlag{Name: "serve, s", Value: &IntSlice{}}, IntSliceFlag{Name: "serve, s", Value: &IntSlice{}},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
}).Run([]string{"run", "-s", "10", "-s", "20"}) }).Run([]string{"run", "-s", "10", "-s", "20"})
} }
@ -539,13 +551,14 @@ func TestParseMultiIntSliceFromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"}, IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
@ -558,13 +571,14 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
}).Run([]string{"run"}) }).Run([]string{"run"})
} }
@ -574,13 +588,14 @@ func TestParseMultiFloat64(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
Float64Flag{Name: "serve, s"}, Float64Flag{Name: "serve, s"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Float64("serve") != 10.2 { if ctx.Float64("serve") != 10.2 {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.Float64("s") != 10.2 { if ctx.Float64("s") != 10.2 {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run", "-s", "10.2"}) a.Run([]string{"run", "-s", "10.2"})
@ -595,10 +610,11 @@ func TestParseDestinationFloat64(t *testing.T) {
Destination: &dest, Destination: &dest,
}, },
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if dest != 10.2 { if dest != 10.2 {
t.Errorf("expected destination Float64 10.2") t.Errorf("expected destination Float64 10.2")
} }
return 0
}, },
} }
a.Run([]string{"run", "--dest", "10.2"}) a.Run([]string{"run", "--dest", "10.2"})
@ -611,13 +627,14 @@ func TestParseMultiFloat64FromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Float64("timeout") != 15.5 { if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.Float64("t") != 15.5 { if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -630,13 +647,14 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Float64("timeout") != 15.5 { if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.Float64("t") != 15.5 { if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -647,13 +665,14 @@ func TestParseMultiBool(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "serve, s"}, BoolFlag{Name: "serve, s"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Bool("serve") != true { if ctx.Bool("serve") != true {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.Bool("s") != true { if ctx.Bool("s") != true {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run", "--serve"}) a.Run([]string{"run", "--serve"})
@ -668,10 +687,11 @@ func TestParseDestinationBool(t *testing.T) {
Destination: &dest, Destination: &dest,
}, },
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if dest != true { if dest != true {
t.Errorf("expected destination Bool true") t.Errorf("expected destination Bool true")
} }
return 0
}, },
} }
a.Run([]string{"run", "--dest"}) a.Run([]string{"run", "--dest"})
@ -684,13 +704,14 @@ func TestParseMultiBoolFromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Bool("debug") != true { if ctx.Bool("debug") != true {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if ctx.Bool("d") != true { if ctx.Bool("d") != true {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -703,13 +724,14 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Bool("debug") != true { if ctx.Bool("debug") != true {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if ctx.Bool("d") != true { if ctx.Bool("d") != true {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -720,13 +742,14 @@ func TestParseMultiBoolT(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolTFlag{Name: "serve, s"}, BoolTFlag{Name: "serve, s"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.BoolT("serve") != true { if ctx.BoolT("serve") != true {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if ctx.BoolT("s") != true { if ctx.BoolT("s") != true {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run", "--serve"}) a.Run([]string{"run", "--serve"})
@ -741,10 +764,11 @@ func TestParseDestinationBoolT(t *testing.T) {
Destination: &dest, Destination: &dest,
}, },
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if dest != true { if dest != true {
t.Errorf("expected destination BoolT true") t.Errorf("expected destination BoolT true")
} }
return 0
}, },
} }
a.Run([]string{"run", "--dest"}) a.Run([]string{"run", "--dest"})
@ -757,13 +781,14 @@ func TestParseMultiBoolTFromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.BoolT("debug") != false { if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if ctx.BoolT("d") != false { if ctx.BoolT("d") != false {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -776,13 +801,14 @@ func TestParseMultiBoolTFromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.BoolT("debug") != false { if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if ctx.BoolT("d") != false { if ctx.BoolT("d") != false {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -811,13 +837,14 @@ func TestParseGeneric(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
GenericFlag{Name: "serve, s", Value: &Parser{}}, GenericFlag{Name: "serve, s", Value: &Parser{}},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
t.Errorf("main name not set") t.Errorf("main name not set")
} }
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
t.Errorf("short name not set") t.Errorf("short name not set")
} }
return 0
}, },
} }
a.Run([]string{"run", "-s", "10,20"}) a.Run([]string{"run", "-s", "10,20"})
@ -830,13 +857,14 @@ func TestParseGenericFromEnv(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) {
t.Errorf("main name not set from env") t.Errorf("main name not set from env")
} }
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
t.Errorf("short name not set from env") t.Errorf("short name not set from env")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})
@ -849,10 +877,11 @@ func TestParseGenericFromEnvCascade(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) {
t.Errorf("value not set from env") t.Errorf("value not set from env")
} }
return 0
}, },
} }
a.Run([]string{"run"}) a.Run([]string{"run"})

18
funcs.go Normal file
View File

@ -0,0 +1,18 @@
package cli
// An action to execute when the bash-completion flag is set
type BashCompleteFn func(*Context)
// An action to execute before any subcommands are run, but after the context is ready
// If a non-nil error is returned, no subcommands are run
type BeforeFn func(*Context) (int, error)
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
type AfterFn func(*Context) (int, error)
// The action to execute when no subcommands are specified
type ActionFn func(*Context) int
// Execute this function if the proper command cannot be found
type CommandNotFoundFn func(*Context, string)

View File

@ -78,13 +78,14 @@ var helpCommand = Command{
Aliases: []string{"h"}, Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command", Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]", ArgsUsage: "[command]",
Action: func(c *Context) { Action: func(c *Context) int {
args := c.Args() args := c.Args()
if args.Present() { if args.Present() {
ShowCommandHelp(c, args.First()) ShowCommandHelp(c, args.First())
} else { } else {
ShowAppHelp(c) ShowAppHelp(c)
} }
return 0
}, },
} }
@ -93,13 +94,14 @@ var helpSubcommand = Command{
Aliases: []string{"h"}, Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command", Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]", ArgsUsage: "[command]",
Action: func(c *Context) { Action: func(c *Context) int {
args := c.Args() args := c.Args()
if args.Present() { if args.Present() {
ShowCommandHelp(c, args.First()) ShowCommandHelp(c, args.First())
} else { } else {
ShowSubcommandHelp(c) ShowSubcommandHelp(c)
} }
return 0
}, },
} }

View File

@ -66,10 +66,11 @@ func Test_Help_Custom_Flags(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "foo, h"}, BoolFlag{Name: "foo, h"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Bool("h") != true { if ctx.Bool("h") != true {
t.Errorf("custom help flag not set") t.Errorf("custom help flag not set")
} }
return 0
}, },
} }
output := new(bytes.Buffer) output := new(bytes.Buffer)
@ -95,10 +96,11 @@ func Test_Version_Custom_Flags(t *testing.T) {
Flags: []Flag{ Flags: []Flag{
BoolFlag{Name: "foo, v"}, BoolFlag{Name: "foo, v"},
}, },
Action: func(ctx *Context) { Action: func(ctx *Context) int {
if ctx.Bool("v") != true { if ctx.Bool("v") != true {
t.Errorf("custom version flag not set") t.Errorf("custom version flag not set")
} }
return 0
}, },
} }
output := new(bytes.Buffer) output := new(bytes.Buffer)