Merge branch 'master' into fix/handle-multierror
This commit is contained in:
commit
70a2c94089
56
CHANGELOG.md
56
CHANGELOG.md
@ -3,14 +3,62 @@
|
|||||||
**ATTN**: This project uses [semantic versioning](http://semver.org/).
|
**ATTN**: This project uses [semantic versioning](http://semver.org/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.19.1] - 2016-11-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixes regression introduced in 1.19.0 where using an `ActionFunc` as
|
||||||
|
the `Action` for a command would cause it to error rather than calling the
|
||||||
|
function. Should not have a affected declarative cases using `func(c
|
||||||
|
*cli.Context) err)`.
|
||||||
|
- Shell completion now handles the case where the user specifies
|
||||||
|
`--generate-bash-completion` immediately after a flag that takes an argument.
|
||||||
|
Previously it call the application with `--generate-bash-completion` as the
|
||||||
|
flag value.
|
||||||
|
|
||||||
|
## [1.19.0] - 2016-11-19
|
||||||
### Added
|
### Added
|
||||||
|
- `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`)
|
||||||
|
- A `Description` field was added to `App` for a more detailed description of
|
||||||
|
the application (similar to the existing `Description` field on `Command`)
|
||||||
- Flag type code generation via `go generate`
|
- Flag type code generation via `go generate`
|
||||||
- Write to stderr and exit 1 if action returns non-nil error
|
- Write to stderr and exit 1 if action returns non-nil error
|
||||||
- Added support for TOML to the `altsrc` loader
|
- Added support for TOML to the `altsrc` loader
|
||||||
|
- `SkipArgReorder` was added to allow users to skip the argument reordering.
|
||||||
|
This is useful if you want to consider all "flags" after an argument as
|
||||||
|
arguments rather than flags (the default behavior of the stdlib `flag`
|
||||||
|
library). This is backported functionality from the [removal of the flag
|
||||||
|
reordering](https://github.com/urfave/cli/pull/398) in the unreleased version
|
||||||
|
2
|
||||||
|
- For formatted errors (those implementing `ErrorFormatter`), the errors will
|
||||||
|
be formatted during output. Compatible with `pkg/errors`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Raise minimum tested/supported Go version to 1.2+
|
- Raise minimum tested/supported Go version to 1.2+
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Consider empty environment variables as set (previously environment variables
|
||||||
|
with the equivalent of `""` would be skipped rather than their value used).
|
||||||
|
- Return an error if the value in a given environment variable cannot be parsed
|
||||||
|
as the flag type. Previously these errors were silently swallowed.
|
||||||
|
- Print full error when an invalid flag is specified (which includes the invalid flag)
|
||||||
|
- `App.Writer` defaults to `stdout` when `nil`
|
||||||
|
- If no action is specified on a command or app, the help is now printed instead of `panic`ing
|
||||||
|
- `App.Metadata` is initialized automatically now (previously was `nil` unless initialized)
|
||||||
|
- Correctly show help message if `-h` is provided to a subcommand
|
||||||
|
- `context.(Global)IsSet` now respects environment variables. Previously it
|
||||||
|
would return `false` if a flag was specified in the environment rather than
|
||||||
|
as an argument
|
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
|
||||||
|
- `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This
|
||||||
|
fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well
|
||||||
|
as `altsrc` where Go would complain that the types didn't match
|
||||||
|
|
||||||
|
## [1.18.1] - 2016-08-28
|
||||||
|
### Fixed
|
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported)
|
||||||
|
|
||||||
## [1.18.0] - 2016-06-27
|
## [1.18.0] - 2016-06-27
|
||||||
### Added
|
### Added
|
||||||
- `./runtests` test runner with coverage tracking by default
|
- `./runtests` test runner with coverage tracking by default
|
||||||
@ -29,6 +77,10 @@
|
|||||||
- No longer swallows `panic`s that occur within the `Action`s themselves when
|
- No longer swallows `panic`s that occur within the `Action`s themselves when
|
||||||
detecting the signature of the `Action` field
|
detecting the signature of the `Action` field
|
||||||
|
|
||||||
|
## [1.17.1] - 2016-08-28
|
||||||
|
### Fixed
|
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
|
||||||
|
|
||||||
## [1.17.0] - 2016-05-09
|
## [1.17.0] - 2016-05-09
|
||||||
### Added
|
### Added
|
||||||
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc`
|
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc`
|
||||||
@ -50,6 +102,10 @@
|
|||||||
- cleanups based on [Go Report Card
|
- cleanups based on [Go Report Card
|
||||||
feedback](https://goreportcard.com/report/github.com/urfave/cli)
|
feedback](https://goreportcard.com/report/github.com/urfave/cli)
|
||||||
|
|
||||||
|
## [1.16.1] - 2016-08-28
|
||||||
|
### Fixed
|
||||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
|
||||||
|
|
||||||
## [1.16.0] - 2016-05-02
|
## [1.16.0] - 2016-05-02
|
||||||
### Added
|
### Added
|
||||||
- `Hidden` field on all flag struct types to omit from generated help text
|
- `Hidden` field on all flag struct types to omit from generated help text
|
||||||
|
33
README.md
33
README.md
@ -455,13 +455,13 @@ error.
|
|||||||
|
|
||||||
Flags for the application and commands are shown in the order they are defined.
|
Flags for the application and commands are shown in the order they are defined.
|
||||||
However, it's possible to sort them from outside this library by using `FlagsByName`
|
However, it's possible to sort them from outside this library by using `FlagsByName`
|
||||||
with `sort`.
|
or `CommandsByName` with `sort`.
|
||||||
|
|
||||||
For example this:
|
For example this:
|
||||||
|
|
||||||
<!-- {
|
<!-- {
|
||||||
"args": ["--help"],
|
"args": ["--help"],
|
||||||
"output": "Load configuration from FILE\n.*Language for the greeting.*"
|
"output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*"
|
||||||
} -->
|
} -->
|
||||||
``` go
|
``` go
|
||||||
package main
|
package main
|
||||||
@ -488,7 +488,27 @@ func main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "complete",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "complete a task on the list",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "add",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "add a task to the list",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
sort.Sort(cli.FlagsByName(app.Flags))
|
sort.Sort(cli.FlagsByName(app.Flags))
|
||||||
|
sort.Sort(cli.CommandsByName(app.Commands))
|
||||||
|
|
||||||
app.Run(os.Args)
|
app.Run(os.Args)
|
||||||
}
|
}
|
||||||
@ -940,16 +960,13 @@ SUPPORT: support@awesometown.example.com
|
|||||||
cli.AppHelpTemplate = `NAME:
|
cli.AppHelpTemplate = `NAME:
|
||||||
{{.Name}} - {{.Usage}}
|
{{.Name}} - {{.Usage}}
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command
|
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||||
[command options]{{end}} {{if
|
|
||||||
.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
|
||||||
{{if len .Authors}}
|
{{if len .Authors}}
|
||||||
AUTHOR(S):
|
AUTHOR:
|
||||||
{{range .Authors}}{{ . }}{{end}}
|
{{range .Authors}}{{ . }}{{end}}
|
||||||
{{end}}{{if .Commands}}
|
{{end}}{{if .Commands}}
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"
|
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||||
}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
|
||||||
GLOBAL OPTIONS:
|
GLOBAL OPTIONS:
|
||||||
{{range .VisibleFlags}}{{.}}
|
{{range .VisibleFlags}}{{.}}
|
||||||
{{end}}{{end}}{{if .Copyright }}
|
{{end}}{{end}}{{if .Copyright }}
|
||||||
|
@ -2,9 +2,9 @@ package altsrc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
@ -237,13 +237,11 @@ func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourc
|
|||||||
func isEnvVarSet(envVars string) bool {
|
func isEnvVarSet(envVars string) bool {
|
||||||
for _, envVar := range strings.Split(envVars, ",") {
|
for _, envVar := range strings.Split(envVars, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if _, ok := syscall.Getenv(envVar); ok {
|
||||||
// TODO: Can't use this for bools as
|
// TODO: Can't use this for bools as
|
||||||
// set means that it was true or false based on
|
// set means that it was true or false based on
|
||||||
// Bool flag type, should work for other types
|
// Bool flag type, should work for other types
|
||||||
if len(envVal) > 0 {
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,13 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.BoolFlag.Apply(set)
|
f.BoolFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped BoolFlag.ApplyWithError
|
||||||
|
func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.BoolFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// BoolTFlag is the flag type that wraps cli.BoolTFlag to allow
|
// BoolTFlag is the flag type that wraps cli.BoolTFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type BoolTFlag struct {
|
type BoolTFlag struct {
|
||||||
@ -46,6 +53,13 @@ func (f *BoolTFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.BoolTFlag.Apply(set)
|
f.BoolTFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped BoolTFlag.ApplyWithError
|
||||||
|
func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.BoolTFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// DurationFlag is the flag type that wraps cli.DurationFlag to allow
|
// DurationFlag is the flag type that wraps cli.DurationFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type DurationFlag struct {
|
type DurationFlag struct {
|
||||||
@ -65,6 +79,13 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.DurationFlag.Apply(set)
|
f.DurationFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped DurationFlag.ApplyWithError
|
||||||
|
func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.DurationFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// Float64Flag is the flag type that wraps cli.Float64Flag to allow
|
// Float64Flag is the flag type that wraps cli.Float64Flag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type Float64Flag struct {
|
type Float64Flag struct {
|
||||||
@ -84,6 +105,13 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) {
|
|||||||
f.Float64Flag.Apply(set)
|
f.Float64Flag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped Float64Flag.ApplyWithError
|
||||||
|
func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.Float64Flag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// GenericFlag is the flag type that wraps cli.GenericFlag to allow
|
// GenericFlag is the flag type that wraps cli.GenericFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type GenericFlag struct {
|
type GenericFlag struct {
|
||||||
@ -103,6 +131,13 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.GenericFlag.Apply(set)
|
f.GenericFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped GenericFlag.ApplyWithError
|
||||||
|
func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.GenericFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// Int64Flag is the flag type that wraps cli.Int64Flag to allow
|
// Int64Flag is the flag type that wraps cli.Int64Flag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type Int64Flag struct {
|
type Int64Flag struct {
|
||||||
@ -122,6 +157,13 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) {
|
|||||||
f.Int64Flag.Apply(set)
|
f.Int64Flag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped Int64Flag.ApplyWithError
|
||||||
|
func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.Int64Flag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// IntFlag is the flag type that wraps cli.IntFlag to allow
|
// IntFlag is the flag type that wraps cli.IntFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type IntFlag struct {
|
type IntFlag struct {
|
||||||
@ -141,6 +183,13 @@ func (f *IntFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.IntFlag.Apply(set)
|
f.IntFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped IntFlag.ApplyWithError
|
||||||
|
func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.IntFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow
|
// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type IntSliceFlag struct {
|
type IntSliceFlag struct {
|
||||||
@ -160,6 +209,13 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.IntSliceFlag.Apply(set)
|
f.IntSliceFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped IntSliceFlag.ApplyWithError
|
||||||
|
func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.IntSliceFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow
|
// Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type Int64SliceFlag struct {
|
type Int64SliceFlag struct {
|
||||||
@ -179,6 +235,13 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.Int64SliceFlag.Apply(set)
|
f.Int64SliceFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped Int64SliceFlag.ApplyWithError
|
||||||
|
func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.Int64SliceFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// StringFlag is the flag type that wraps cli.StringFlag to allow
|
// StringFlag is the flag type that wraps cli.StringFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type StringFlag struct {
|
type StringFlag struct {
|
||||||
@ -198,6 +261,13 @@ func (f *StringFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.StringFlag.Apply(set)
|
f.StringFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped StringFlag.ApplyWithError
|
||||||
|
func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.StringFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow
|
// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type StringSliceFlag struct {
|
type StringSliceFlag struct {
|
||||||
@ -217,6 +287,13 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.StringSliceFlag.Apply(set)
|
f.StringSliceFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped StringSliceFlag.ApplyWithError
|
||||||
|
func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.StringSliceFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// Uint64Flag is the flag type that wraps cli.Uint64Flag to allow
|
// Uint64Flag is the flag type that wraps cli.Uint64Flag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type Uint64Flag struct {
|
type Uint64Flag struct {
|
||||||
@ -236,6 +313,13 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) {
|
|||||||
f.Uint64Flag.Apply(set)
|
f.Uint64Flag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped Uint64Flag.ApplyWithError
|
||||||
|
func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.Uint64Flag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
// UintFlag is the flag type that wraps cli.UintFlag to allow
|
// UintFlag is the flag type that wraps cli.UintFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type UintFlag struct {
|
type UintFlag struct {
|
||||||
@ -254,3 +338,10 @@ func (f *UintFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.set = set
|
f.set = set
|
||||||
f.UintFlag.Apply(set)
|
f.UintFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped UintFlag.ApplyWithError
|
||||||
|
func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.UintFlag.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
36
app.go
36
app.go
@ -145,10 +145,6 @@ func (a *App) Setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.EnableBashCompletion {
|
|
||||||
a.appendFlag(BashCompletionFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideVersion {
|
if !a.HideVersion {
|
||||||
a.appendFlag(VersionFlag)
|
a.appendFlag(VersionFlag)
|
||||||
}
|
}
|
||||||
@ -173,8 +169,20 @@ func (a *App) Setup() {
|
|||||||
func (a *App) Run(arguments []string) (err error) {
|
func (a *App) Run(arguments []string) (err error) {
|
||||||
a.Setup()
|
a.Setup()
|
||||||
|
|
||||||
|
// handle the completion flag separately from the flagset since
|
||||||
|
// completion could be attempted after a flag, but before its value was put
|
||||||
|
// on the command line. this causes the flagset to interpret the completion
|
||||||
|
// flag name as the value of the flag before it which is undesirable
|
||||||
|
// note that we can only do this because the shell autocomplete function
|
||||||
|
// always appends the completion flag at the end of the command
|
||||||
|
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
|
||||||
|
|
||||||
// parse flags
|
// parse flags
|
||||||
set := flagSet(a.Name, a.Flags)
|
set, err := flagSet(a.Name, a.Flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
err = set.Parse(arguments[1:])
|
err = set.Parse(arguments[1:])
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
@ -184,6 +192,7 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
return nerr
|
return nerr
|
||||||
}
|
}
|
||||||
|
context.shellComplete = shellComplete
|
||||||
|
|
||||||
if checkCompletions(context) {
|
if checkCompletions(context) {
|
||||||
return nil
|
return nil
|
||||||
@ -242,6 +251,10 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.Action == nil {
|
||||||
|
a.Action = helpCommand.Action
|
||||||
|
}
|
||||||
|
|
||||||
// Run default Action
|
// Run default Action
|
||||||
err = HandleAction(a.Action, context)
|
err = HandleAction(a.Action, context)
|
||||||
|
|
||||||
@ -283,13 +296,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|||||||
}
|
}
|
||||||
a.Commands = newCmds
|
a.Commands = newCmds
|
||||||
|
|
||||||
// append flags
|
// parse flags
|
||||||
if a.EnableBashCompletion {
|
set, err := flagSet(a.Name, a.Flags)
|
||||||
a.appendFlag(BashCompletionFlag)
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse flags
|
|
||||||
set := flagSet(a.Name, a.Flags)
|
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
err = set.Parse(ctx.Args().Tail())
|
err = set.Parse(ctx.Args().Tail())
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
@ -467,7 +479,9 @@ func (a Author) String() string {
|
|||||||
// it's an ActionFunc or a func with the legacy signature for Action, the func
|
// it's an ActionFunc or a func with the legacy signature for Action, the func
|
||||||
// is run!
|
// is run!
|
||||||
func HandleAction(action interface{}, context *Context) (err error) {
|
func HandleAction(action interface{}, context *Context) (err error) {
|
||||||
if a, ok := action.(func(*Context) error); ok {
|
if a, ok := action.(ActionFunc); ok {
|
||||||
|
return a(context)
|
||||||
|
} else if a, ok := action.(func(*Context) error); ok {
|
||||||
return a(context)
|
return a(context)
|
||||||
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
|
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
|
||||||
a(context)
|
a(context)
|
||||||
|
130
app_test.go
130
app_test.go
@ -178,6 +178,49 @@ func ExampleApp_Run_commandHelp() {
|
|||||||
// This is how we describe describeit the function
|
// This is how we describe describeit the function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleApp_Run_noAction() {
|
||||||
|
app := App{}
|
||||||
|
app.Name = "greet"
|
||||||
|
app.Run([]string{"greet"})
|
||||||
|
// Output:
|
||||||
|
// NAME:
|
||||||
|
// greet
|
||||||
|
//
|
||||||
|
// USAGE:
|
||||||
|
// [global options] command [command options] [arguments...]
|
||||||
|
//
|
||||||
|
// COMMANDS:
|
||||||
|
// help, h Shows a list of commands or help for one command
|
||||||
|
//
|
||||||
|
// GLOBAL OPTIONS:
|
||||||
|
// --help, -h show help
|
||||||
|
// --version, -v print the version
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleApp_Run_subcommandNoAction() {
|
||||||
|
app := App{}
|
||||||
|
app.Name = "greet"
|
||||||
|
app.Commands = []Command{
|
||||||
|
{
|
||||||
|
Name: "describeit",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "use it to see a description",
|
||||||
|
Description: "This is how we describe describeit the function",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Run([]string{"greet", "describeit"})
|
||||||
|
// Output:
|
||||||
|
// NAME:
|
||||||
|
// describeit - use it to see a description
|
||||||
|
//
|
||||||
|
// USAGE:
|
||||||
|
// describeit [arguments...]
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
// This is how we describe describeit the function
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleApp_Run_bashComplete() {
|
func ExampleApp_Run_bashComplete() {
|
||||||
// set args for examples sake
|
// set args for examples sake
|
||||||
os.Args = []string{"greet", "--generate-bash-completion"}
|
os.Args = []string{"greet", "--generate-bash-completion"}
|
||||||
@ -1480,7 +1523,11 @@ func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
|
|||||||
func TestHandleAction_WithNonFuncAction(t *testing.T) {
|
func TestHandleAction_WithNonFuncAction(t *testing.T) {
|
||||||
app := NewApp()
|
app := NewApp()
|
||||||
app.Action = 42
|
app.Action = 42
|
||||||
err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
|
fs, err := flagSet(app.Name, app.Flags)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating FlagSet: %s", err)
|
||||||
|
}
|
||||||
|
err = HandleAction(app.Action, NewContext(app, fs, nil))
|
||||||
|
|
||||||
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")
|
||||||
@ -1504,7 +1551,11 @@ func TestHandleAction_WithNonFuncAction(t *testing.T) {
|
|||||||
func TestHandleAction_WithInvalidFuncSignature(t *testing.T) {
|
func TestHandleAction_WithInvalidFuncSignature(t *testing.T) {
|
||||||
app := NewApp()
|
app := NewApp()
|
||||||
app.Action = func() string { return "" }
|
app.Action = func() string { return "" }
|
||||||
err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
|
fs, err := flagSet(app.Name, app.Flags)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating FlagSet: %s", err)
|
||||||
|
}
|
||||||
|
err = HandleAction(app.Action, NewContext(app, fs, nil))
|
||||||
|
|
||||||
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")
|
||||||
@ -1528,7 +1579,11 @@ func TestHandleAction_WithInvalidFuncSignature(t *testing.T) {
|
|||||||
func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) {
|
func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) {
|
||||||
app := NewApp()
|
app := NewApp()
|
||||||
app.Action = func(_ *Context) (int, error) { return 0, nil }
|
app.Action = func(_ *Context) (int, error) { return 0, nil }
|
||||||
err := HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
|
fs, err := flagSet(app.Name, app.Flags)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating FlagSet: %s", err)
|
||||||
|
}
|
||||||
|
err = HandleAction(app.Action, NewContext(app, fs, nil))
|
||||||
|
|
||||||
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")
|
||||||
@ -1559,5 +1614,72 @@ func TestHandleAction_WithUnknownPanic(t *testing.T) {
|
|||||||
fn(ctx)
|
fn(ctx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
HandleAction(app.Action, NewContext(app, flagSet(app.Name, app.Flags), nil))
|
fs, err := flagSet(app.Name, app.Flags)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating FlagSet: %s", err)
|
||||||
|
}
|
||||||
|
HandleAction(app.Action, NewContext(app, fs, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShellCompletionForIncompleteFlags(t *testing.T) {
|
||||||
|
app := NewApp()
|
||||||
|
app.Flags = []Flag{
|
||||||
|
IntFlag{
|
||||||
|
Name: "test-completion",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.EnableBashCompletion = true
|
||||||
|
app.BashComplete = func(ctx *Context) {
|
||||||
|
for _, command := range ctx.App.Commands {
|
||||||
|
if command.Hidden {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range command.Names() {
|
||||||
|
fmt.Fprintln(ctx.App.Writer, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, flag := range ctx.App.Flags {
|
||||||
|
for _, name := range strings.Split(flag.GetName(), ",") {
|
||||||
|
if name == BashCompletionFlag.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch name = strings.TrimSpace(name); len(name) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
fmt.Fprintln(ctx.App.Writer, "-"+name)
|
||||||
|
default:
|
||||||
|
fmt.Fprintln(ctx.App.Writer, "--"+name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.Action = func(ctx *Context) error {
|
||||||
|
return fmt.Errorf("should not get here")
|
||||||
|
}
|
||||||
|
err := app.Run([]string{"", "--test-completion", "--" + BashCompletionFlag.Name})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("app should not return an error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleActionActuallyWorksWithActions(t *testing.T) {
|
||||||
|
var f ActionFunc
|
||||||
|
called := false
|
||||||
|
f = func(c *Context) error {
|
||||||
|
called = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := HandleAction(f, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Should not have errored: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !called {
|
||||||
|
t.Errorf("Function was not called")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
52
command.go
52
command.go
@ -61,6 +61,20 @@ type Command struct {
|
|||||||
commandNamePath []string
|
commandNamePath []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommandsByName []Command
|
||||||
|
|
||||||
|
func (c CommandsByName) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CommandsByName) Less(i, j int) bool {
|
||||||
|
return c[i].Name < c[j].Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CommandsByName) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
|
|
||||||
// FullName returns the full name of the command.
|
// FullName returns the full name of the command.
|
||||||
// For subcommands this ensures that parent commands are part of the command path
|
// For subcommands this ensures that parent commands are part of the command path
|
||||||
func (c Command) FullName() string {
|
func (c Command) FullName() string {
|
||||||
@ -87,11 +101,10 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.App.EnableBashCompletion {
|
set, err := flagSet(c.Name, c.Flags)
|
||||||
c.Flags = append(c.Flags, BashCompletionFlag)
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
set := flagSet(c.Name, c.Flags)
|
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
if c.SkipFlagParsing {
|
if c.SkipFlagParsing {
|
||||||
@ -132,6 +145,19 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
err = set.Parse(ctx.Args().Tail())
|
err = set.Parse(ctx.Args().Tail())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nerr := normalizeFlags(c.Flags, set)
|
||||||
|
if nerr != nil {
|
||||||
|
fmt.Fprintln(ctx.App.Writer, nerr)
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
ShowCommandHelp(ctx, c.Name)
|
||||||
|
return nerr
|
||||||
|
}
|
||||||
|
|
||||||
|
context := NewContext(ctx.App, set, ctx)
|
||||||
|
if checkCommandCompletions(context, c.Name) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c.OnUsageError != nil {
|
if c.OnUsageError != nil {
|
||||||
err := c.OnUsageError(ctx, err, false)
|
err := c.OnUsageError(ctx, err, false)
|
||||||
@ -144,20 +170,6 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
nerr := normalizeFlags(c.Flags, set)
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(ctx.App.Writer, nerr)
|
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
|
|
||||||
context := NewContext(ctx.App, set, ctx)
|
|
||||||
|
|
||||||
if checkCommandCompletions(context, c.Name) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCommandHelp(context, c.Name) {
|
if checkCommandHelp(context, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -187,6 +199,10 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Action == nil {
|
||||||
|
c.Action = helpSubcommand.Action
|
||||||
|
}
|
||||||
|
|
||||||
context.Command = c
|
context.Command = c
|
||||||
err = HandleAction(c.Action, context)
|
err = HandleAction(c.Action, context)
|
||||||
|
|
||||||
|
18
context.go
18
context.go
@ -3,9 +3,9 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context is a type that is passed through to
|
// Context is a type that is passed through to
|
||||||
@ -15,6 +15,7 @@ import (
|
|||||||
type Context struct {
|
type Context struct {
|
||||||
App *App
|
App *App
|
||||||
Command Command
|
Command Command
|
||||||
|
shellComplete bool
|
||||||
flagSet *flag.FlagSet
|
flagSet *flag.FlagSet
|
||||||
setFlags map[string]bool
|
setFlags map[string]bool
|
||||||
parentContext *Context
|
parentContext *Context
|
||||||
@ -22,7 +23,13 @@ type Context struct {
|
|||||||
|
|
||||||
// NewContext creates a new context. For use in when invoking an App or Command action.
|
// NewContext creates a new context. For use in when invoking an App or Command action.
|
||||||
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
|
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
|
||||||
return &Context{App: app, flagSet: set, parentContext: parentCtx}
|
c := &Context{App: app, flagSet: set, parentContext: parentCtx}
|
||||||
|
|
||||||
|
if parentCtx != nil {
|
||||||
|
c.shellComplete = parentCtx.shellComplete
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumFlags returns the number of flags set
|
// NumFlags returns the number of flags set
|
||||||
@ -91,7 +98,7 @@ func (c *Context) IsSet(name string) bool {
|
|||||||
|
|
||||||
eachName(envVarValue.String(), func(envVar string) {
|
eachName(envVarValue.String(), func(envVar string) {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if _, ok := syscall.Getenv(envVar); ok {
|
||||||
c.setFlags[name] = true
|
c.setFlags[name] = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -147,6 +154,11 @@ func (c *Context) Parent() *Context {
|
|||||||
return c.parentContext
|
return c.parentContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// value returns the value of the flag coressponding to `name`
|
||||||
|
func (c *Context) value(name string) interface{} {
|
||||||
|
return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
|
||||||
|
}
|
||||||
|
|
||||||
// Args contains apps console arguments
|
// Args contains apps console arguments
|
||||||
type Args []string
|
type Args []string
|
||||||
|
|
||||||
|
@ -184,18 +184,30 @@ func TestContext_IsSet(t *testing.T) {
|
|||||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
||||||
// Should be moved to `flag_test` in v2
|
// Should be moved to `flag_test` in v2
|
||||||
func TestContext_IsSet_fromEnv(t *testing.T) {
|
func TestContext_IsSet_fromEnv(t *testing.T) {
|
||||||
var timeoutIsSet, tIsSet, noEnvVarIsSet, nIsSet bool
|
var (
|
||||||
|
timeoutIsSet, tIsSet bool
|
||||||
|
noEnvVarIsSet, nIsSet bool
|
||||||
|
passwordIsSet, pIsSet bool
|
||||||
|
unparsableIsSet, uIsSet bool
|
||||||
|
)
|
||||||
|
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||||
|
os.Setenv("APP_PASSWORD", "")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||||
|
StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"},
|
||||||
|
Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"},
|
||||||
Float64Flag{Name: "no-env-var, n"},
|
Float64Flag{Name: "no-env-var, n"},
|
||||||
},
|
},
|
||||||
Action: func(ctx *Context) error {
|
Action: func(ctx *Context) error {
|
||||||
timeoutIsSet = ctx.IsSet("timeout")
|
timeoutIsSet = ctx.IsSet("timeout")
|
||||||
tIsSet = ctx.IsSet("t")
|
tIsSet = ctx.IsSet("t")
|
||||||
|
passwordIsSet = ctx.IsSet("password")
|
||||||
|
pIsSet = ctx.IsSet("p")
|
||||||
|
unparsableIsSet = ctx.IsSet("unparsable")
|
||||||
|
uIsSet = ctx.IsSet("u")
|
||||||
noEnvVarIsSet = ctx.IsSet("no-env-var")
|
noEnvVarIsSet = ctx.IsSet("no-env-var")
|
||||||
nIsSet = ctx.IsSet("n")
|
nIsSet = ctx.IsSet("n")
|
||||||
return nil
|
return nil
|
||||||
@ -204,8 +216,15 @@ func TestContext_IsSet_fromEnv(t *testing.T) {
|
|||||||
a.Run([]string{"run"})
|
a.Run([]string{"run"})
|
||||||
expect(t, timeoutIsSet, true)
|
expect(t, timeoutIsSet, true)
|
||||||
expect(t, tIsSet, true)
|
expect(t, tIsSet, true)
|
||||||
|
expect(t, passwordIsSet, true)
|
||||||
|
expect(t, pIsSet, true)
|
||||||
expect(t, noEnvVarIsSet, false)
|
expect(t, noEnvVarIsSet, false)
|
||||||
expect(t, nIsSet, false)
|
expect(t, nIsSet, false)
|
||||||
|
|
||||||
|
os.Setenv("APP_UNPARSABLE", "foobar")
|
||||||
|
a.Run([]string{"run"})
|
||||||
|
expect(t, unparsableIsSet, false)
|
||||||
|
expect(t, uIsSet, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContext_GlobalIsSet(t *testing.T) {
|
func TestContext_GlobalIsSet(t *testing.T) {
|
||||||
@ -230,14 +249,22 @@ func TestContext_GlobalIsSet(t *testing.T) {
|
|||||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
||||||
// Should be moved to `flag_test` in v2
|
// Should be moved to `flag_test` in v2
|
||||||
func TestContext_GlobalIsSet_fromEnv(t *testing.T) {
|
func TestContext_GlobalIsSet_fromEnv(t *testing.T) {
|
||||||
var timeoutIsSet, tIsSet, noEnvVarIsSet, nIsSet bool
|
var (
|
||||||
|
timeoutIsSet, tIsSet bool
|
||||||
|
noEnvVarIsSet, nIsSet bool
|
||||||
|
passwordIsSet, pIsSet bool
|
||||||
|
unparsableIsSet, uIsSet bool
|
||||||
|
)
|
||||||
|
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||||
|
os.Setenv("APP_PASSWORD", "")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||||
|
StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"},
|
||||||
Float64Flag{Name: "no-env-var, n"},
|
Float64Flag{Name: "no-env-var, n"},
|
||||||
|
Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"},
|
||||||
},
|
},
|
||||||
Commands: []Command{
|
Commands: []Command{
|
||||||
{
|
{
|
||||||
@ -245,6 +272,10 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) {
|
|||||||
Action: func(ctx *Context) error {
|
Action: func(ctx *Context) error {
|
||||||
timeoutIsSet = ctx.GlobalIsSet("timeout")
|
timeoutIsSet = ctx.GlobalIsSet("timeout")
|
||||||
tIsSet = ctx.GlobalIsSet("t")
|
tIsSet = ctx.GlobalIsSet("t")
|
||||||
|
passwordIsSet = ctx.GlobalIsSet("password")
|
||||||
|
pIsSet = ctx.GlobalIsSet("p")
|
||||||
|
unparsableIsSet = ctx.GlobalIsSet("unparsable")
|
||||||
|
uIsSet = ctx.GlobalIsSet("u")
|
||||||
noEnvVarIsSet = ctx.GlobalIsSet("no-env-var")
|
noEnvVarIsSet = ctx.GlobalIsSet("no-env-var")
|
||||||
nIsSet = ctx.GlobalIsSet("n")
|
nIsSet = ctx.GlobalIsSet("n")
|
||||||
return nil
|
return nil
|
||||||
@ -252,11 +283,22 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
a.Run([]string{"run", "hello"})
|
if err := a.Run([]string{"run", "hello"}); err != nil {
|
||||||
|
t.Logf("error running Run(): %+v", err)
|
||||||
|
}
|
||||||
expect(t, timeoutIsSet, true)
|
expect(t, timeoutIsSet, true)
|
||||||
expect(t, tIsSet, true)
|
expect(t, tIsSet, true)
|
||||||
|
expect(t, passwordIsSet, true)
|
||||||
|
expect(t, pIsSet, true)
|
||||||
expect(t, noEnvVarIsSet, false)
|
expect(t, noEnvVarIsSet, false)
|
||||||
expect(t, nIsSet, false)
|
expect(t, nIsSet, false)
|
||||||
|
|
||||||
|
os.Setenv("APP_UNPARSABLE", "foobar")
|
||||||
|
if err := a.Run([]string{"run"}); err != nil {
|
||||||
|
t.Logf("error running Run(): %+v", err)
|
||||||
|
}
|
||||||
|
expect(t, unparsableIsSet, false)
|
||||||
|
expect(t, uIsSet, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContext_NumFlags(t *testing.T) {
|
func TestContext_NumFlags(t *testing.T) {
|
||||||
|
22
errors.go
22
errors.go
@ -34,6 +34,10 @@ func (m MultiError) Error() string {
|
|||||||
return strings.Join(errs, "\n")
|
return strings.Join(errs, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrorFormatter interface {
|
||||||
|
Format(s fmt.State, verb rune)
|
||||||
|
}
|
||||||
|
|
||||||
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
||||||
// code
|
// code
|
||||||
type ExitCoder interface {
|
type ExitCoder interface {
|
||||||
@ -44,11 +48,11 @@ type ExitCoder interface {
|
|||||||
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
||||||
type ExitError struct {
|
type ExitError struct {
|
||||||
exitCode int
|
exitCode int
|
||||||
message string
|
message interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExitError makes a new *ExitError
|
// NewExitError makes a new *ExitError
|
||||||
func NewExitError(message string, exitCode int) *ExitError {
|
func NewExitError(message interface{}, exitCode int) *ExitError {
|
||||||
return &ExitError{
|
return &ExitError{
|
||||||
exitCode: exitCode,
|
exitCode: exitCode,
|
||||||
message: message,
|
message: message,
|
||||||
@ -58,7 +62,7 @@ func NewExitError(message string, exitCode int) *ExitError {
|
|||||||
// Error returns the string message, fulfilling the interface required by
|
// Error returns the string message, fulfilling the interface required by
|
||||||
// `error`
|
// `error`
|
||||||
func (ee *ExitError) Error() string {
|
func (ee *ExitError) Error() string {
|
||||||
return ee.message
|
return fmt.Sprintf("%v", ee.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExitCode returns the exit code, fulfilling the interface required by
|
// ExitCode returns the exit code, fulfilling the interface required by
|
||||||
@ -78,7 +82,11 @@ func HandleExitCoder(err error) {
|
|||||||
|
|
||||||
if exitErr, ok := err.(ExitCoder); ok {
|
if exitErr, ok := err.(ExitCoder); ok {
|
||||||
if err.Error() != "" {
|
if err.Error() != "" {
|
||||||
fmt.Fprintln(ErrWriter, err)
|
if _, ok := exitErr.(ErrorFormatter); ok {
|
||||||
|
fmt.Fprintf(ErrWriter, "%+v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(ErrWriter, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OsExiter(exitErr.ExitCode())
|
OsExiter(exitErr.ExitCode())
|
||||||
return
|
return
|
||||||
@ -91,7 +99,11 @@ func HandleExitCoder(err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err.Error() != "" {
|
if err.Error() != "" {
|
||||||
fmt.Fprintln(ErrWriter, err)
|
if _, ok := err.(ErrorFormatter); ok {
|
||||||
|
fmt.Fprintf(ErrWriter, "%+v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(ErrWriter, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OsExiter(1)
|
OsExiter(1)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -104,3 +105,53 @@ func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) {
|
|||||||
expect(t, called, true)
|
expect(t, called, true)
|
||||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "")
|
expect(t, ErrWriter.(*bytes.Buffer).String(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make a stub to not import pkg/errors
|
||||||
|
type ErrorWithFormat struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrorWithFormat(m string) *ErrorWithFormat {
|
||||||
|
return &ErrorWithFormat{error: errors.New(m)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ErrorWithFormat) Format(s fmt.State, verb rune) {
|
||||||
|
fmt.Fprintf(s, "This the format: %v", f.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleExitCoder_ErrorWithFormat(t *testing.T) {
|
||||||
|
called := false
|
||||||
|
|
||||||
|
OsExiter = func(rc int) {
|
||||||
|
called = true
|
||||||
|
}
|
||||||
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
OsExiter = fakeOsExiter
|
||||||
|
ErrWriter = fakeErrWriter
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := NewErrorWithFormat("I am formatted")
|
||||||
|
HandleExitCoder(err)
|
||||||
|
|
||||||
|
expect(t, called, true)
|
||||||
|
expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) {
|
||||||
|
called := false
|
||||||
|
|
||||||
|
OsExiter = func(rc int) {
|
||||||
|
called = true
|
||||||
|
}
|
||||||
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
|
defer func() { OsExiter = fakeOsExiter }()
|
||||||
|
|
||||||
|
err := NewMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2"))
|
||||||
|
HandleExitCoder(err)
|
||||||
|
|
||||||
|
expect(t, called, true)
|
||||||
|
expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n")
|
||||||
|
}
|
||||||
|
265
flag.go
265
flag.go
@ -3,11 +3,11 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -62,13 +62,29 @@ type Flag interface {
|
|||||||
GetName() string
|
GetName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
// errorableFlag is an interface that allows us to return errors during apply
|
||||||
|
// it allows flags defined in this library to return errors in a fashion backwards compatible
|
||||||
|
// TODO remove in v2 and modify the existing Flag interface to return errors
|
||||||
|
type errorableFlag interface {
|
||||||
|
Flag
|
||||||
|
|
||||||
|
ApplyWithError(*flag.FlagSet) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
|
||||||
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
||||||
|
|
||||||
for _, f := range flags {
|
for _, f := range flags {
|
||||||
f.Apply(set)
|
//TODO remove in v2 when errorableFlag is removed
|
||||||
|
if ef, ok := f.(errorableFlag); ok {
|
||||||
|
if err := ef.ApplyWithError(set); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f.Apply(set)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return set
|
return set, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func eachName(longName string, fn func(string)) {
|
func eachName(longName string, fn func(string)) {
|
||||||
@ -87,13 +103,22 @@ type Generic interface {
|
|||||||
|
|
||||||
// Apply takes the flagset and calls Set on the generic flag with the value
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||||
// provided by the user for parsing by the flag
|
// provided by the user for parsing by the flag
|
||||||
|
// Ignores parsing errors
|
||||||
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError takes the flagset and calls Set on the generic flag with the value
|
||||||
|
// provided by the user for parsing by the flag
|
||||||
|
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
val := f.Value
|
val := f.Value
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
val.Set(envVal)
|
if err := val.Set(envVal); err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,9 +127,11 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
|
|||||||
eachName(f.Name, func(name string) {
|
eachName(f.Name, func(name string) {
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringSlice is an opaque type for []string to satisfy flag.Value
|
// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
|
||||||
type StringSlice []string
|
type StringSlice []string
|
||||||
|
|
||||||
// Set appends the string value to the list of values
|
// Set appends the string value to the list of values
|
||||||
@ -123,16 +150,29 @@ func (f *StringSlice) Value() []string {
|
|||||||
return *f
|
return *f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns the slice of strings set by this flag
|
||||||
|
func (f *StringSlice) Get() interface{} {
|
||||||
|
return *f
|
||||||
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := &StringSlice{}
|
newVal := &StringSlice{}
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
newVal.Set(s)
|
if err := newVal.Set(s); err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
break
|
break
|
||||||
@ -146,9 +186,11 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntSlice is an opaque type for []int to satisfy flag.Value
|
// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||||
type IntSlice []int
|
type IntSlice []int
|
||||||
|
|
||||||
// Set parses the value into an integer and appends it to the list of values
|
// Set parses the value into an integer and appends it to the list of values
|
||||||
@ -171,18 +213,28 @@ func (f *IntSlice) Value() []int {
|
|||||||
return *f
|
return *f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns the slice of ints set by this flag
|
||||||
|
func (f *IntSlice) Get() interface{} {
|
||||||
|
return *f
|
||||||
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := &IntSlice{}
|
newVal := &IntSlice{}
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
err := newVal.Set(s)
|
if err := newVal.Set(s); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err)
|
||||||
fmt.Fprintf(ErrWriter, err.Error())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
@ -197,9 +249,11 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64Slice is an opaque type for []int to satisfy flag.Value
|
// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||||
type Int64Slice []int64
|
type Int64Slice []int64
|
||||||
|
|
||||||
// Set parses the value into an integer and appends it to the list of values
|
// Set parses the value into an integer and appends it to the list of values
|
||||||
@ -222,18 +276,28 @@ func (f *Int64Slice) Value() []int64 {
|
|||||||
return *f
|
return *f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get returns the slice of ints set by this flag
|
||||||
|
func (f *Int64Slice) Get() interface{} {
|
||||||
|
return *f
|
||||||
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
|
func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := &Int64Slice{}
|
newVal := &Int64Slice{}
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
err := newVal.Set(s)
|
if err := newVal.Set(s); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err)
|
||||||
fmt.Fprintf(ErrWriter, err.Error())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
@ -248,19 +312,33 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
})
|
})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
val := false
|
val := false
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValBool, err := strconv.ParseBool(envVal)
|
if envVal == "" {
|
||||||
if err == nil {
|
val = false
|
||||||
val = envValBool
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envValBool, err := strconv.ParseBool(envVal)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val = envValBool
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,20 +351,35 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Bool(name, val, f.Usage)
|
set.Bool(name, val, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
val := true
|
val := true
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValBool, err := strconv.ParseBool(envVal)
|
if envVal == "" {
|
||||||
if err == nil {
|
val = false
|
||||||
val = envValBool
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envValBool, err := strconv.ParseBool(envVal)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val = envValBool
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,14 +391,22 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Bool(name, val, f.Usage)
|
set.Bool(name, val, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f StringFlag) Apply(set *flag.FlagSet) {
|
func (f StringFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
f.Value = envVal
|
f.Value = envVal
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -319,19 +420,28 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.String(name, f.Value, f.Usage)
|
set.String(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f IntFlag) Apply(set *flag.FlagSet) {
|
func (f IntFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = int(envValInt)
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
f.Value = int(envValInt)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -343,19 +453,29 @@ func (f IntFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Int(name, f.Value, f.Usage)
|
set.Int(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f Int64Flag) Apply(set *flag.FlagSet) {
|
func (f Int64Flag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = envValInt
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = envValInt
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -367,19 +487,29 @@ func (f Int64Flag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Int64(name, f.Value, f.Usage)
|
set.Int64(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f UintFlag) Apply(set *flag.FlagSet) {
|
func (f UintFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = uint(envValInt)
|
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = uint(envValInt)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,19 +521,29 @@ func (f UintFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Uint(name, f.Value, f.Usage)
|
set.Uint(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f Uint64Flag) Apply(set *flag.FlagSet) {
|
func (f Uint64Flag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = uint64(envValInt)
|
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = uint64(envValInt)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,19 +555,29 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Uint64(name, f.Value, f.Usage)
|
set.Uint64(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValDuration, err := time.ParseDuration(envVal)
|
envValDuration, err := time.ParseDuration(envVal)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = envValDuration
|
return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = envValDuration
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -439,18 +589,29 @@ func (f DurationFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Duration(name, f.Value, f.Usage)
|
set.Duration(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVar != "" {
|
if f.EnvVar != "" {
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||||
envVar = strings.TrimSpace(envVar)
|
envVar = strings.TrimSpace(envVar)
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
f.Value = float64(envValFloat)
|
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = float64(envValFloat)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,6 +623,8 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Float64(name, f.Value, f.Usage)
|
set.Float64(name, f.Value, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func visibleFlags(fl []Flag) []Flag {
|
func visibleFlags(fl []Flag) []Flag {
|
||||||
|
111
flag_test.go
111
flag_test.go
@ -29,6 +29,81 @@ func TestBoolFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFlagsFromEnv(t *testing.T) {
|
||||||
|
var flagTests = []struct {
|
||||||
|
input string
|
||||||
|
output interface{}
|
||||||
|
flag Flag
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||||
|
{"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||||
|
{"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||||
|
{"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||||
|
{"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"1s", 1 * time.Second, DurationFlag{Name: "time", EnvVar: "TIME"}, nil},
|
||||||
|
{"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Errorf(`could not parse foobar as duration for flag time: time: invalid duration foobar`)},
|
||||||
|
|
||||||
|
{"1.2", 1.2, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"1", int64(1), Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"1", 1, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"1,2", IntSlice{1, 2}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"1,2", Int64Slice{1, 2}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, nil},
|
||||||
|
|
||||||
|
{"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, nil},
|
||||||
|
|
||||||
|
{"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"1", uint64(1), Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, nil},
|
||||||
|
{"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`)},
|
||||||
|
{"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`)},
|
||||||
|
|
||||||
|
{"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range flagTests {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv(reflect.ValueOf(test.flag).FieldByName("EnvVar").String(), test.input)
|
||||||
|
a := App{
|
||||||
|
Flags: []Flag{test.flag},
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
if !reflect.DeepEqual(ctx.value(test.flag.GetName()), test.output) {
|
||||||
|
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.value(test.flag.GetName()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := a.Run([]string{"run"})
|
||||||
|
if !reflect.DeepEqual(test.err, err) {
|
||||||
|
t.Errorf("expected error %s, got error %s", test.err, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var stringFlagTests = []struct {
|
var stringFlagTests = []struct {
|
||||||
name string
|
name string
|
||||||
usage string
|
usage string
|
||||||
@ -941,6 +1016,38 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) {
|
|||||||
a.Run([]string{"run"})
|
a.Run([]string{"run"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseBoolTFromEnv(t *testing.T) {
|
||||||
|
var boolTFlagTests = []struct {
|
||||||
|
input string
|
||||||
|
output bool
|
||||||
|
}{
|
||||||
|
{"", false},
|
||||||
|
{"1", true},
|
||||||
|
{"false", false},
|
||||||
|
{"true", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range boolTFlagTests {
|
||||||
|
os.Clearenv()
|
||||||
|
os.Setenv("DEBUG", test.input)
|
||||||
|
a := App{
|
||||||
|
Flags: []Flag{
|
||||||
|
BoolTFlag{Name: "debug, d", EnvVar: "DEBUG"},
|
||||||
|
},
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
if ctx.Bool("debug") != test.output {
|
||||||
|
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug"))
|
||||||
|
}
|
||||||
|
if ctx.Bool("d") != test.output {
|
||||||
|
t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
a.Run([]string{"run"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseMultiBoolT(t *testing.T) {
|
func TestParseMultiBoolT(t *testing.T) {
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1036,6 +1143,10 @@ func (p *Parser) String() string {
|
|||||||
return fmt.Sprintf("%s,%s", p[0], p[1])
|
return fmt.Sprintf("%s,%s", p[0], p[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Get() interface{} {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseGeneric(t *testing.T) {
|
func TestParseGeneric(t *testing.T) {
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
|
@ -232,6 +232,13 @@ def _write_altsrc_flag_types(outfile, types):
|
|||||||
f.set = set
|
f.set = set
|
||||||
f.{name}Flag.Apply(set)
|
f.{name}Flag.Apply(set)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped {name}Flag.ApplyWithError
|
||||||
|
func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{
|
||||||
|
f.set = set
|
||||||
|
return f.{name}Flag.ApplyWithError(set)
|
||||||
|
}}
|
||||||
""".format(**typedef))
|
""".format(**typedef))
|
||||||
|
|
||||||
|
|
||||||
|
43
help.go
43
help.go
@ -13,7 +13,7 @@ import (
|
|||||||
// 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 AppHelpTemplate = `NAME:
|
var AppHelpTemplate = `NAME:
|
||||||
{{.Name}} - {{.Usage}}
|
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
||||||
@ -252,20 +252,43 @@ func checkSubcommandHelp(c *Context) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCompletions(c *Context) bool {
|
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
|
||||||
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion {
|
if !a.EnableBashCompletion {
|
||||||
ShowCompletions(c)
|
return false, arguments
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
pos := len(arguments) - 1
|
||||||
|
lastArg := arguments[pos]
|
||||||
|
|
||||||
|
if lastArg != "--"+BashCompletionFlag.Name {
|
||||||
|
return false, arguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, arguments[:pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCompletions(c *Context) bool {
|
||||||
|
if !c.shellComplete {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if args := c.Args(); args.Present() {
|
||||||
|
name := args.First()
|
||||||
|
if cmd := c.App.Command(name); cmd != nil {
|
||||||
|
// let the command handle the completion
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowCompletions(c)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCommandCompletions(c *Context, name string) bool {
|
func checkCommandCompletions(c *Context, name string) bool {
|
||||||
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
|
if !c.shellComplete {
|
||||||
ShowCommandCompletions(c, name)
|
return false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
ShowCommandCompletions(c, name)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
9
helpers_unix_test.go
Normal file
9
helpers_unix_test.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func clearenv() {
|
||||||
|
os.Clearenv()
|
||||||
|
}
|
20
helpers_windows_test.go
Normal file
20
helpers_windows_test.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// os.Clearenv() doesn't actually unset variables on Windows
|
||||||
|
// See: https://github.com/golang/go/issues/17902
|
||||||
|
func clearenv() {
|
||||||
|
for _, s := range os.Environ() {
|
||||||
|
for j := 1; j < len(s); j++ {
|
||||||
|
if s[j] == '=' {
|
||||||
|
keyp, _ := syscall.UTF16PtrFromString(s[0:j])
|
||||||
|
syscall.SetEnvironmentVariable(keyp, nil)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user