Merge branch 'master' into fix-altsrc-slice-inputs
This commit is contained in:
commit
beddedeabd
@ -244,11 +244,9 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
app.HelpName = app.Name
|
app.HelpName = app.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Description != "" {
|
app.Usage = c.Usage
|
||||||
app.Usage = c.Description
|
app.Description = c.Description
|
||||||
} else {
|
app.ArgsUsage = c.ArgsUsage
|
||||||
app.Usage = c.Usage
|
|
||||||
}
|
|
||||||
|
|
||||||
// set CommandNotFound
|
// set CommandNotFound
|
||||||
app.CommandNotFound = ctx.App.CommandNotFound
|
app.CommandNotFound = ctx.App.CommandNotFound
|
||||||
@ -264,6 +262,7 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
app.Author = ctx.App.Author
|
app.Author = ctx.App.Author
|
||||||
app.Email = ctx.App.Email
|
app.Email = ctx.App.Email
|
||||||
app.Writer = ctx.App.Writer
|
app.Writer = ctx.App.Writer
|
||||||
|
app.ErrWriter = ctx.App.ErrWriter
|
||||||
|
|
||||||
app.categories = CommandCategories{}
|
app.categories = CommandCategories{}
|
||||||
for _, command := range c.Subcommands {
|
for _, command := range c.Subcommands {
|
||||||
|
@ -153,3 +153,32 @@ func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
|||||||
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) {
|
||||||
|
app := NewApp()
|
||||||
|
app.ErrWriter = ioutil.Discard
|
||||||
|
app.Commands = []Command{
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Usage: "this is for testing",
|
||||||
|
Subcommands: []Command{
|
||||||
|
{
|
||||||
|
Name: "baz",
|
||||||
|
Usage: "this is for testing",
|
||||||
|
Action: func(c *Context) error {
|
||||||
|
if c.App.ErrWriter != ioutil.Discard {
|
||||||
|
return fmt.Errorf("ErrWriter not passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "bar", "baz"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -97,15 +97,6 @@ func HandleExitCoder(err error) {
|
|||||||
OsExiter(code)
|
OsExiter(code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err.Error() != "" {
|
|
||||||
if _, ok := err.(ErrorFormatter); ok {
|
|
||||||
fmt.Fprintf(ErrWriter, "%+v\n", err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(ErrWriter, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OsExiter(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMultiError(multiErr MultiError) int {
|
func handleMultiError(multiErr MultiError) int {
|
||||||
|
@ -67,56 +67,6 @@ func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
|
|||||||
expect(t, called, true)
|
expect(t, called, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleExitCoder_ErrorWithMessage(t *testing.T) {
|
|
||||||
exitCode := 0
|
|
||||||
called := false
|
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
|
||||||
if !called {
|
|
||||||
exitCode = rc
|
|
||||||
called = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ErrWriter = &bytes.Buffer{}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
OsExiter = fakeOsExiter
|
|
||||||
ErrWriter = fakeErrWriter
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := errors.New("gourd havens")
|
|
||||||
HandleExitCoder(err)
|
|
||||||
|
|
||||||
expect(t, exitCode, 1)
|
|
||||||
expect(t, called, true)
|
|
||||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "gourd havens\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) {
|
|
||||||
exitCode := 0
|
|
||||||
called := false
|
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
|
||||||
if !called {
|
|
||||||
exitCode = rc
|
|
||||||
called = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ErrWriter = &bytes.Buffer{}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
OsExiter = fakeOsExiter
|
|
||||||
ErrWriter = fakeErrWriter
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := errors.New("")
|
|
||||||
HandleExitCoder(err)
|
|
||||||
|
|
||||||
expect(t, exitCode, 1)
|
|
||||||
expect(t, called, true)
|
|
||||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a stub to not import pkg/errors
|
// make a stub to not import pkg/errors
|
||||||
type ErrorWithFormat struct {
|
type ErrorWithFormat struct {
|
||||||
error
|
error
|
||||||
@ -145,7 +95,7 @@ func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
|
|||||||
ErrWriter = fakeErrWriter
|
ErrWriter = fakeErrWriter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := NewErrorWithFormat("I am formatted")
|
err := NewExitError(NewErrorWithFormat("I am formatted"), 1)
|
||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
|
|
||||||
expect(t, called, true)
|
expect(t, called, true)
|
||||||
|
92
flag_test.go
92
flag_test.go
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -31,57 +32,57 @@ func TestBoolFlagHelpOutput(t *testing.T) {
|
|||||||
|
|
||||||
func TestFlagsFromEnv(t *testing.T) {
|
func TestFlagsFromEnv(t *testing.T) {
|
||||||
var flagTests = []struct {
|
var flagTests = []struct {
|
||||||
input string
|
input string
|
||||||
output interface{}
|
output interface{}
|
||||||
flag Flag
|
flag Flag
|
||||||
err error
|
errRegexp string
|
||||||
}{
|
}{
|
||||||
{"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
{"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""},
|
||||||
{"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
{"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""},
|
||||||
{"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
{"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""},
|
||||||
{"foobar", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Errorf(`could not parse foobar as bool value for flag debug: strconv.ParseBool: parsing "foobar": invalid syntax`)},
|
{"foobar", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)},
|
||||||
|
|
||||||
{"", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
{"", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""},
|
||||||
{"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
{"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""},
|
||||||
{"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
{"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""},
|
||||||
{"foobar", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Errorf(`could not parse foobar as bool value for flag debug: strconv.ParseBool: parsing "foobar": invalid syntax`)},
|
{"foobar", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)},
|
||||||
|
|
||||||
{"1s", 1 * time.Second, DurationFlag{Name: "time", EnvVar: "TIME"}, nil},
|
{"1s", 1 * time.Second, DurationFlag{Name: "time", EnvVar: "TIME"}, ""},
|
||||||
{"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Errorf(`could not parse foobar as duration for flag time: time: invalid duration foobar`)},
|
{"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Sprintf(`could not parse foobar as duration for flag time: .*`)},
|
||||||
|
|
||||||
{"1.2", 1.2, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1.2", 1.2, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"foobar", 0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as float64 value for flag seconds: strconv.ParseFloat: parsing "foobar": invalid syntax`)},
|
{"foobar", 0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as float64 value for flag seconds: .*`)},
|
||||||
|
|
||||||
{"1", int64(1), Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1", int64(1), Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"1.2", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as int value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
{"1.2", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)},
|
||||||
{"foobar", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
{"foobar", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)},
|
||||||
|
|
||||||
{"1", 1, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1", 1, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"1.2", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as int value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
{"1.2", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)},
|
||||||
{"foobar", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
{"foobar", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)},
|
||||||
|
|
||||||
{"1,2", IntSlice{1, 2}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1,2", IntSlice{1, 2}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"1.2,2", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2,2 as int slice value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
{"1.2,2", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int slice value for flag seconds: .*`)},
|
||||||
{"foobar", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int slice value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
{"foobar", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int slice value for flag seconds: .*`)},
|
||||||
|
|
||||||
{"1,2", Int64Slice{1, 2}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1,2", Int64Slice{1, 2}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"1.2,2", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2,2 as int64 slice value for flag seconds: strconv.ParseInt: parsing "1.2": invalid syntax`)},
|
{"1.2,2", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int64 slice value for flag seconds: .*`)},
|
||||||
{"foobar", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as int64 slice value for flag seconds: strconv.ParseInt: parsing "foobar": invalid syntax`)},
|
{"foobar", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int64 slice value for flag seconds: .*`)},
|
||||||
|
|
||||||
{"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, nil},
|
{"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, ""},
|
||||||
|
|
||||||
{"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, nil},
|
{"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, ""},
|
||||||
|
|
||||||
{"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"1.2", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as uint value for flag seconds: strconv.ParseUint: parsing "1.2": invalid syntax`)},
|
{"1.2", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint value for flag seconds: .*`)},
|
||||||
{"foobar", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as uint value for flag seconds: strconv.ParseUint: parsing "foobar": invalid syntax`)},
|
{"foobar", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint value for flag seconds: .*`)},
|
||||||
|
|
||||||
{"1", uint64(1), Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
{"1", uint64(1), Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""},
|
||||||
{"1.2", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse 1.2 as uint64 value for flag seconds: strconv.ParseUint: parsing "1.2": invalid syntax`)},
|
{"1.2", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint64 value for flag seconds: .*`)},
|
||||||
{"foobar", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Errorf(`could not parse foobar as uint64 value for flag seconds: strconv.ParseUint: parsing "foobar": invalid syntax`)},
|
{"foobar", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint64 value for flag seconds: .*`)},
|
||||||
|
|
||||||
{"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, nil},
|
{"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range flagTests {
|
for _, test := range flagTests {
|
||||||
@ -98,8 +99,19 @@ func TestFlagsFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := a.Run([]string{"run"})
|
err := a.Run([]string{"run"})
|
||||||
if !reflect.DeepEqual(test.err, err) {
|
|
||||||
t.Errorf("expected error %s, got error %s", test.err, err)
|
if test.errRegexp != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error to match %s, got none", test.errRegexp)
|
||||||
|
} else {
|
||||||
|
if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched {
|
||||||
|
t.Errorf("expected error to match %s, got error %s", test.errRegexp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil && test.errRegexp == "" {
|
||||||
|
t.Errorf("expected no error got %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
help.go
2
help.go
@ -64,7 +64,7 @@ OPTIONS:
|
|||||||
// cli.go uses text/template to render templates. You can
|
// cli.go uses text/template to render templates. You can
|
||||||
// render custom help text by setting this variable.
|
// render custom help text by setting this variable.
|
||||||
var SubcommandHelpTemplate = `NAME:
|
var SubcommandHelpTemplate = `NAME:
|
||||||
{{.HelpName}} - {{.Usage}}
|
{{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||||
|
Loading…
Reference in New Issue
Block a user