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"))
```
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.
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.
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
@ -265,20 +265,20 @@ altsrc.InputSourceContext for their given sources.
Here is a more complete sample of a command using YAML support:
``` go
command := &cli.Command{
Name: "test-cmd",
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(c *cli.Context) {
// Action to run
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}),
cli.StringFlag{Name: "load"}},
}
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c)
command := &cli.Command{
Name: "test-cmd",
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(c *cli.Context) {
// Action to run
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}),
cli.StringFlag{Name: "load"}},
}
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c)
```
### Subcommands
@ -339,19 +339,19 @@ E.g.
```go
...
app.Commands = []cli.Command{
{
Name: "noop",
},
{
Name: "add",
Category: "template",
},
{
Name: "remove",
Category: "template",
},
}
app.Commands = []cli.Command{
{
Name: "noop",
},
{
Name: "add",
Category: "template",
},
{
Name: "remove",
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
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
// 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
func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) func(context *cli.Context) error {
return func(context *cli.Context) error {
func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFn {
return func(context *cli.Context) (int, error) {
inputSource, err := createInputSource()
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
// 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
func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) func(context *cli.Context) error {
return func(context *cli.Context) error {
func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFn {
return func(context *cli.Context) (int, error) {
inputSource, err := createInputSource(context)
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"},
Usage: "this is for testing",
Description: "testing",
Action: func(c *cli.Context) {
Action: func(c *cli.Context) int {
val := c.Int("test")
expect(t, val, 15)
return 0
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}),
cli.StringFlag{Name: "load"}},
}
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c)
_, err := command.Run(c)
expect(t, err, nil)
}
@ -61,9 +62,10 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(c *cli.Context) {
Action: func(c *cli.Context) int {
val := c.Int("test")
expect(t, val, 10)
return 0
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}),
@ -71,7 +73,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
}
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c)
_, err := command.Run(c)
expect(t, err, nil)
}
@ -92,9 +94,10 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(c *cli.Context) {
Action: func(c *cli.Context) int {
val := c.Int("test")
expect(t, val, 7)
return 0
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test"}),
@ -102,7 +105,7 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
}
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c)
_, err := command.Run(c)
expect(t, err, nil)
}
@ -123,9 +126,10 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(c *cli.Context) {
Action: func(c *cli.Context) int {
val := c.Int("test")
expect(t, val, 15)
return 0
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", Value: 7}),
@ -133,7 +137,7 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
}
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c)
_, err := command.Run(c)
expect(t, err, nil)
}
@ -157,16 +161,17 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T
Aliases: []string{"tc"},
Usage: "this is for testing",
Description: "testing",
Action: func(c *cli.Context) {
Action: func(c *cli.Context) int {
val := c.Int("test")
expect(t, val, 11)
return 0
},
Flags: []cli.Flag{
NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}),
cli.StringFlag{Name: "load"}},
}
command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
err := command.Run(c)
_, err := command.Run(c)
expect(t, err, nil)
}

79
app.go
View File

@ -10,6 +10,15 @@ import (
"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
// an app be created with the cli.NewApp() function
type App struct {
@ -38,17 +47,17 @@ type App struct {
// Populate on app startup, only gettable throught method Categories()
categories CommandCategories
// 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
// 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
// It is run even if Action() panics
After func(context *Context) error
After AfterFn
// 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
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.
// 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.
@ -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
func (a *App) Run(arguments []string) (err error) {
func (a *App) Run(arguments []string) (ec int, err error) {
if a.Author != "" || 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 {
fmt.Fprintln(a.Writer, nerr)
ShowAppHelp(context)
return nerr
return DefaultErrorExitCode, nerr
}
if checkCompletions(context) {
return nil
return DefaultSuccessExitCode, nil
}
if err != nil {
if a.OnUsageError != nil {
err := a.OnUsageError(context, err, false)
return err
if err != nil {
return DefaultErrorExitCode, err
}
return DefaultSuccessExitCode, err
} else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowAppHelp(context)
return err
return DefaultErrorExitCode, err
}
}
if !a.HideHelp && checkHelp(context) {
ShowAppHelp(context)
return nil
return DefaultSuccessExitCode, nil
}
if !a.HideVersion && checkVersion(context) {
ShowVersion(context)
return nil
return DefaultSuccessExitCode, nil
}
if a.After != nil {
defer func() {
if afterErr := a.After(context); afterErr != nil {
afterEc, afterErr := a.After(context)
if afterErr != nil {
if err != nil {
err = NewMultiError(err, afterErr)
} else {
err = afterErr
}
}
ec = afterEc
}()
}
if a.Before != nil {
err = a.Before(context)
ec, err = a.Before(context)
if err != nil {
fmt.Fprintf(a.Writer, "%v\n\n", err)
ShowAppHelp(context)
return err
return ec, err
}
}
@ -198,20 +212,19 @@ func (a *App) Run(arguments []string) (err error) {
}
// Run default Action
a.Action(context)
return nil
return a.Action(context), nil
}
// Another entry point to the cli app, takes care of passing arguments and error handling
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)
os.Exit(1)
os.Exit(exitCode)
}
}
// 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
if len(a.Commands) > 0 {
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
@ -251,37 +264,40 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} else {
ShowCommandHelp(ctx, context.Args().First())
}
return nerr
return DefaultErrorExitCode, nerr
}
if checkCompletions(context) {
return nil
return DefaultSuccessExitCode, nil
}
if err != nil {
if a.OnUsageError != nil {
err = a.OnUsageError(context, err, true)
return err
if err != nil {
return DefaultErrorExitCode, err
}
return DefaultSuccessExitCode, err
} else {
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
ShowSubcommandHelp(context)
return err
return DefaultErrorExitCode, err
}
}
if len(a.Commands) > 0 {
if checkSubcommandHelp(context) {
return nil
return DefaultSuccessExitCode, nil
}
} else {
if checkCommandHelp(ctx, context.Args().First()) {
return nil
return DefaultSuccessExitCode, nil
}
}
if a.After != nil {
defer func() {
afterErr := a.After(context)
afterEc, afterErr := a.After(context)
if afterErr != nil {
if err != nil {
err = NewMultiError(err, afterErr)
@ -289,13 +305,14 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
err = afterErr
}
}
ec = afterEc
}()
}
if a.Before != nil {
err := a.Before(context)
ec, err = a.Before(context)
if err != nil {
return err
return ec, err
}
}
@ -309,9 +326,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
}
// Run default Action
a.Action(context)
return nil
return a.Action(context), nil
}
// 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{
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"))
return 0
}
app.UsageText = "app [first_arg] [second_arg]"
app.Author = "Harrison"
@ -58,8 +59,9 @@ func ExampleApp_Run_subcommand() {
Usage: "Name of the person to greet",
},
},
Action: func(c *Context) {
Action: func(c *Context) int {
fmt.Println("Hello,", c.String("name"))
return 0
},
},
},
@ -86,8 +88,9 @@ func ExampleApp_Run_help() {
Aliases: []string{"d"},
Usage: "use it to see a description",
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")
return 0
},
},
}
@ -116,15 +119,17 @@ func ExampleApp_Run_bashComplete() {
Aliases: []string{"d"},
Usage: "use it to see a description",
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")
return 0
},
}, {
Name: "next",
Usage: "next example",
Description: "more stuff to see when generating bash completion",
Action: func(c *Context) {
Action: func(c *Context) int {
fmt.Printf("the next example")
return 0
},
},
}
@ -142,14 +147,17 @@ func TestApp_Run(t *testing.T) {
s := ""
app := NewApp()
app.Action = func(c *Context) {
app.Action = func(c *Context) int {
s = s + c.Args().First()
return 0
}
err := app.Run([]string{"command", "foo"})
ec, err := app.Run([]string{"command", "foo"})
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, ec, 0)
expect(t, s, "foobar")
}
@ -188,9 +196,10 @@ func TestApp_CommandWithArgBeforeFlags(t *testing.T) {
Flags: []Flag{
StringFlag{Name: "option", Value: "", Usage: "some option"},
},
Action: func(c *Context) {
Action: func(c *Context) int {
parsedOption = c.String("option")
firstArg = c.Args().First()
return 0
},
}
app.Commands = []Command{command}
@ -208,8 +217,9 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
a.Commands = []Command{
{
Name: "foo",
Action: func(c *Context) {
Action: func(c *Context) int {
context = c
return 0
},
Flags: []Flag{
StringFlag{
@ -218,7 +228,7 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
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"})
@ -237,9 +247,10 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
Flags: []Flag{
StringFlag{Name: "option", Value: "", Usage: "some option"},
},
Action: func(c *Context) {
Action: func(c *Context) int {
parsedOption = c.String("option")
args = c.Args()
return 0
},
}
app.Commands = []Command{command}
@ -258,8 +269,9 @@ func TestApp_CommandWithDash(t *testing.T) {
app := NewApp()
command := Command{
Name: "cmd",
Action: func(c *Context) {
Action: func(c *Context) int {
args = c.Args()
return 0
},
}
app.Commands = []Command{command}
@ -276,8 +288,9 @@ func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
app := NewApp()
command := Command{
Name: "cmd",
Action: func(c *Context) {
Action: func(c *Context) int {
args = c.Args()
return 0
},
}
app.Commands = []Command{command}
@ -296,8 +309,9 @@ func TestApp_Float64Flag(t *testing.T) {
app.Flags = []Flag{
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")
return 0
}
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"},
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")
parsedStringSlice = c.StringSlice("ip")
parsedOption = c.String("option")
firstArg = c.Args().First()
return 0
},
}
app.Commands = []Command{command}
@ -373,9 +388,10 @@ func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) {
IntSliceFlag{Name: "a", Usage: "set numbers"},
StringSliceFlag{Name: "str", Usage: "set strings"},
},
Action: func(c *Context) {
Action: func(c *Context) int {
parsedIntSlice = c.IntSlice("a")
parsedStringSlice = c.StringSlice("str")
return 0
},
}
app.Commands = []Command{command}
@ -427,7 +443,7 @@ func TestApp_SetStdout(t *testing.T) {
app.Name = "test"
app.Writer = w
err := app.Run([]string{"help"})
_, err := app.Run([]string{"help"})
if err != nil {
t.Fatalf("Run error: %s", err)
@ -445,21 +461,22 @@ func TestApp_BeforeFunc(t *testing.T) {
app := NewApp()
app.Before = func(c *Context) error {
app.Before = func(c *Context) (int, error) {
beforeRun = true
s := c.String("opt")
if s == "fail" {
return beforeError
return DefaultErrorExitCode, beforeError
}
return nil
return DefaultSuccessExitCode, nil
}
app.Commands = []Command{
Command{
Name: "sub",
Action: func(c *Context) {
Action: func(c *Context) int {
subcommandRun = true
return DefaultSuccessExitCode
},
},
}
@ -469,7 +486,7 @@ func TestApp_BeforeFunc(t *testing.T) {
}
// 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 {
t.Fatalf("Run error: %s", err)
@ -483,11 +500,15 @@ func TestApp_BeforeFunc(t *testing.T) {
t.Errorf("Subcommand not executed when expected")
}
if ec != DefaultSuccessExitCode {
t.Errorf("Expected exit code to be %d but got %d", DefaultSuccessExitCode, ec)
}
// reset
beforeRun, subcommandRun = false, false
// 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
if err != beforeError {
@ -502,6 +523,9 @@ func TestApp_BeforeFunc(t *testing.T) {
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) {
@ -511,21 +535,22 @@ func TestApp_AfterFunc(t *testing.T) {
app := NewApp()
app.After = func(c *Context) error {
app.After = func(c *Context) (int, error) {
afterRun = true
s := c.String("opt")
if s == "fail" {
return afterError
return DefaultErrorExitCode, afterError
}
return nil
return DefaultSuccessExitCode, nil
}
app.Commands = []Command{
Command{
Name: "sub",
Action: func(c *Context) {
Action: func(c *Context) int {
subcommandRun = true
return DefaultSuccessExitCode
},
},
}
@ -535,7 +560,7 @@ func TestApp_AfterFunc(t *testing.T) {
}
// 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 {
t.Fatalf("Run error: %s", err)
@ -549,11 +574,15 @@ func TestApp_AfterFunc(t *testing.T) {
t.Errorf("Subcommand not executed when expected")
}
if ec != DefaultSuccessExitCode {
t.Errorf("Expected exit code to be %d but got %d", DefaultSuccessExitCode, ec)
}
// reset
afterRun, subcommandRun = false, false
// 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
if err != afterError {
@ -567,6 +596,10 @@ func TestApp_AfterFunc(t *testing.T) {
if subcommandRun == false {
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) {
@ -579,7 +612,7 @@ func TestAppNoHelpFlag(t *testing.T) {
app := NewApp()
app.Writer = ioutil.Discard
err := app.Run([]string{"test", "-h"})
_, err := app.Run([]string{"test", "-h"})
if err != flag.ErrHelp {
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{
Command{
Name: "bar",
Action: func(c *Context) {
Action: func(c *Context) int {
subcommandRun = true
return 0
},
},
}
@ -655,9 +689,10 @@ func TestGlobalFlag(t *testing.T) {
app.Flags = []Flag{
StringFlag{Name: "global, g", Usage: "global"},
}
app.Action = func(c *Context) {
app.Action = func(c *Context) int {
globalFlag = c.GlobalString("global")
globalFlagSet = c.GlobalIsSet("global")
return 0
}
app.Run([]string{"command", "-g", "foo"})
expect(t, globalFlag, "foo")
@ -683,13 +718,14 @@ func TestGlobalFlagsInSubcommands(t *testing.T) {
Subcommands: []Command{
{
Name: "bar",
Action: func(c *Context) {
Action: func(c *Context) int {
if c.GlobalBool("debug") {
subcommandRun = true
}
if c.GlobalBool("parent") {
parentFlag = true
}
return 0
},
},
},
@ -731,7 +767,7 @@ func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) {
}
app.Commands = []Command{cmd}
err := app.Run(flagSet)
_, err := app.Run(flagSet)
if err != nil {
t.Error(err)
@ -772,7 +808,7 @@ func TestApp_Run_SubcommandFullPath(t *testing.T) {
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"})
_, err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil {
t.Error(err)
}
@ -803,7 +839,7 @@ func TestApp_Run_SubcommandHelpName(t *testing.T) {
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"})
_, err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil {
t.Error(err)
}
@ -834,7 +870,7 @@ func TestApp_Run_CommandHelpName(t *testing.T) {
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "bar", "--help"})
_, err := app.Run([]string{"command", "foo", "bar", "--help"})
if err != nil {
t.Error(err)
}
@ -865,7 +901,7 @@ func TestApp_Run_CommandSubcommandHelpName(t *testing.T) {
}
app.Commands = []Command{cmd}
err := app.Run([]string{"command", "foo", "--help"})
_, err := app.Run([]string{"command", "foo", "--help"})
if err != nil {
t.Error(err)
}
@ -891,11 +927,12 @@ func TestApp_Run_Help(t *testing.T) {
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Writer = buf
app.Action = func(c *Context) {
app.Action = func(c *Context) int {
buf.WriteString("boom I say!")
return 0
}
err := app.Run(args)
_, err := app.Run(args)
if err != nil {
t.Error(err)
}
@ -922,11 +959,12 @@ func TestApp_Run_Version(t *testing.T) {
app.Usage = "make an explosive entrance"
app.Version = "0.1.0"
app.Writer = buf
app.Action = func(c *Context) {
app.Action = func(c *Context) int {
buf.WriteString("boom I say!")
return 0
}
err := app.Run(args)
_, err := app.Run(args)
if err != nil {
t.Error(err)
}
@ -991,11 +1029,11 @@ func TestApp_Run_Categories(t *testing.T) {
func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
app := NewApp()
app.Action = func(c *Context) {}
app.Before = func(c *Context) error { return fmt.Errorf("before error") }
app.After = func(c *Context) error { return fmt.Errorf("after error") }
app.Action = func(c *Context) int { return 0 }
app.Before = func(c *Context) (int, error) { return 1, fmt.Errorf("before 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 {
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") {
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) {
@ -1018,12 +1060,12 @@ func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
},
},
Name: "bar",
Before: func(c *Context) error { return fmt.Errorf("before error") },
After: func(c *Context) error { return fmt.Errorf("after error") },
Before: func(c *Context) (int, error) { return 1, fmt.Errorf("before 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 {
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") {
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) {
@ -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 {
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 {
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
Category string
// 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
// If a non-nil error is returned, no sub-subcommands are run
Before func(context *Context) error
// An action to execute after any subcommands are run, but before the subcommand has finished
Before BeforeFn
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After func(context *Context) error
After AfterFn
// 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.
// 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.
@ -65,7 +65,7 @@ func (c Command) FullName() string {
type Commands []Command
// 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 {
return c.startApp(ctx)
}
@ -126,12 +126,15 @@ func (c Command) Run(ctx *Context) (err error) {
if err != nil {
if c.OnUsageError != nil {
err := c.OnUsageError(ctx, err)
return err
if err != nil {
return DefaultErrorExitCode, err
}
return DefaultSuccessExitCode, err
} else {
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer)
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)
ShowCommandHelp(ctx, c.Name)
return nerr
return DefaultErrorExitCode, nerr
}
context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) {
return nil
return DefaultSuccessExitCode, nil
}
if checkCommandHelp(context, c.Name) {
return nil
return DefaultSuccessExitCode, nil
}
if c.After != nil {
defer func() {
afterErr := c.After(context)
afterEc, afterErr := c.After(context)
if afterErr != nil {
if err != nil {
err = NewMultiError(err, afterErr)
} else {
err = afterErr
}
ec = afterEc
}
}()
}
if c.Before != nil {
err := c.Before(context)
ec, err = c.Before(context)
if err != nil {
fmt.Fprintln(ctx.App.Writer, err)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return err
return ec, err
}
}
context.Command = c
c.Action(context)
return nil
ec = c.Action(context)
return ec, err
}
func (c Command) Names() []string {
@ -200,7 +206,7 @@ func (c Command) HasName(name string) bool {
return false
}
func (c Command) startApp(ctx *Context) error {
func (c Command) startApp(ctx *Context) (int, error) {
app := NewApp()
// set the name and usage

View File

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

View File

@ -4,9 +4,9 @@ import (
"fmt"
"os"
"reflect"
"runtime"
"strings"
"testing"
"runtime"
)
var boolFlagTests = []struct {
@ -64,7 +64,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
expectedSuffix = " [%APP_FOO%]"
}
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%]"
}
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%]"
}
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%]"
}
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%]"
}
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%]"
}
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%]"
}
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{
StringFlag{Name: "serve, s"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.String("serve") != "10" {
t.Errorf("main name not set")
}
if ctx.String("s") != "10" {
t.Errorf("short name not set")
}
return 0
},
}).Run([]string{"run", "-s", "10"})
}
@ -343,10 +344,11 @@ func TestParseDestinationString(t *testing.T) {
Destination: &dest,
},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if dest != "10" {
t.Errorf("expected destination String 10")
}
return 0
},
}
a.Run([]string{"run", "--dest", "10"})
@ -359,13 +361,14 @@ func TestParseMultiStringFromEnv(t *testing.T) {
Flags: []Flag{
StringFlag{Name: "count, c", EnvVar: "APP_COUNT"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
return 0
},
}).Run([]string{"run"})
}
@ -377,13 +380,14 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) {
Flags: []Flag{
StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.String("count") != "20" {
t.Errorf("main name not set")
}
if ctx.String("c") != "20" {
t.Errorf("short name not set")
}
return 0
},
}).Run([]string{"run"})
}
@ -393,13 +397,14 @@ func TestParseMultiStringSlice(t *testing.T) {
Flags: []Flag{
StringSliceFlag{Name: "serve, s", Value: &StringSlice{}},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) {
t.Errorf("short name not set")
}
return 0
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
@ -412,13 +417,14 @@ func TestParseMultiStringSliceFromEnv(t *testing.T) {
Flags: []Flag{
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"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
return 0
},
}).Run([]string{"run"})
}
@ -431,13 +437,14 @@ func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
Flags: []Flag{
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"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) {
t.Errorf("short name not set from env")
}
return 0
},
}).Run([]string{"run"})
}
@ -447,13 +454,14 @@ func TestParseMultiInt(t *testing.T) {
Flags: []Flag{
IntFlag{Name: "serve, s"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.Int("serve") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("s") != 10 {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run", "-s", "10"})
@ -468,10 +476,11 @@ func TestParseDestinationInt(t *testing.T) {
Destination: &dest,
},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if dest != 10 {
t.Errorf("expected destination Int 10")
}
return 0
},
}
a.Run([]string{"run", "--dest", "10"})
@ -484,13 +493,14 @@ func TestParseMultiIntFromEnv(t *testing.T) {
Flags: []Flag{
IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.Int("timeout") != 10 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run"})
@ -503,13 +513,14 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) {
Flags: []Flag{
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 {
t.Errorf("main name not set")
}
if ctx.Int("t") != 10 {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run"})
@ -520,13 +531,14 @@ func TestParseMultiIntSlice(t *testing.T) {
Flags: []Flag{
IntSliceFlag{Name: "serve, s", Value: &IntSlice{}},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) {
t.Errorf("short name not set")
}
return 0
},
}).Run([]string{"run", "-s", "10", "-s", "20"})
}
@ -539,13 +551,14 @@ func TestParseMultiIntSliceFromEnv(t *testing.T) {
Flags: []Flag{
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}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
return 0
},
}).Run([]string{"run"})
}
@ -558,13 +571,14 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
Flags: []Flag{
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}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) {
t.Errorf("short name not set from env")
}
return 0
},
}).Run([]string{"run"})
}
@ -574,13 +588,14 @@ func TestParseMultiFloat64(t *testing.T) {
Flags: []Flag{
Float64Flag{Name: "serve, s"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.Float64("serve") != 10.2 {
t.Errorf("main name not set")
}
if ctx.Float64("s") != 10.2 {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run", "-s", "10.2"})
@ -595,10 +610,11 @@ func TestParseDestinationFloat64(t *testing.T) {
Destination: &dest,
},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if dest != 10.2 {
t.Errorf("expected destination Float64 10.2")
}
return 0
},
}
a.Run([]string{"run", "--dest", "10.2"})
@ -611,13 +627,14 @@ func TestParseMultiFloat64FromEnv(t *testing.T) {
Flags: []Flag{
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.Float64("timeout") != 15.5 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run"})
@ -630,13 +647,14 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
Flags: []Flag{
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 {
t.Errorf("main name not set")
}
if ctx.Float64("t") != 15.5 {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run"})
@ -647,13 +665,14 @@ func TestParseMultiBool(t *testing.T) {
Flags: []Flag{
BoolFlag{Name: "serve, s"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.Bool("serve") != true {
t.Errorf("main name not set")
}
if ctx.Bool("s") != true {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run", "--serve"})
@ -668,10 +687,11 @@ func TestParseDestinationBool(t *testing.T) {
Destination: &dest,
},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if dest != true {
t.Errorf("expected destination Bool true")
}
return 0
},
}
a.Run([]string{"run", "--dest"})
@ -684,13 +704,14 @@ func TestParseMultiBoolFromEnv(t *testing.T) {
Flags: []Flag{
BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
return 0
},
}
a.Run([]string{"run"})
@ -703,13 +724,14 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) {
Flags: []Flag{
BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.Bool("debug") != true {
t.Errorf("main name not set from env")
}
if ctx.Bool("d") != true {
t.Errorf("short name not set from env")
}
return 0
},
}
a.Run([]string{"run"})
@ -720,13 +742,14 @@ func TestParseMultiBoolT(t *testing.T) {
Flags: []Flag{
BoolTFlag{Name: "serve, s"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.BoolT("serve") != true {
t.Errorf("main name not set")
}
if ctx.BoolT("s") != true {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run", "--serve"})
@ -741,10 +764,11 @@ func TestParseDestinationBoolT(t *testing.T) {
Destination: &dest,
},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if dest != true {
t.Errorf("expected destination BoolT true")
}
return 0
},
}
a.Run([]string{"run", "--dest"})
@ -757,13 +781,14 @@ func TestParseMultiBoolTFromEnv(t *testing.T) {
Flags: []Flag{
BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env")
}
if ctx.BoolT("d") != false {
t.Errorf("short name not set from env")
}
return 0
},
}
a.Run([]string{"run"})
@ -776,13 +801,14 @@ func TestParseMultiBoolTFromEnvCascade(t *testing.T) {
Flags: []Flag{
BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if ctx.BoolT("debug") != false {
t.Errorf("main name not set from env")
}
if ctx.BoolT("d") != false {
t.Errorf("short name not set from env")
}
return 0
},
}
a.Run([]string{"run"})
@ -811,13 +837,14 @@ func TestParseGeneric(t *testing.T) {
Flags: []Flag{
GenericFlag{Name: "serve, s", Value: &Parser{}},
},
Action: func(ctx *Context) {
Action: func(ctx *Context) int {
if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) {
t.Errorf("main name not set")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) {
t.Errorf("short name not set")
}
return 0
},
}
a.Run([]string{"run", "-s", "10,20"})
@ -830,13 +857,14 @@ func TestParseGenericFromEnv(t *testing.T) {
Flags: []Flag{
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"}) {
t.Errorf("main name not set from env")
}
if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) {
t.Errorf("short name not set from env")
}
return 0
},
}
a.Run([]string{"run"})
@ -849,10 +877,11 @@ func TestParseGenericFromEnvCascade(t *testing.T) {
Flags: []Flag{
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"}) {
t.Errorf("value not set from env")
}
return 0
},
}
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"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *Context) {
Action: func(c *Context) int {
args := c.Args()
if args.Present() {
ShowCommandHelp(c, args.First())
} else {
ShowAppHelp(c)
}
return 0
},
}
@ -93,13 +94,14 @@ var helpSubcommand = Command{
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *Context) {
Action: func(c *Context) int {
args := c.Args()
if args.Present() {
ShowCommandHelp(c, args.First())
} else {
ShowSubcommandHelp(c)
}
return 0
},
}

View File

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