commit
50a0fdeea6
36
.travis.yml
36
.travis.yml
@ -1,35 +1,23 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
|
dist: trusty
|
||||||
|
osx_image: xcode8.3
|
||||||
|
go: 1.8.x
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.2.x
|
|
||||||
- 1.3.x
|
|
||||||
- 1.4.2
|
|
||||||
- 1.5.x
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- master
|
|
||||||
|
|
||||||
env: pip_install="pip install --user"
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: master
|
|
||||||
include:
|
|
||||||
- go: 1.6.x
|
|
||||||
os: osx
|
|
||||||
env: pip_install="sudo pip install"
|
|
||||||
- go: 1.7.x
|
|
||||||
os: osx
|
|
||||||
env: pip_install="sudo pip install"
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- $pip_install flake8
|
- if [[ $(uname) == Darwin ]]; then
|
||||||
|
sudo pip install flake8;
|
||||||
|
else
|
||||||
|
pip install --user flake8;
|
||||||
|
fi
|
||||||
- mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave
|
- mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave
|
||||||
- rm -rvf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2
|
- rm -rvf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2
|
||||||
- rm -rvf ${GOPATH%%:*}/pkg/*/gopkg.in/urfave/cli.v2.a
|
- rm -rvf ${GOPATH%%:*}/pkg/*/gopkg.in/urfave/cli.v2.a
|
||||||
|
65
CHANGELOG.md
65
CHANGELOG.md
@ -34,13 +34,70 @@
|
|||||||
|
|
||||||
## [Unreleased] - (1.x series)
|
## [Unreleased] - (1.x series)
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
## [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
|
||||||
|
- `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
|
||||||
@ -59,6 +116,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`
|
||||||
@ -80,6 +141,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
|
||||||
|
32
README.md
32
README.md
@ -461,13 +461,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
|
||||||
@ -492,9 +492,28 @@ func main() {
|
|||||||
Usage: "Load configuration from `FILE`",
|
Usage: "Load configuration from `FILE`",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
@ -1001,16 +1020,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,8 +2,8 @@ package altsrc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v2"
|
"gopkg.in/urfave/cli.v2"
|
||||||
)
|
)
|
||||||
@ -217,13 +217,11 @@ func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourc
|
|||||||
|
|
||||||
func isEnvVarSet(envVars []string) bool {
|
func isEnvVarSet(envVars []string) bool {
|
||||||
for _, envVar := range envVars {
|
for _, envVar := range envVars {
|
||||||
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)
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
@ -46,6 +53,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 {
|
||||||
@ -65,6 +79,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 {
|
||||||
@ -84,6 +105,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 {
|
||||||
@ -103,6 +131,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 {
|
||||||
@ -122,6 +157,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 {
|
||||||
@ -141,6 +183,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 {
|
||||||
@ -160,6 +209,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)
|
||||||
|
}
|
||||||
|
|
||||||
// Float64SliceFlag is the flag type that wraps cli.Float64SliceFlag to allow
|
// Float64SliceFlag is the flag type that wraps cli.Float64SliceFlag to allow
|
||||||
// for other values to be specified
|
// for other values to be specified
|
||||||
type Float64SliceFlag struct {
|
type Float64SliceFlag struct {
|
||||||
@ -179,6 +235,13 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
f.Float64SliceFlag.Apply(set)
|
f.Float64SliceFlag.Apply(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||||
|
// wrapped Float64SliceFlag.ApplyWithError
|
||||||
|
func (f *Float64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
|
f.set = set
|
||||||
|
return f.Float64SliceFlag.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)
|
||||||
|
}
|
||||||
|
@ -63,7 +63,7 @@ func TestStringSliceApplyInputSourceValue(t *testing.T) {
|
|||||||
c := runTest(t, testApplyInputSource{
|
c := runTest(t, testApplyInputSource{
|
||||||
Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}),
|
Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}),
|
||||||
FlagName: "test",
|
FlagName: "test",
|
||||||
MapValue: []string{"hello", "world"},
|
MapValue: []interface{}{"hello", "world"},
|
||||||
})
|
})
|
||||||
expect(t, c.StringSlice("test"), []string{"hello", "world"})
|
expect(t, c.StringSlice("test"), []string{"hello", "world"})
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
|||||||
c := runTest(t, testApplyInputSource{
|
c := runTest(t, testApplyInputSource{
|
||||||
Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}),
|
Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}),
|
||||||
FlagName: "test",
|
FlagName: "test",
|
||||||
MapValue: []string{"hello", "world"},
|
MapValue: []interface{}{"hello", "world"},
|
||||||
ContextValueString: "ohno",
|
ContextValueString: "ohno",
|
||||||
})
|
})
|
||||||
expect(t, c.StringSlice("test"), []string{"ohno"})
|
expect(t, c.StringSlice("test"), []string{"ohno"})
|
||||||
@ -82,7 +82,7 @@ func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|||||||
c := runTest(t, testApplyInputSource{
|
c := runTest(t, testApplyInputSource{
|
||||||
Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}),
|
Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}),
|
||||||
FlagName: "test",
|
FlagName: "test",
|
||||||
MapValue: []string{"hello", "world"},
|
MapValue: []interface{}{"hello", "world"},
|
||||||
EnvVarName: "TEST",
|
EnvVarName: "TEST",
|
||||||
EnvVarValue: "oh,no",
|
EnvVarValue: "oh,no",
|
||||||
})
|
})
|
||||||
@ -93,7 +93,7 @@ func TestIntSliceApplyInputSourceValue(t *testing.T) {
|
|||||||
c := runTest(t, testApplyInputSource{
|
c := runTest(t, testApplyInputSource{
|
||||||
Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}),
|
Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}),
|
||||||
FlagName: "test",
|
FlagName: "test",
|
||||||
MapValue: []int{1, 2},
|
MapValue: []interface{}{1, 2},
|
||||||
})
|
})
|
||||||
expect(t, c.IntSlice("test"), []int{1, 2})
|
expect(t, c.IntSlice("test"), []int{1, 2})
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) {
|
|||||||
c := runTest(t, testApplyInputSource{
|
c := runTest(t, testApplyInputSource{
|
||||||
Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}),
|
Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}),
|
||||||
FlagName: "test",
|
FlagName: "test",
|
||||||
MapValue: []int{1, 2},
|
MapValue: []interface{}{1, 2},
|
||||||
ContextValueString: "3",
|
ContextValueString: "3",
|
||||||
})
|
})
|
||||||
expect(t, c.IntSlice("test"), []int{3})
|
expect(t, c.IntSlice("test"), []int{3})
|
||||||
@ -112,7 +112,7 @@ func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
|||||||
c := runTest(t, testApplyInputSource{
|
c := runTest(t, testApplyInputSource{
|
||||||
Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}),
|
Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}),
|
||||||
FlagName: "test",
|
FlagName: "test",
|
||||||
MapValue: []int{1, 2},
|
MapValue: []interface{}{1, 2},
|
||||||
EnvVarName: "TEST",
|
EnvVarName: "TEST",
|
||||||
EnvVarValue: "3,4",
|
EnvVarValue: "3,4",
|
||||||
})
|
})
|
||||||
|
@ -130,45 +130,59 @@ func (fsm *MapInputSource) String(name string) (string, error) {
|
|||||||
// StringSlice returns an []string from the map if it exists otherwise returns nil
|
// StringSlice returns an []string from the map if it exists otherwise returns nil
|
||||||
func (fsm *MapInputSource) StringSlice(name string) ([]string, error) {
|
func (fsm *MapInputSource) StringSlice(name string) ([]string, error) {
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
otherGenericValue, exists := fsm.valueMap[name]
|
||||||
if exists {
|
if !exists {
|
||||||
otherValue, isType := otherGenericValue.([]string)
|
otherGenericValue, exists = nestedVal(name, fsm.valueMap)
|
||||||
if !isType {
|
if !exists {
|
||||||
return nil, incorrectTypeForFlagError(name, "[]string", otherGenericValue)
|
return nil, nil
|
||||||
}
|
}
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.([]string)
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(name, "[]string", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
otherValue, isType := otherGenericValue.([]interface{})
|
||||||
|
if !isType {
|
||||||
|
return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringSlice = make([]string, 0, len(otherValue))
|
||||||
|
for i, v := range otherValue {
|
||||||
|
stringValue, isType := v.(string)
|
||||||
|
|
||||||
|
if !isType {
|
||||||
|
return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "string", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
stringSlice = append(stringSlice, stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringSlice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntSlice returns an []int from the map if it exists otherwise returns nil
|
// IntSlice returns an []int from the map if it exists otherwise returns nil
|
||||||
func (fsm *MapInputSource) IntSlice(name string) ([]int, error) {
|
func (fsm *MapInputSource) IntSlice(name string) ([]int, error) {
|
||||||
otherGenericValue, exists := fsm.valueMap[name]
|
otherGenericValue, exists := fsm.valueMap[name]
|
||||||
if exists {
|
if !exists {
|
||||||
otherValue, isType := otherGenericValue.([]int)
|
otherGenericValue, exists = nestedVal(name, fsm.valueMap)
|
||||||
if !isType {
|
if !exists {
|
||||||
return nil, incorrectTypeForFlagError(name, "[]int", otherGenericValue)
|
return nil, nil
|
||||||
}
|
}
|
||||||
return otherValue, nil
|
|
||||||
}
|
|
||||||
nestedGenericValue, exists := nestedVal(name, fsm.valueMap)
|
|
||||||
if exists {
|
|
||||||
otherValue, isType := nestedGenericValue.([]int)
|
|
||||||
if !isType {
|
|
||||||
return nil, incorrectTypeForFlagError(name, "[]int", nestedGenericValue)
|
|
||||||
}
|
|
||||||
return otherValue, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
otherValue, isType := otherGenericValue.([]interface{})
|
||||||
|
if !isType {
|
||||||
|
return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
var intSlice = make([]int, 0, len(otherValue))
|
||||||
|
for i, v := range otherValue {
|
||||||
|
intValue, isType := v.(int)
|
||||||
|
|
||||||
|
if !isType {
|
||||||
|
return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "int", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
intSlice = append(intSlice, intValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return intSlice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic returns an cli.Generic from the map if it exists otherwise returns nil
|
// Generic returns an cli.Generic from the map if it exists otherwise returns nil
|
||||||
|
@ -57,8 +57,8 @@ func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) {
|
|||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case reflect.Array:
|
case reflect.Array, reflect.Slice:
|
||||||
fallthrough // [todo] - Support array type
|
ret[key] = val.([]interface{})
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind())
|
return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind())
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/urfave/cli.v2"
|
"gopkg.in/urfave/cli.v2"
|
||||||
|
|
||||||
@ -78,6 +80,12 @@ func loadDataFrom(filePath string) ([]byte, error) {
|
|||||||
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
|
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
|
||||||
}
|
}
|
||||||
return ioutil.ReadFile(filePath)
|
return ioutil.ReadFile(filePath)
|
||||||
|
} else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") {
|
||||||
|
// on Windows systems u.Path is always empty, so we need to check the string directly.
|
||||||
|
if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil {
|
||||||
|
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
|
||||||
|
}
|
||||||
|
return ioutil.ReadFile(filePath)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
|
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
|
||||||
}
|
}
|
||||||
|
36
app.go
36
app.go
@ -65,6 +65,12 @@ type App struct {
|
|||||||
ErrWriter io.Writer
|
ErrWriter io.Writer
|
||||||
// Other custom info
|
// Other custom info
|
||||||
Metadata map[string]interface{}
|
Metadata map[string]interface{}
|
||||||
|
// Carries a function which returns app specific info.
|
||||||
|
ExtraInfo func() map[string]string
|
||||||
|
// CustomAppHelpTemplate the text template for app help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
CustomAppHelpTemplate string
|
||||||
|
|
||||||
didSetup bool
|
didSetup bool
|
||||||
}
|
}
|
||||||
@ -156,6 +162,10 @@ func (a *App) Setup() {
|
|||||||
if a.Metadata == nil {
|
if a.Metadata == nil {
|
||||||
a.Metadata = make(map[string]interface{})
|
a.Metadata = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.Writer == nil {
|
||||||
|
a.Writer = os.Stdout
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
||||||
@ -163,8 +173,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)
|
||||||
@ -174,6 +196,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
|
||||||
@ -223,7 +246,6 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
if a.Before != nil {
|
if a.Before != nil {
|
||||||
beforeErr := a.Before(context)
|
beforeErr := a.Before(context)
|
||||||
if beforeErr != nil {
|
if beforeErr != nil {
|
||||||
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
|
|
||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
HandleExitCoder(beforeErr)
|
HandleExitCoder(beforeErr)
|
||||||
err = beforeErr
|
err = beforeErr
|
||||||
@ -240,6 +262,10 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.Action == nil {
|
||||||
|
a.Action = helpCommand.Action
|
||||||
|
}
|
||||||
|
|
||||||
// Run default Action
|
// Run default Action
|
||||||
err = a.Action(context)
|
err = a.Action(context)
|
||||||
|
|
||||||
@ -278,7 +304,11 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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(ctx.Args().Tail())
|
err = set.Parse(ctx.Args().Tail())
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
|
288
app_test.go
288
app_test.go
@ -181,9 +181,59 @@ 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 - A new cli application
|
||||||
|
//
|
||||||
|
// USAGE:
|
||||||
|
// greet [global options] command [command options] [arguments...]
|
||||||
|
//
|
||||||
|
// VERSION:
|
||||||
|
// 0.0.0
|
||||||
|
//
|
||||||
|
// COMMANDS:
|
||||||
|
// help, h Shows a list of commands or help for one command
|
||||||
|
//
|
||||||
|
// GLOBAL OPTIONS:
|
||||||
|
// --help, -h show help (default: false)
|
||||||
|
// --version, -v print the version (default: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleApp_Run_subcommandNoAction() {
|
||||||
|
app := &App{
|
||||||
|
Name: "greet",
|
||||||
|
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:
|
||||||
|
// greet describeit - use it to see a description
|
||||||
|
//
|
||||||
|
// USAGE:
|
||||||
|
// greet describeit [command options] [arguments...]
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
// This is how we describe describeit the function
|
||||||
|
//
|
||||||
|
// OPTIONS:
|
||||||
|
// --help, -h show help (default: false)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleApp_Run_shellComplete() {
|
func ExampleApp_Run_shellComplete() {
|
||||||
// set args for examples sake
|
// set args for examples sake
|
||||||
os.Args = []string{"greet", "--generate-completion"}
|
os.Args = []string{"greet", fmt.Sprintf("--%s", genCompName())}
|
||||||
|
|
||||||
app := &App{
|
app := &App{
|
||||||
Name: "greet",
|
Name: "greet",
|
||||||
@ -249,12 +299,11 @@ var commandAppTests = []struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestApp_Command(t *testing.T) {
|
func TestApp_Command(t *testing.T) {
|
||||||
app := &App{}
|
app := &App{
|
||||||
fooCommand := &Command{Name: "foobar", Aliases: []string{"f"}}
|
Commands: []*Command{
|
||||||
batCommand := &Command{Name: "batbaz", Aliases: []string{"b"}}
|
{Name: "foobar", Aliases: []string{"f"}},
|
||||||
app.Commands = []*Command{
|
{Name: "batbaz", Aliases: []string{"b"}},
|
||||||
fooCommand,
|
},
|
||||||
batCommand,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range commandAppTests {
|
for _, test := range commandAppTests {
|
||||||
@ -262,6 +311,12 @@ func TestApp_Command(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApp_Setup_defaultsWriter(t *testing.T) {
|
||||||
|
app := &App{}
|
||||||
|
app.Setup()
|
||||||
|
expect(t, app.Writer, os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
|
func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
|
||||||
var context *Context
|
var context *Context
|
||||||
|
|
||||||
@ -312,19 +367,21 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
|||||||
var parsedOption string
|
var parsedOption string
|
||||||
var args Args
|
var args Args
|
||||||
|
|
||||||
app := &App{}
|
app := &App{
|
||||||
command := &Command{
|
Commands: []*Command{
|
||||||
Name: "cmd",
|
{
|
||||||
Flags: []Flag{
|
Name: "cmd",
|
||||||
&StringFlag{Name: "option", Value: "", Usage: "some option"},
|
Flags: []Flag{
|
||||||
},
|
&StringFlag{Name: "option", Value: "", Usage: "some option"},
|
||||||
Action: func(c *Context) error {
|
},
|
||||||
parsedOption = c.String("option")
|
Action: func(c *Context) error {
|
||||||
args = c.Args()
|
parsedOption = c.String("option")
|
||||||
return nil
|
args = c.Args()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Commands = []*Command{command}
|
|
||||||
|
|
||||||
app.Run([]string{"", "cmd", "--option", "my-option", "my-arg", "--", "--notARealFlag"})
|
app.Run([]string{"", "cmd", "--option", "my-option", "my-arg", "--", "--notARealFlag"})
|
||||||
|
|
||||||
@ -337,15 +394,17 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
|||||||
func TestApp_CommandWithDash(t *testing.T) {
|
func TestApp_CommandWithDash(t *testing.T) {
|
||||||
var args Args
|
var args Args
|
||||||
|
|
||||||
app := &App{}
|
app := &App{
|
||||||
command := &Command{
|
Commands: []*Command{
|
||||||
Name: "cmd",
|
{
|
||||||
Action: func(c *Context) error {
|
Name: "cmd",
|
||||||
args = c.Args()
|
Action: func(c *Context) error {
|
||||||
return nil
|
args = c.Args()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Commands = []*Command{command}
|
|
||||||
|
|
||||||
app.Run([]string{"", "cmd", "my-arg", "-"})
|
app.Run([]string{"", "cmd", "my-arg", "-"})
|
||||||
|
|
||||||
@ -356,15 +415,17 @@ func TestApp_CommandWithDash(t *testing.T) {
|
|||||||
func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
|
func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
|
||||||
var args Args
|
var args Args
|
||||||
|
|
||||||
app := &App{}
|
app := &App{
|
||||||
command := &Command{
|
Commands: []*Command{
|
||||||
Name: "cmd",
|
{
|
||||||
Action: func(c *Context) error {
|
Name: "cmd",
|
||||||
args = c.Args()
|
Action: func(c *Context) error {
|
||||||
return nil
|
args = c.Args()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Commands = []*Command{command}
|
|
||||||
|
|
||||||
app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
|
app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
|
||||||
|
|
||||||
@ -446,22 +507,24 @@ func TestApp_ParseSliceFlags(t *testing.T) {
|
|||||||
var parsedIntSlice []int
|
var parsedIntSlice []int
|
||||||
var parsedStringSlice []string
|
var parsedStringSlice []string
|
||||||
|
|
||||||
app := &App{}
|
app := &App{
|
||||||
command := &Command{
|
Commands: []*Command{
|
||||||
Name: "cmd",
|
{
|
||||||
Flags: []Flag{
|
Name: "cmd",
|
||||||
&IntSliceFlag{Name: "p", Value: NewIntSlice(), Usage: "set one or more ip addr"},
|
Flags: []Flag{
|
||||||
&StringSliceFlag{Name: "ip", Value: NewStringSlice(), Usage: "set one or more ports to open"},
|
&IntSliceFlag{Name: "p", Value: NewIntSlice(), Usage: "set one or more ip addr"},
|
||||||
},
|
&StringSliceFlag{Name: "ip", Value: NewStringSlice(), Usage: "set one or more ports to open"},
|
||||||
Action: func(c *Context) error {
|
},
|
||||||
parsedIntSlice = c.IntSlice("p")
|
Action: func(c *Context) error {
|
||||||
parsedStringSlice = c.StringSlice("ip")
|
parsedIntSlice = c.IntSlice("p")
|
||||||
parsedOption = c.String("option")
|
parsedStringSlice = c.StringSlice("ip")
|
||||||
firstArg = c.Args().First()
|
parsedOption = c.String("option")
|
||||||
return nil
|
firstArg = c.Args().First()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Commands = []*Command{command}
|
|
||||||
|
|
||||||
app.Run([]string{"", "cmd", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4", "my-arg"})
|
app.Run([]string{"", "cmd", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4", "my-arg"})
|
||||||
|
|
||||||
@ -504,20 +567,22 @@ func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) {
|
|||||||
var parsedIntSlice []int
|
var parsedIntSlice []int
|
||||||
var parsedStringSlice []string
|
var parsedStringSlice []string
|
||||||
|
|
||||||
app := &App{}
|
app := &App{
|
||||||
command := &Command{
|
Commands: []*Command{
|
||||||
Name: "cmd",
|
{
|
||||||
Flags: []Flag{
|
Name: "cmd",
|
||||||
&IntSliceFlag{Name: "a", Usage: "set numbers"},
|
Flags: []Flag{
|
||||||
&StringSliceFlag{Name: "str", Usage: "set strings"},
|
&IntSliceFlag{Name: "a", Usage: "set numbers"},
|
||||||
},
|
&StringSliceFlag{Name: "str", Usage: "set strings"},
|
||||||
Action: func(c *Context) error {
|
},
|
||||||
parsedIntSlice = c.IntSlice("a")
|
Action: func(c *Context) error {
|
||||||
parsedStringSlice = c.StringSlice("str")
|
parsedIntSlice = c.IntSlice("a")
|
||||||
return nil
|
parsedStringSlice = c.StringSlice("str")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Commands = []*Command{command}
|
|
||||||
|
|
||||||
app.Run([]string{"", "cmd", "-a", "2", "-str", "A", "my-arg"})
|
app.Run([]string{"", "cmd", "-a", "2", "-str", "A", "my-arg"})
|
||||||
|
|
||||||
@ -824,6 +889,7 @@ func TestApp_OrderOfOperations(t *testing.T) {
|
|||||||
app := &App{
|
app := &App{
|
||||||
EnableShellCompletion: true,
|
EnableShellCompletion: true,
|
||||||
ShellComplete: func(c *Context) {
|
ShellComplete: func(c *Context) {
|
||||||
|
fmt.Fprintf(os.Stderr, "---> ShellComplete(%#v)\n", c)
|
||||||
counts.Total++
|
counts.Total++
|
||||||
counts.ShellComplete = counts.Total
|
counts.ShellComplete = counts.Total
|
||||||
},
|
},
|
||||||
@ -888,7 +954,7 @@ func TestApp_OrderOfOperations(t *testing.T) {
|
|||||||
|
|
||||||
resetCounts()
|
resetCounts()
|
||||||
|
|
||||||
_ = app.Run([]string{"command", "--generate-completion"})
|
_ = app.Run([]string{"command", fmt.Sprintf("--%s", genCompName())})
|
||||||
expect(t, counts.ShellComplete, 1)
|
expect(t, counts.ShellComplete, 1)
|
||||||
expect(t, counts.Total, 1)
|
expect(t, counts.Total, 1)
|
||||||
|
|
||||||
@ -1485,3 +1551,107 @@ func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
|
|||||||
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A custom flag that conforms to the relevant interfaces, but has none of the
|
||||||
|
// fields that the other flag types do.
|
||||||
|
type customBoolFlag struct {
|
||||||
|
Nombre string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use the normal FlagStringer
|
||||||
|
func (c *customBoolFlag) String() string {
|
||||||
|
return "***" + c.Nombre + "***"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *customBoolFlag) Names() []string {
|
||||||
|
return []string{c.Nombre}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *customBoolFlag) Apply(set *flag.FlagSet) {
|
||||||
|
set.String(c.Nombre, c.Nombre, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomFlagsUnused(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Flags: []Flag{&customBoolFlag{"custom"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Run returned unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomFlagsUsed(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Flags: []Flag{&customBoolFlag{"custom"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "--custom=bar"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Run returned unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomHelpVersionFlags(t *testing.T) {
|
||||||
|
app := &App{}
|
||||||
|
|
||||||
|
// Be sure to reset the global flags
|
||||||
|
defer func(helpFlag Flag, versionFlag Flag) {
|
||||||
|
HelpFlag = helpFlag.(*BoolFlag)
|
||||||
|
VersionFlag = versionFlag.(*BoolFlag)
|
||||||
|
}(HelpFlag, VersionFlag)
|
||||||
|
|
||||||
|
HelpFlag = &customBoolFlag{"help-custom"}
|
||||||
|
VersionFlag = &customBoolFlag{"version-custom"}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "--help-custom=bar"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Run returned unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShellCompletionForIncompleteFlags(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Flags: []Flag{
|
||||||
|
&IntFlag{
|
||||||
|
Name: "test-completion",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EnableShellCompletion: true,
|
||||||
|
ShellComplete: 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 flag.Names() {
|
||||||
|
if name == genCompName() {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
return fmt.Errorf("should not get here")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := app.Run([]string{"", "--test-completion", "--" + genCompName()})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("app should not return an error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
10
appveyor.yml
10
appveyor.yml
@ -1,6 +1,8 @@
|
|||||||
version: "{build}"
|
version: "{build}"
|
||||||
|
|
||||||
os: Windows Server 2012 R2
|
os: Windows Server 2016
|
||||||
|
|
||||||
|
image: Visual Studio 2017
|
||||||
|
|
||||||
clone_folder: c:\gopath\src\github.com\urfave\cli
|
clone_folder: c:\gopath\src\github.com\urfave\cli
|
||||||
|
|
||||||
@ -9,9 +11,9 @@ cache:
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
GOPATH: C:\gopath
|
GOPATH: C:\gopath
|
||||||
GOVERSION: 1.6
|
GOVERSION: 1.8.x
|
||||||
PYTHON: C:\Python27-x64
|
PYTHON: C:\Python36-x64
|
||||||
PYTHON_VERSION: 2.7.x
|
PYTHON_VERSION: 3.6.x
|
||||||
PYTHON_ARCH: 64
|
PYTHON_ARCH: 64
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
20
autocomplete/bash_autocomplete
Normal file → Executable file
20
autocomplete/bash_autocomplete
Normal file → Executable file
@ -3,12 +3,14 @@
|
|||||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
: ${PROG:=$(basename ${BASH_SOURCE})}
|
||||||
|
|
||||||
_cli_bash_autocomplete() {
|
_cli_bash_autocomplete() {
|
||||||
local cur opts base
|
local cur opts base
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
complete -F _cli_bash_autocomplete $PROG
|
complete -F _cli_bash_autocomplete $PROG
|
||||||
|
|
||||||
|
unset PROG
|
||||||
|
@ -236,7 +236,11 @@ def _flag_name_stringly_repl(match):
|
|||||||
|
|
||||||
@_migrator
|
@_migrator
|
||||||
def _commands_opaque_type(source):
|
def _commands_opaque_type(source):
|
||||||
return re.sub('cli\\.Commands', '[]*cli.Command', source, flags=re.UNICODE)
|
return _subfmt(
|
||||||
|
'cli\\.Commands(?P<suffix>[^B])',
|
||||||
|
'[]*cli.Command{suffix}',
|
||||||
|
source
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@_migrator
|
@_migrator
|
||||||
|
3
cli.go
3
cli.go
@ -11,7 +11,8 @@
|
|||||||
// Name: "greet",
|
// Name: "greet",
|
||||||
// Usage: "say a greeting",
|
// Usage: "say a greeting",
|
||||||
// Action: func(c *cli.Context) error {
|
// Action: func(c *cli.Context) error {
|
||||||
// println("Greetings")
|
// fmt.Println("Greetings")
|
||||||
|
// return nil
|
||||||
// },
|
// },
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
69
command.go
69
command.go
@ -49,6 +49,25 @@ type Command struct {
|
|||||||
// Full name of command for help, defaults to full command name, including parent commands.
|
// Full name of command for help, defaults to full command name, including parent commands.
|
||||||
HelpName string
|
HelpName string
|
||||||
commandNamePath []string
|
commandNamePath []string
|
||||||
|
|
||||||
|
// CustomHelpTemplate the text template for the command help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
CustomHelpTemplate 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.
|
||||||
@ -75,7 +94,10 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
c.appendFlag(GenerateCompletionFlag)
|
c.appendFlag(GenerateCompletionFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
set := flagSet(c.Name, c.Flags)
|
set, err := flagSet(c.Name, c.Flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
if c.SkipFlagParsing {
|
if c.SkipFlagParsing {
|
||||||
@ -84,18 +106,6 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
err = set.Parse(ctx.Args().Tail())
|
err = set.Parse(ctx.Args().Tail())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if c.OnUsageError != nil {
|
|
||||||
err := c.OnUsageError(ctx, err, false)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage:", err.Error())
|
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nerr := normalizeFlags(c.Flags, set)
|
nerr := normalizeFlags(c.Flags, set)
|
||||||
if nerr != nil {
|
if nerr != nil {
|
||||||
fmt.Fprintln(ctx.App.Writer, nerr)
|
fmt.Fprintln(ctx.App.Writer, nerr)
|
||||||
@ -105,11 +115,23 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context := NewContext(ctx.App, set, ctx)
|
context := NewContext(ctx.App, set, ctx)
|
||||||
|
context.Command = c
|
||||||
if checkCommandCompletions(context, c.Name) {
|
if checkCommandCompletions(context, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if c.OnUsageError != nil {
|
||||||
|
err := c.OnUsageError(context, err, false)
|
||||||
|
HandleExitCoder(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
|
||||||
|
fmt.Fprintln(context.App.Writer)
|
||||||
|
ShowCommandHelp(context, c.Name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if checkCommandHelp(context, c.Name) {
|
if checkCommandHelp(context, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -131,14 +153,16 @@ func (c *Command) Run(ctx *Context) (err error) {
|
|||||||
if c.Before != nil {
|
if c.Before != nil {
|
||||||
err = c.Before(context)
|
err = c.Before(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(ctx.App.Writer, err)
|
ShowCommandHelp(context, c.Name)
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Action == nil {
|
||||||
|
c.Action = helpSubcommand.Action
|
||||||
|
}
|
||||||
|
|
||||||
context.Command = c
|
context.Command = c
|
||||||
err = c.Action(context)
|
err = c.Action(context)
|
||||||
|
|
||||||
@ -175,14 +199,13 @@ 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
|
||||||
|
app.CustomAppHelpTemplate = c.CustomHelpTemplate
|
||||||
|
|
||||||
// set the flags and commands
|
// set the flags and commands
|
||||||
app.Commands = c.Subcommands
|
app.Commands = c.Subcommands
|
||||||
@ -193,6 +216,7 @@ func (c *Command) startApp(ctx *Context) error {
|
|||||||
app.HideVersion = ctx.App.HideVersion
|
app.HideVersion = ctx.App.HideVersion
|
||||||
app.Compiled = ctx.App.Compiled
|
app.Compiled = ctx.App.Compiled
|
||||||
app.Writer = ctx.App.Writer
|
app.Writer = ctx.App.Writer
|
||||||
|
app.ErrWriter = ctx.App.ErrWriter
|
||||||
|
|
||||||
app.Categories = newCommandCategories()
|
app.Categories = newCommandCategories()
|
||||||
for _, command := range c.Subcommands {
|
for _, command := range c.Subcommands {
|
||||||
@ -215,6 +239,7 @@ func (c *Command) startApp(ctx *Context) error {
|
|||||||
} else {
|
} else {
|
||||||
app.Action = helpSubcommand.Action
|
app.Action = helpSubcommand.Action
|
||||||
}
|
}
|
||||||
|
app.OnUsageError = c.OnUsageError
|
||||||
|
|
||||||
for index, cc := range app.Commands {
|
for index, cc := range app.Commands {
|
||||||
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
||||||
|
@ -123,6 +123,31 @@ func TestCommand_Run_BeforeSavesMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommand_OnUsageError_hasCommandContext(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Flags: []Flag{
|
||||||
|
&IntFlag{Name: "flag"},
|
||||||
|
},
|
||||||
|
OnUsageError: func(c *Context, err error, _ bool) error {
|
||||||
|
return fmt.Errorf("intercepted in %s: %s", c.Command.Name, err.Error())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected to receive error from Run, got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(err.Error(), "intercepted in bar") {
|
||||||
|
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
||||||
app := &App{
|
app := &App{
|
||||||
Commands: []*Command{
|
Commands: []*Command{
|
||||||
@ -150,3 +175,66 @@ 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_OnUsageError_WithSubcommand(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Subcommands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Flags: []Flag{
|
||||||
|
&IntFlag{Name: "flag"},
|
||||||
|
},
|
||||||
|
OnUsageError: func(c *Context, err error, _ bool) error {
|
||||||
|
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
|
||||||
|
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
return errors.New("intercepted: " + err.Error())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected to receive error from Run, got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
|
||||||
|
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
ErrWriter: ioutil.Discard,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
18
context.go
18
context.go
@ -13,8 +13,9 @@ import (
|
|||||||
// can be used to retrieve context-specific args and
|
// can be used to retrieve context-specific args and
|
||||||
// parsed command-line options.
|
// parsed command-line options.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
App *App
|
App *App
|
||||||
Command *Command
|
Command *Command
|
||||||
|
shellComplete bool
|
||||||
|
|
||||||
flagSet *flag.FlagSet
|
flagSet *flag.FlagSet
|
||||||
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
|
||||||
@ -114,6 +121,11 @@ func (c *Context) Lineage() []*Context {
|
|||||||
return lineage
|
return lineage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// value returns the value of the flag corresponding to `name`
|
||||||
|
func (c *Context) value(name string) interface{} {
|
||||||
|
return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
|
||||||
|
}
|
||||||
|
|
||||||
// Args returns the command line arguments associated with the context.
|
// Args returns the command line arguments associated with the context.
|
||||||
func (c *Context) Args() Args {
|
func (c *Context) Args() Args {
|
||||||
ret := args(c.flagSet.Args())
|
ret := args(c.flagSet.Args())
|
||||||
|
@ -2,7 +2,6 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -158,68 +157,6 @@ func TestContext_IsSet(t *testing.T) {
|
|||||||
expect(t, ctx.IsSet("bogus"), false)
|
expect(t, ctx.IsSet("bogus"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
|
||||||
// Should be moved to `flag_test` in v2
|
|
||||||
func TestContext_IsSet_fromEnv(t *testing.T) {
|
|
||||||
var (
|
|
||||||
timeoutIsSet, tIsSet, noEnvVarIsSet, nIsSet bool
|
|
||||||
globalTimeoutIsSet, TIsSet, globalNoEnvVarIsSet, NIsSet bool
|
|
||||||
)
|
|
||||||
|
|
||||||
os.Clearenv()
|
|
||||||
os.Setenv("GLOBAL_APP_TIMEOUT_SECONDS", "15.5")
|
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
|
||||||
a := App{
|
|
||||||
Flags: []Flag{
|
|
||||||
&Float64Flag{
|
|
||||||
Name: "global-timeout",
|
|
||||||
Aliases: []string{"T"},
|
|
||||||
EnvVars: []string{"GLOBAL_APP_TIMEOUT_SECONDS"},
|
|
||||||
},
|
|
||||||
&Float64Flag{
|
|
||||||
Name: "global-no-env-var",
|
|
||||||
Aliases: []string{"N"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Commands: []*Command{
|
|
||||||
{
|
|
||||||
Name: "hello",
|
|
||||||
Flags: []Flag{
|
|
||||||
&Float64Flag{
|
|
||||||
Name: "timeout",
|
|
||||||
Aliases: []string{"t"},
|
|
||||||
EnvVars: []string{"APP_TIMEOUT_SECONDS"},
|
|
||||||
},
|
|
||||||
&Float64Flag{
|
|
||||||
Name: "no-env-var",
|
|
||||||
Aliases: []string{"n"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(ctx *Context) error {
|
|
||||||
globalTimeoutIsSet = ctx.IsSet("global-timeout")
|
|
||||||
TIsSet = ctx.IsSet("T")
|
|
||||||
globalNoEnvVarIsSet = ctx.IsSet("global-no-env-var")
|
|
||||||
NIsSet = ctx.IsSet("N")
|
|
||||||
timeoutIsSet = ctx.IsSet("timeout")
|
|
||||||
tIsSet = ctx.IsSet("t")
|
|
||||||
noEnvVarIsSet = ctx.IsSet("no-env-var")
|
|
||||||
nIsSet = ctx.IsSet("n")
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.Run([]string{"run", "hello"})
|
|
||||||
expect(t, globalTimeoutIsSet, true)
|
|
||||||
expect(t, TIsSet, true)
|
|
||||||
expect(t, globalNoEnvVarIsSet, false)
|
|
||||||
expect(t, NIsSet, false)
|
|
||||||
expect(t, timeoutIsSet, true)
|
|
||||||
expect(t, tIsSet, true)
|
|
||||||
expect(t, noEnvVarIsSet, false)
|
|
||||||
expect(t, nIsSet, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext_NumFlags(t *testing.T) {
|
func TestContext_NumFlags(t *testing.T) {
|
||||||
set := flag.NewFlagSet("test", 0)
|
set := flag.NewFlagSet("test", 0)
|
||||||
set.Bool("myflag", false, "doc")
|
set.Bool("myflag", false, "doc")
|
||||||
@ -238,8 +175,10 @@ func TestContext_Set(t *testing.T) {
|
|||||||
set.Int("int", 5, "an int")
|
set.Int("int", 5, "an int")
|
||||||
c := NewContext(nil, set, nil)
|
c := NewContext(nil, set, nil)
|
||||||
|
|
||||||
|
expect(t, c.IsSet("int"), false)
|
||||||
c.Set("int", "1")
|
c.Set("int", "1")
|
||||||
expect(t, c.Int("int"), 1)
|
expect(t, c.Int("int"), 1)
|
||||||
|
expect(t, c.IsSet("int"), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContext_LocalFlagNames(t *testing.T) {
|
func TestContext_LocalFlagNames(t *testing.T) {
|
||||||
|
43
errors.go
43
errors.go
@ -48,6 +48,10 @@ func (m *multiError) Errors() []error {
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
@ -57,12 +61,12 @@ type ExitCoder interface {
|
|||||||
|
|
||||||
type exitError struct {
|
type exitError struct {
|
||||||
exitCode int
|
exitCode int
|
||||||
message string
|
message interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit wraps a message and exit code into an ExitCoder suitable for handling by
|
// Exit wraps a message and exit code into an ExitCoder suitable for handling by
|
||||||
// HandleExitCoder
|
// HandleExitCoder
|
||||||
func Exit(message string, exitCode int) ExitCoder {
|
func Exit(message interface{}, exitCode int) ExitCoder {
|
||||||
return &exitError{
|
return &exitError{
|
||||||
exitCode: exitCode,
|
exitCode: exitCode,
|
||||||
message: message,
|
message: message,
|
||||||
@ -70,7 +74,7 @@ func Exit(message string, exitCode int) ExitCoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ee *exitError) Error() string {
|
func (ee *exitError) Error() string {
|
||||||
return ee.message
|
return fmt.Sprintf("%v", ee.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee *exitError) ExitCode() int {
|
func (ee *exitError) ExitCode() int {
|
||||||
@ -80,7 +84,7 @@ func (ee *exitError) ExitCode() int {
|
|||||||
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
||||||
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
||||||
// given exit code. If the given error is a MultiError, then this func is
|
// given exit code. If the given error is a MultiError, then this func is
|
||||||
// called on all members of the Errors slice.
|
// called on all members of the Errors slice and calls OsExiter with the last exit code.
|
||||||
func HandleExitCoder(err error) {
|
func HandleExitCoder(err error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
@ -88,21 +92,34 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
if multiErr, ok := err.(MultiError); ok {
|
if multiErr, ok := err.(MultiError); ok {
|
||||||
for _, merr := range multiErr.Errors() {
|
code := handleMultiError(multiErr)
|
||||||
HandleExitCoder(merr)
|
OsExiter(code)
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if err.Error() != "" {
|
|
||||||
fmt.Fprintln(ErrWriter, err)
|
func handleMultiError(multiErr MultiError) int {
|
||||||
}
|
code := 1
|
||||||
OsExiter(1)
|
for _, merr := range multiErr.Errors() {
|
||||||
|
if multiErr2, ok := merr.(MultiError); ok {
|
||||||
|
code = handleMultiError(multiErr2)
|
||||||
|
} else if merr != nil {
|
||||||
|
fmt.Fprintln(ErrWriter, merr)
|
||||||
|
if exitErr, ok := merr.(ExitCoder); ok {
|
||||||
|
code = exitErr.ExitCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -11,8 +12,10 @@ func TestHandleExitCoder_nil(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
defer func() { OsExiter = fakeOsExiter }()
|
||||||
@ -28,8 +31,10 @@ func TestHandleExitCoder_ExitCoder(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
defer func() { OsExiter = fakeOsExiter }()
|
||||||
@ -45,27 +50,43 @@ func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) {
|
|||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
exitCode = rc
|
||||||
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() { OsExiter = fakeOsExiter }()
|
defer func() { OsExiter = fakeOsExiter }()
|
||||||
|
|
||||||
exitErr := Exit("galactic perimeter breach", 9)
|
exitErr := Exit("galactic perimeter breach", 9)
|
||||||
err := newMultiError(errors.New("wowsa"), errors.New("egad"), exitErr)
|
exitErr2 := Exit("last ExitCoder", 11)
|
||||||
|
err := newMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2)
|
||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
|
|
||||||
expect(t, exitCode, 9)
|
expect(t, exitCode, 11)
|
||||||
expect(t, called, true)
|
expect(t, called, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleExitCoder_ErrorWithMessage(t *testing.T) {
|
// make a stub to not import pkg/errors
|
||||||
exitCode := 0
|
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
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ErrWriter = &bytes.Buffer{}
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
@ -74,33 +95,28 @@ func TestHandleExitCoder_ErrorWithMessage(t *testing.T) {
|
|||||||
ErrWriter = fakeErrWriter
|
ErrWriter = fakeErrWriter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := errors.New("gourd havens")
|
err := Exit(NewErrorWithFormat("I am formatted"), 1)
|
||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
|
|
||||||
expect(t, exitCode, 1)
|
|
||||||
expect(t, called, true)
|
expect(t, called, true)
|
||||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "gourd havens\n")
|
expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) {
|
func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) {
|
||||||
exitCode := 0
|
|
||||||
called := false
|
called := false
|
||||||
|
|
||||||
OsExiter = func(rc int) {
|
OsExiter = func(rc int) {
|
||||||
exitCode = rc
|
if !called {
|
||||||
called = true
|
called = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ErrWriter = &bytes.Buffer{}
|
ErrWriter = &bytes.Buffer{}
|
||||||
|
|
||||||
defer func() {
|
defer func() { OsExiter = fakeOsExiter }()
|
||||||
OsExiter = fakeOsExiter
|
|
||||||
ErrWriter = fakeErrWriter
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := errors.New("")
|
err := newMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2"))
|
||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
|
|
||||||
expect(t, exitCode, 1)
|
|
||||||
expect(t, called, true)
|
expect(t, called, true)
|
||||||
expect(t, ErrWriter.(*bytes.Buffer).String(), "")
|
expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n")
|
||||||
}
|
}
|
||||||
|
252
flag.go
252
flag.go
@ -4,12 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,11 +22,19 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GenerateCompletionFlag enables completion for all commands and subcommands
|
// GenerateCompletionFlag enables completion for all commands and subcommands
|
||||||
var GenerateCompletionFlag = &BoolFlag{
|
var GenerateCompletionFlag Flag = &BoolFlag{
|
||||||
Name: "generate-completion",
|
Name: "generate-completion",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func genCompName() string {
|
||||||
|
names := GenerateCompletionFlag.Names()
|
||||||
|
if len(names) == 0 {
|
||||||
|
return "generate-completion"
|
||||||
|
}
|
||||||
|
return names[0]
|
||||||
|
}
|
||||||
|
|
||||||
// InitCompletionFlag generates completion code
|
// InitCompletionFlag generates completion code
|
||||||
var InitCompletionFlag = &StringFlag{
|
var InitCompletionFlag = &StringFlag{
|
||||||
Name: "init-completion",
|
Name: "init-completion",
|
||||||
@ -34,7 +42,7 @@ var InitCompletionFlag = &StringFlag{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VersionFlag prints the version for the application
|
// VersionFlag prints the version for the application
|
||||||
var VersionFlag = &BoolFlag{
|
var VersionFlag Flag = &BoolFlag{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
Aliases: []string{"v"},
|
Aliases: []string{"v"},
|
||||||
Usage: "print the version",
|
Usage: "print the version",
|
||||||
@ -43,7 +51,7 @@ var VersionFlag = &BoolFlag{
|
|||||||
// HelpFlag prints the help for all commands and subcommands.
|
// HelpFlag prints the help for all commands and subcommands.
|
||||||
// Set to nil to disable the flag. The subcommand
|
// Set to nil to disable the flag. The subcommand
|
||||||
// will still be added unless HideHelp is set to true.
|
// will still be added unless HideHelp is set to true.
|
||||||
var HelpFlag = &BoolFlag{
|
var HelpFlag Flag = &BoolFlag{
|
||||||
Name: "help",
|
Name: "help",
|
||||||
Aliases: []string{"h"},
|
Aliases: []string{"h"},
|
||||||
Usage: "show help",
|
Usage: "show help",
|
||||||
@ -88,13 +96,29 @@ type Flag interface {
|
|||||||
Names() []string
|
Names() []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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic is a generic parseable type identified by a specific flag
|
// Generic is a generic parseable type identified by a specific flag
|
||||||
@ -105,11 +129,18 @@ 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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
val.Set(envVal)
|
val.Set(envVal)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -119,6 +150,7 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
set.Var(val, name, f.Usage)
|
set.Var(val, name, f.Usage)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringSlice wraps a []string to satisfy flag.Value
|
// StringSlice wraps a []string to satisfy flag.Value
|
||||||
@ -166,15 +198,28 @@ func (f *StringSlice) Value() []string {
|
|||||||
return f.slice
|
return f.slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := NewStringSlice()
|
newVal := NewStringSlice()
|
||||||
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 %q as string value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
break
|
break
|
||||||
@ -189,6 +234,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntSlice wraps an []int to satisfy flag.Value
|
// IntSlice wraps an []int to satisfy flag.Value
|
||||||
@ -256,17 +302,27 @@ func (i *IntSlice) Value() []int {
|
|||||||
return i.slice
|
return i.slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := NewIntSlice()
|
newVal := NewIntSlice()
|
||||||
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 %q as int slice value for flag %s: %s", envVal, f.Name, err)
|
||||||
fmt.Fprintf(ErrWriter, err.Error())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
@ -282,6 +338,7 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
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
|
||||||
@ -329,17 +386,27 @@ func (f *Int64Slice) Value() []int64 {
|
|||||||
return f.slice
|
return f.slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := NewInt64Slice()
|
newVal := NewInt64Slice()
|
||||||
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 %q as int64 slice value for flag %s: %s", envVal, f.Name, err)
|
||||||
fmt.Fprintf(ErrWriter, err.Error())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.Value = newVal
|
f.Value = newVal
|
||||||
@ -355,17 +422,30 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
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 {
|
||||||
if f.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
envValBool, err := strconv.ParseBool(envVal)
|
if envVal == "" {
|
||||||
if err == nil {
|
f.Value = false
|
||||||
f.Value = envValBool
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
envValBool, err := strconv.ParseBool(envVal)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse %q as bool value for flag %s: %s", envVal, f.Name, err)
|
||||||
|
}
|
||||||
|
f.Value = envValBool
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,13 +458,20 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Bool(name, f.Value, f.Usage)
|
set.Bool(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 *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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
f.Value = envVal
|
f.Value = envVal
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -398,18 +485,26 @@ 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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
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 %q as int value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
f.Value = int(envValInt)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,18 +516,27 @@ 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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
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 %q as int value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = envValInt
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -440,22 +544,31 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
if f.Destination != nil {
|
if f.Destination != nil {
|
||||||
set.Int64Var(f.Destination, name, f.Value, f.Usage)
|
set.Int64Var(f.Destination, name, f.Value, f.Usage)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
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 %q as uint value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = uint(envValInt)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -463,22 +576,31 @@ func (f *UintFlag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
if f.Destination != nil {
|
if f.Destination != nil {
|
||||||
set.UintVar(f.Destination, name, f.Value, f.Usage)
|
set.UintVar(f.Destination, name, f.Value, f.Usage)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
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 %q as uint64 value for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = uint64(envValInt)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -486,22 +608,31 @@ func (f *Uint64Flag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
if f.Destination != nil {
|
if f.Destination != nil {
|
||||||
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
|
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
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 %q as duration for flag %s: %s", envVal, f.Name, err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = envValDuration
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,17 +644,27 @@ 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.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
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 %q as float64 value for flag %s: %s", envVal, f.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Value = float64(envValFloat)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,6 +676,7 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) {
|
|||||||
}
|
}
|
||||||
set.Float64(name, f.Value, f.Usage)
|
set.Float64(name, f.Value, f.Usage)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFloat64Slice makes a *Float64Slice with default values
|
// NewFloat64Slice makes a *Float64Slice with default values
|
||||||
@ -588,10 +730,16 @@ func (f *Float64Slice) Value() []float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
|
// Ignores errors
|
||||||
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) {
|
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) {
|
||||||
|
f.ApplyWithError(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||||
|
func (f *Float64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||||
if f.EnvVars != nil {
|
if f.EnvVars != nil {
|
||||||
for _, envVar := range f.EnvVars {
|
for _, envVar := range f.EnvVars {
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||||
newVal := NewFloat64Slice()
|
newVal := NewFloat64Slice()
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
for _, s := range strings.Split(envVal, ",") {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
@ -613,12 +761,14 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
for _, name := range f.Names() {
|
for _, name := range f.Names() {
|
||||||
set.Var(f.Value, name, f.Usage)
|
set.Var(f.Value, name, f.Usage)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func visibleFlags(fl []Flag) []Flag {
|
func visibleFlags(fl []Flag) []Flag {
|
||||||
visible := []Flag{}
|
visible := []Flag{}
|
||||||
for _, flag := range fl {
|
for _, flag := range fl {
|
||||||
if !flagValue(flag).FieldByName("Hidden").Bool() {
|
field := flagValue(flag).FieldByName("Hidden")
|
||||||
|
if !field.IsValid() || !field.Bool() {
|
||||||
visible = append(visible, flag)
|
visible = append(visible, flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ type BoolFlag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value bool
|
Value bool
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *bool
|
Destination *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ type DurationFlag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value time.Duration
|
Value time.Duration
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *time.Duration
|
Destination *time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +107,7 @@ type Float64Flag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value float64
|
Value float64
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *float64
|
Destination *float64
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +195,7 @@ type Int64Flag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value int64
|
Value int64
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *int64
|
Destination *int64
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +240,7 @@ type IntFlag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value int
|
Value int
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *int
|
Destination *int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,6 +414,7 @@ type StringFlag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value string
|
Value string
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *string
|
Destination *string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,6 +502,7 @@ type Uint64Flag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value uint64
|
Value uint64
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *uint64
|
Destination *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,6 +547,7 @@ type UintFlag struct {
|
|||||||
Hidden bool
|
Hidden bool
|
||||||
Value uint
|
Value uint
|
||||||
DefaultText string
|
DefaultText string
|
||||||
|
|
||||||
Destination *uint
|
Destination *uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
175
flag_test.go
175
flag_test.go
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -41,6 +42,106 @@ func TestBoolFlagApply_SetsAllNames(t *testing.T) {
|
|||||||
expect(t, v, true)
|
expect(t, v, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFlagsFromEnv(t *testing.T) {
|
||||||
|
newSetIntSlice := func(defaults ...int) IntSlice {
|
||||||
|
s := NewIntSlice(defaults...)
|
||||||
|
s.hasBeenSet = true
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
|
||||||
|
newSetInt64Slice := func(defaults ...int64) Int64Slice {
|
||||||
|
s := NewInt64Slice(defaults...)
|
||||||
|
s.hasBeenSet = true
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
|
||||||
|
newSetStringSlice := func(defaults ...string) StringSlice {
|
||||||
|
s := NewStringSlice(defaults...)
|
||||||
|
s.hasBeenSet = true
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
|
||||||
|
var flagTests = []struct {
|
||||||
|
input string
|
||||||
|
output interface{}
|
||||||
|
flag Flag
|
||||||
|
errRegexp string
|
||||||
|
}{
|
||||||
|
{"", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
|
||||||
|
{"1", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
|
||||||
|
{"false", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
|
||||||
|
{"foobar", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, `could not parse "foobar" as bool value for flag debug: .*`},
|
||||||
|
|
||||||
|
{"1s", 1 * time.Second, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, ""},
|
||||||
|
{"foobar", false, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, `could not parse "foobar" as duration for flag time: .*`},
|
||||||
|
|
||||||
|
{"1.2", 1.2, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"1", 1.0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"foobar", 0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as float64 value for flag seconds: .*`},
|
||||||
|
|
||||||
|
{"1", int64(1), &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"1.2", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value for flag seconds: .*`},
|
||||||
|
{"foobar", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value for flag seconds: .*`},
|
||||||
|
|
||||||
|
{"1", 1, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"1.2", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value for flag seconds: .*`},
|
||||||
|
{"foobar", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value for flag seconds: .*`},
|
||||||
|
|
||||||
|
{"1,2", newSetIntSlice(1, 2), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"1.2,2", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int slice value for flag seconds: .*`},
|
||||||
|
{"foobar", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int slice value for flag seconds: .*`},
|
||||||
|
|
||||||
|
{"1,2", newSetInt64Slice(1, 2), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"1.2,2", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int64 slice value for flag seconds: .*`},
|
||||||
|
{"foobar", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int64 slice value for flag seconds: .*`},
|
||||||
|
|
||||||
|
{"foo", "foo", &StringFlag{Name: "name", EnvVars: []string{"NAME"}}, ""},
|
||||||
|
|
||||||
|
{"foo,bar", newSetStringSlice("foo", "bar"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""},
|
||||||
|
|
||||||
|
{"1", uint(1), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"1.2", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint value for flag seconds: .*`},
|
||||||
|
{"foobar", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint value for flag seconds: .*`},
|
||||||
|
|
||||||
|
{"1", uint64(1), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
|
||||||
|
{"1.2", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint64 value for flag seconds: .*`},
|
||||||
|
{"foobar", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 value for flag seconds: .*`},
|
||||||
|
|
||||||
|
{"foo,bar", &Parser{"foo", "bar"}, &GenericFlag{Name: "names", Value: &Parser{}, EnvVars: []string{"NAMES"}}, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range flagTests {
|
||||||
|
clearenv()
|
||||||
|
envVarSlice := reflect.Indirect(reflect.ValueOf(test.flag)).FieldByName("EnvVars").Slice(0, 1)
|
||||||
|
os.Setenv(envVarSlice.Index(0).String(), test.input)
|
||||||
|
a := App{
|
||||||
|
Flags: []Flag{test.flag},
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
if !reflect.DeepEqual(ctx.value(test.flag.Names()[0]), test.output) {
|
||||||
|
t.Errorf("ex:%01d expected %q to be parsed as %#v, instead was %#v", i, test.input, test.output, ctx.value(test.flag.Names()[0]))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := a.Run([]string{"run"})
|
||||||
|
|
||||||
|
if test.errRegexp != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error to match %q, got none", test.errRegexp)
|
||||||
|
} else {
|
||||||
|
if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched {
|
||||||
|
t.Errorf("expected error to match %q, got error %s", test.errRegexp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil && test.errRegexp == "" {
|
||||||
|
t.Errorf("expected no error got %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var stringFlagTests = []struct {
|
var stringFlagTests = []struct {
|
||||||
name string
|
name string
|
||||||
aliases []string
|
aliases []string
|
||||||
@ -78,7 +179,7 @@ func TestStringFlagDefaultText(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_FOO", "derp")
|
os.Setenv("APP_FOO", "derp")
|
||||||
for _, test := range stringFlagTests {
|
for _, test := range stringFlagTests {
|
||||||
flag := &StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}}
|
flag := &StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}}
|
||||||
@ -130,7 +231,7 @@ func TestStringSliceFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_QWWX", "11,4")
|
os.Setenv("APP_QWWX", "11,4")
|
||||||
for _, test := range stringSliceFlagTests {
|
for _, test := range stringSliceFlagTests {
|
||||||
flag := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}}
|
flag := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}}
|
||||||
@ -175,7 +276,7 @@ func TestIntFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_BAR", "2")
|
os.Setenv("APP_BAR", "2")
|
||||||
for _, test := range intFlagTests {
|
for _, test := range intFlagTests {
|
||||||
flag := &IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
flag := &IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
||||||
@ -222,7 +323,7 @@ func TestInt64FlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_BAR", "2")
|
os.Setenv("APP_BAR", "2")
|
||||||
for _, test := range int64FlagTests {
|
for _, test := range int64FlagTests {
|
||||||
flag := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
flag := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
||||||
@ -258,7 +359,7 @@ func TestUintFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUintFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestUintFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_BAR", "2")
|
os.Setenv("APP_BAR", "2")
|
||||||
for _, test := range uintFlagTests {
|
for _, test := range uintFlagTests {
|
||||||
flag := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
flag := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
||||||
@ -294,7 +395,7 @@ func TestUint64FlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_BAR", "2")
|
os.Setenv("APP_BAR", "2")
|
||||||
for _, test := range uint64FlagTests {
|
for _, test := range uint64FlagTests {
|
||||||
flag := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
flag := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
||||||
@ -330,7 +431,7 @@ func TestDurationFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_BAR", "2h3m6s")
|
os.Setenv("APP_BAR", "2h3m6s")
|
||||||
for _, test := range durationFlagTests {
|
for _, test := range durationFlagTests {
|
||||||
flag := &DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
flag := &DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}}
|
||||||
@ -380,7 +481,7 @@ func TestIntSliceFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_SMURF", "42,3")
|
os.Setenv("APP_SMURF", "42,3")
|
||||||
for _, test := range intSliceFlagTests {
|
for _, test := range intSliceFlagTests {
|
||||||
flag := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}}
|
flag := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}}
|
||||||
@ -429,7 +530,7 @@ func TestInt64SliceFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_SMURF", "42,17179869184")
|
os.Setenv("APP_SMURF", "42,17179869184")
|
||||||
for _, test := range int64SliceFlagTests {
|
for _, test := range int64SliceFlagTests {
|
||||||
flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
|
flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
|
||||||
@ -465,7 +566,7 @@ func TestFloat64FlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_BAZ", "99.4")
|
os.Setenv("APP_BAZ", "99.4")
|
||||||
for _, test := range float64FlagTests {
|
for _, test := range float64FlagTests {
|
||||||
flag := &Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}}
|
flag := &Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}}
|
||||||
@ -516,7 +617,7 @@ func TestFloat64SliceFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFloat64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestFloat64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_SMURF", "0.1234,-10.5")
|
os.Setenv("APP_SMURF", "0.1234,-10.5")
|
||||||
for _, test := range float64SliceFlagTests {
|
for _, test := range float64SliceFlagTests {
|
||||||
flag := Float64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
|
flag := Float64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}}
|
||||||
@ -553,7 +654,7 @@ func TestGenericFlagHelpOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
|
func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_ZAP", "3")
|
os.Setenv("APP_ZAP", "3")
|
||||||
for _, test := range genericFlagTests {
|
for _, test := range genericFlagTests {
|
||||||
flag := &GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}}
|
flag := &GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}}
|
||||||
@ -615,7 +716,7 @@ func TestParseDestinationString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiStringFromEnv(t *testing.T) {
|
func TestParseMultiStringFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_COUNT", "20")
|
os.Setenv("APP_COUNT", "20")
|
||||||
(&App{
|
(&App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -634,7 +735,7 @@ func TestParseMultiStringFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiStringFromEnvCascade(t *testing.T) {
|
func TestParseMultiStringFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_COUNT", "20")
|
os.Setenv("APP_COUNT", "20")
|
||||||
(&App{
|
(&App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -706,7 +807,7 @@ func TestParseMultiStringSliceWithDefaultsUnset(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiStringSliceFromEnv(t *testing.T) {
|
func TestParseMultiStringSliceFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -726,7 +827,7 @@ func TestParseMultiStringSliceFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) {
|
func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -746,7 +847,7 @@ func TestParseMultiStringSliceFromEnvWithDefaults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
|
func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -766,7 +867,7 @@ func TestParseMultiStringSliceFromEnvCascade(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) {
|
func TestParseMultiStringSliceFromEnvCascadeWithDefaults(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -823,7 +924,7 @@ func TestParseDestinationInt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiIntFromEnv(t *testing.T) {
|
func TestParseMultiIntFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -843,7 +944,7 @@ func TestParseMultiIntFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiIntFromEnvCascade(t *testing.T) {
|
func TestParseMultiIntFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
os.Setenv("APP_TIMEOUT_SECONDS", "10")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -914,7 +1015,7 @@ func TestParseMultiIntSliceWithDefaultsUnset(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiIntSliceFromEnv(t *testing.T) {
|
func TestParseMultiIntSliceFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -934,7 +1035,7 @@ func TestParseMultiIntSliceFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) {
|
func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -954,7 +1055,7 @@ func TestParseMultiIntSliceFromEnvWithDefaults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
|
func TestParseMultiIntSliceFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,40")
|
os.Setenv("APP_INTERVALS", "20,30,40")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -991,7 +1092,7 @@ func TestParseMultiInt64Slice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiInt64SliceFromEnv(t *testing.T) {
|
func TestParseMultiInt64SliceFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,17179869184")
|
os.Setenv("APP_INTERVALS", "20,30,17179869184")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -1011,7 +1112,7 @@ func TestParseMultiInt64SliceFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) {
|
func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "20,30,17179869184")
|
os.Setenv("APP_INTERVALS", "20,30,17179869184")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -1068,7 +1169,7 @@ func TestParseDestinationFloat64(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiFloat64FromEnv(t *testing.T) {
|
func TestParseMultiFloat64FromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1088,7 +1189,7 @@ func TestParseMultiFloat64FromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
|
func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1108,7 +1209,7 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiFloat64SliceFromEnv(t *testing.T) {
|
func TestParseMultiFloat64SliceFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "0.1,-10.5")
|
os.Setenv("APP_INTERVALS", "0.1,-10.5")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -1128,7 +1229,7 @@ func TestParseMultiFloat64SliceFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiFloat64SliceFromEnvCascade(t *testing.T) {
|
func TestParseMultiFloat64SliceFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_INTERVALS", "0.1234,-10.5")
|
os.Setenv("APP_INTERVALS", "0.1234,-10.5")
|
||||||
|
|
||||||
(&App{
|
(&App{
|
||||||
@ -1185,7 +1286,7 @@ func TestParseDestinationBool(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiBoolFromEnv(t *testing.T) {
|
func TestParseMultiBoolFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_DEBUG", "1")
|
os.Setenv("APP_DEBUG", "1")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1205,7 +1306,7 @@ func TestParseMultiBoolFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiBoolFromEnvCascade(t *testing.T) {
|
func TestParseMultiBoolFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_DEBUG", "1")
|
os.Setenv("APP_DEBUG", "1")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1264,7 +1365,7 @@ func TestParseDestinationBoolTrue(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiBoolTrueFromEnv(t *testing.T) {
|
func TestParseMultiBoolTrueFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_DEBUG", "0")
|
os.Setenv("APP_DEBUG", "0")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1289,7 +1390,7 @@ func TestParseMultiBoolTrueFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseMultiBoolTrueFromEnvCascade(t *testing.T) {
|
func TestParseMultiBoolTrueFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_DEBUG", "0")
|
os.Setenv("APP_DEBUG", "0")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1331,6 +1432,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{
|
||||||
@ -1350,7 +1455,7 @@ func TestParseGeneric(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseGenericFromEnv(t *testing.T) {
|
func TestParseGenericFromEnv(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_SERVE", "20,30")
|
os.Setenv("APP_SERVE", "20,30")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
@ -1375,7 +1480,7 @@ func TestParseGenericFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseGenericFromEnvCascade(t *testing.T) {
|
func TestParseGenericFromEnvCascade(t *testing.T) {
|
||||||
os.Clearenv()
|
clearenv()
|
||||||
os.Setenv("APP_FOO", "99,2000")
|
os.Setenv("APP_FOO", "99,2000")
|
||||||
a := App{
|
a := App{
|
||||||
Flags: []Flag{
|
Flags: []Flag{
|
||||||
|
@ -68,6 +68,9 @@ import tempfile
|
|||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
|
|
||||||
|
_PY3 = sys.version_info.major == 3
|
||||||
|
|
||||||
|
|
||||||
class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter,
|
class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter,
|
||||||
argparse.RawDescriptionHelpFormatter):
|
argparse.RawDescriptionHelpFormatter):
|
||||||
pass
|
pass
|
||||||
@ -104,7 +107,7 @@ def main(sysargs=sys.argv[:]):
|
|||||||
def _generate_flag_types(writefunc, output_go, input_json):
|
def _generate_flag_types(writefunc, output_go, input_json):
|
||||||
types = json.load(input_json)
|
types = json.load(input_json)
|
||||||
|
|
||||||
tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False)
|
tmp = _get_named_tmp_go()
|
||||||
writefunc(tmp, types)
|
writefunc(tmp, types)
|
||||||
tmp.close()
|
tmp.close()
|
||||||
|
|
||||||
@ -117,6 +120,13 @@ def _generate_flag_types(writefunc, output_go, input_json):
|
|||||||
os.remove(tmp.name)
|
os.remove(tmp.name)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_named_tmp_go():
|
||||||
|
tmp_args = dict(suffix='.go', mode='w', delete=False)
|
||||||
|
if _PY3:
|
||||||
|
tmp_args['encoding'] = 'utf-8'
|
||||||
|
return tempfile.NamedTemporaryFile(**tmp_args)
|
||||||
|
|
||||||
|
|
||||||
def _set_typedef_defaults(typedef):
|
def _set_typedef_defaults(typedef):
|
||||||
typedef.setdefault('doctail', '')
|
typedef.setdefault('doctail', '')
|
||||||
typedef.setdefault('context_type', typedef['type'])
|
typedef.setdefault('context_type', typedef['type'])
|
||||||
@ -222,11 +232,18 @@ 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))
|
||||||
|
|
||||||
|
|
||||||
def _fwrite(outfile, text):
|
def _fwrite(outfile, text):
|
||||||
print(textwrap.dedent(text), end='', file=outfile)
|
print(textwrap.dedent(text), end=None, file=outfile)
|
||||||
|
|
||||||
|
|
||||||
_WRITEFUNCS = {
|
_WRITEFUNCS = {
|
||||||
|
122
help.go
122
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}}
|
||||||
@ -47,7 +47,7 @@ var CommandHelpTemplate = `NAME:
|
|||||||
{{.HelpName}} - {{.Usage}}
|
{{.HelpName}} - {{.Usage}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
|
||||||
|
|
||||||
CATEGORY:
|
CATEGORY:
|
||||||
{{.Category}}{{end}}{{if .Description}}
|
{{.Category}}{{end}}{{if .Description}}
|
||||||
@ -64,10 +64,10 @@ 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}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
||||||
|
|
||||||
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
||||||
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
||||||
@ -112,17 +112,43 @@ var helpSubcommand = &Command{
|
|||||||
// Prints help for the App or Command
|
// Prints help for the App or Command
|
||||||
type helpPrinter func(w io.Writer, templ string, data interface{})
|
type helpPrinter func(w io.Writer, templ string, data interface{})
|
||||||
|
|
||||||
|
// Prints help for the App or Command with custom template function.
|
||||||
|
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
|
||||||
|
|
||||||
// HelpPrinter is a function that writes the help output. If not set a default
|
// HelpPrinter is a function that writes the help output. If not set a default
|
||||||
// is used. The function signature is:
|
// is used. The function signature is:
|
||||||
// func(w io.Writer, templ string, data interface{})
|
// func(w io.Writer, templ string, data interface{})
|
||||||
var HelpPrinter helpPrinter = printHelp
|
var HelpPrinter helpPrinter = printHelp
|
||||||
|
|
||||||
|
// HelpPrinterCustom is same as HelpPrinter but
|
||||||
|
// takes a custom function for template function map.
|
||||||
|
var HelpPrinterCustom helpPrinterCustom = printHelpCustom
|
||||||
|
|
||||||
// VersionPrinter prints the version for the App
|
// VersionPrinter prints the version for the App
|
||||||
var VersionPrinter = printVersion
|
var VersionPrinter = printVersion
|
||||||
|
|
||||||
|
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
|
||||||
|
func ShowAppHelpAndExit(c *Context, exitCode int) {
|
||||||
|
ShowAppHelp(c)
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
// ShowAppHelp is an action that displays the help.
|
// ShowAppHelp is an action that displays the help.
|
||||||
func ShowAppHelp(c *Context) {
|
func ShowAppHelp(c *Context) (err error) {
|
||||||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
if c.App.CustomAppHelpTemplate == "" {
|
||||||
|
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customAppData := func() map[string]interface{} {
|
||||||
|
if c.App.ExtraInfo == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return map[string]interface{}{
|
||||||
|
"ExtraInfo": c.App.ExtraInfo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
||||||
@ -137,6 +163,12 @@ func DefaultAppComplete(c *Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowCommandHelpAndExit - exits with code after showing help
|
||||||
|
func ShowCommandHelpAndExit(c *Context, command string, code int) {
|
||||||
|
ShowCommandHelp(c, command)
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
// ShowCommandHelp prints help for the given command
|
// ShowCommandHelp prints help for the given command
|
||||||
func ShowCommandHelp(ctx *Context, command string) error {
|
func ShowCommandHelp(ctx *Context, command string) error {
|
||||||
// show the subcommand help for a command with subcommands
|
// show the subcommand help for a command with subcommands
|
||||||
@ -147,7 +179,11 @@ func ShowCommandHelp(ctx *Context, command string) error {
|
|||||||
|
|
||||||
for _, c := range ctx.App.Commands {
|
for _, c := range ctx.App.Commands {
|
||||||
if c.HasName(command) {
|
if c.HasName(command) {
|
||||||
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
if c.CustomHelpTemplate != "" {
|
||||||
|
HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil)
|
||||||
|
} else {
|
||||||
|
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -198,10 +234,15 @@ func ShowCommandCompletions(ctx *Context, command string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) {
|
||||||
funcMap := template.FuncMap{
|
funcMap := template.FuncMap{
|
||||||
"join": strings.Join,
|
"join": strings.Join,
|
||||||
}
|
}
|
||||||
|
if customFunc != nil {
|
||||||
|
for key, value := range customFunc {
|
||||||
|
funcMap[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
||||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||||
@ -219,13 +260,15 @@ func printHelp(out io.Writer, templ string, data interface{}) {
|
|||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||||
|
printHelpCustom(out, templ, data, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func checkVersion(c *Context) bool {
|
func checkVersion(c *Context) bool {
|
||||||
found := false
|
found := false
|
||||||
if VersionFlag.Name != "" {
|
for _, name := range VersionFlag.Names() {
|
||||||
for _, name := range VersionFlag.Names() {
|
if c.Bool(name) {
|
||||||
if c.Bool(name) {
|
found = true
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return found
|
return found
|
||||||
@ -233,11 +276,9 @@ func checkVersion(c *Context) bool {
|
|||||||
|
|
||||||
func checkHelp(c *Context) bool {
|
func checkHelp(c *Context) bool {
|
||||||
found := false
|
found := false
|
||||||
if HelpFlag.Name != "" {
|
for _, name := range HelpFlag.Names() {
|
||||||
for _, name := range HelpFlag.Names() {
|
if c.Bool(name) {
|
||||||
if c.Bool(name) {
|
found = true
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return found
|
return found
|
||||||
@ -261,22 +302,45 @@ func checkSubcommandHelp(c *Context) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCompletions(c *Context) bool {
|
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
|
||||||
if c.Bool(GenerateCompletionFlag.Name) && c.App.EnableShellCompletion {
|
if !a.EnableShellCompletion {
|
||||||
ShowCompletions(c)
|
return false, arguments
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
pos := len(arguments) - 1
|
||||||
|
lastArg := arguments[pos]
|
||||||
|
|
||||||
|
if lastArg != "--"+genCompName() {
|
||||||
|
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(GenerateCompletionFlag.Name) && c.App.EnableShellCompletion {
|
if !c.shellComplete {
|
||||||
ShowCommandCompletions(c, name)
|
return false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
ShowCommandCompletions(c, name)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkInitCompletion(c *Context) (bool, error) {
|
func checkInitCompletion(c *Context) (bool, error) {
|
||||||
@ -302,12 +366,12 @@ func bashCompletionCode(progName string) string {
|
|||||||
local cur opts base;
|
local cur opts base;
|
||||||
COMPREPLY=();
|
COMPREPLY=();
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}";
|
cur="${COMP_WORDS[COMP_CWORD]}";
|
||||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-completion );
|
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --%s );
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) );
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) );
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
complete -F _cli_bash_autocomplete %s`
|
complete -F _cli_bash_autocomplete %s`
|
||||||
return fmt.Sprintf(template, progName)
|
return fmt.Sprintf(template, genCompName(), progName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zshCompletionCode(progName string) string {
|
func zshCompletionCode(progName string) string {
|
||||||
|
163
help_test.go
163
help_test.go
@ -3,6 +3,8 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -255,6 +257,92 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShowCommandHelp_Customtemplate(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "frobbly",
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
HelpName: "foo frobbly",
|
||||||
|
CustomHelpTemplate: `NAME:
|
||||||
|
{{.HelpName}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
{{.HelpName}} [FLAGS] TARGET [TARGET ...]
|
||||||
|
|
||||||
|
FLAGS:
|
||||||
|
{{range .VisibleFlags}}{{.}}
|
||||||
|
{{end}}
|
||||||
|
EXAMPLES:
|
||||||
|
1. Frobbly runs with this param locally.
|
||||||
|
$ {{.HelpName}} wobbly
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
app.Writer = output
|
||||||
|
app.Run([]string{"foo", "help", "frobbly"})
|
||||||
|
|
||||||
|
if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") {
|
||||||
|
t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") {
|
||||||
|
t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "$ foo frobbly wobbly") {
|
||||||
|
t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShowSubcommandHelp_CommandUsageText(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "frobbly",
|
||||||
|
UsageText: "this is usage text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
app.Writer = output
|
||||||
|
|
||||||
|
app.Run([]string{"foo", "frobbly", "--help"})
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "this is usage text") {
|
||||||
|
t.Errorf("expected output to include usage text; got: %q", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "frobbly",
|
||||||
|
Subcommands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "bobbly",
|
||||||
|
UsageText: "this is usage text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
app.Writer = output
|
||||||
|
app.Run([]string{"foo", "frobbly", "bobbly", "--help"})
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "this is usage text") {
|
||||||
|
t.Errorf("expected output to include usage text; got: %q", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
||||||
app := &App{
|
app := &App{
|
||||||
Commands: []*Command{
|
Commands: []*Command{
|
||||||
@ -286,3 +374,78 @@ func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
|||||||
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShowAppHelp_CustomAppTemplate(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []*Command{
|
||||||
|
{
|
||||||
|
Name: "frobbly",
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "secretfrob",
|
||||||
|
Hidden: true,
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExtraInfo: func() map[string]string {
|
||||||
|
platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU())
|
||||||
|
return map[string]string{
|
||||||
|
"PLATFORM": platform,
|
||||||
|
"RUNTIME": goruntime,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CustomAppHelpTemplate: `NAME:
|
||||||
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
{{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...]
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
{{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||||
|
{{end}}{{if .VisibleFlags}}
|
||||||
|
GLOBAL FLAGS:
|
||||||
|
{{range .VisibleFlags}}{{.}}
|
||||||
|
{{end}}{{end}}
|
||||||
|
VERSION:
|
||||||
|
2.0.0
|
||||||
|
{{"\n"}}{{range $key, $value := ExtraInfo}}
|
||||||
|
{{$key}}:
|
||||||
|
{{$value}}
|
||||||
|
{{end}}`,
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
app.Writer = output
|
||||||
|
app.Run([]string{"app", "--help"})
|
||||||
|
|
||||||
|
if strings.Contains(output.String(), "secretfrob") {
|
||||||
|
t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "frobbly") {
|
||||||
|
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "PLATFORM:") ||
|
||||||
|
!strings.Contains(output.String(), "OS:") ||
|
||||||
|
!strings.Contains(output.String(), "Arch:") {
|
||||||
|
t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "RUNTIME:") ||
|
||||||
|
!strings.Contains(output.String(), "Version:") ||
|
||||||
|
!strings.Contains(output.String(), "CPUs:") {
|
||||||
|
t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "VERSION:") ||
|
||||||
|
!strings.Contains(output.String(), "2.0.0") {
|
||||||
|
t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
runtests
30
runtests
@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from __future__ import print_function
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import codecs
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
@ -12,6 +13,7 @@ import tempfile
|
|||||||
from subprocess import check_call, check_output
|
from subprocess import check_call, check_output
|
||||||
|
|
||||||
|
|
||||||
|
_PY3 = sys.version_info.major == 3
|
||||||
_WINDOWS = platform.system().lower() == 'windows'
|
_WINDOWS = platform.system().lower() == 'windows'
|
||||||
_PACKAGE_NAME = os.environ.get(
|
_PACKAGE_NAME = os.environ.get(
|
||||||
'CLI_PACKAGE_NAME', 'github.com/urfave/cli'
|
'CLI_PACKAGE_NAME', 'github.com/urfave/cli'
|
||||||
@ -37,7 +39,7 @@ def _target(func):
|
|||||||
|
|
||||||
@_target
|
@_target
|
||||||
def _test():
|
def _test():
|
||||||
if check_output('go version'.split()).split()[2] < 'go1.2':
|
if _go_version() < 'go1.2':
|
||||||
_run('go test -v .')
|
_run('go test -v .')
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -61,7 +63,7 @@ def _test():
|
|||||||
|
|
||||||
@_target
|
@_target
|
||||||
def _gfmrun():
|
def _gfmrun():
|
||||||
go_version = check_output('go version'.split()).split()[2]
|
go_version = _go_version()
|
||||||
if go_version < 'go1.3':
|
if go_version < 'go1.3':
|
||||||
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
|
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
|
||||||
return
|
return
|
||||||
@ -75,7 +77,7 @@ def _vet():
|
|||||||
|
|
||||||
@_target
|
@_target
|
||||||
def _migrations():
|
def _migrations():
|
||||||
go_version = check_output('go version'.split()).split()[2]
|
go_version = _go_version()
|
||||||
if go_version < 'go1.3':
|
if go_version < 'go1.3':
|
||||||
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
|
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
|
||||||
return
|
return
|
||||||
@ -115,7 +117,7 @@ def _toc():
|
|||||||
|
|
||||||
@_target
|
@_target
|
||||||
def _gen():
|
def _gen():
|
||||||
go_version = check_output('go version'.split()).split()[2]
|
go_version = _go_version()
|
||||||
if go_version < 'go1.5':
|
if go_version < 'go1.5':
|
||||||
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
|
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
|
||||||
return
|
return
|
||||||
@ -133,23 +135,29 @@ def _run(command):
|
|||||||
|
|
||||||
|
|
||||||
def _gfmrun_count():
|
def _gfmrun_count():
|
||||||
with open('README.md') as infile:
|
with codecs.open('README.md', 'r', 'utf-8') as infile:
|
||||||
lines = infile.read().splitlines()
|
lines = infile.read().splitlines()
|
||||||
return len(filter(_is_go_runnable, lines))
|
return len(list(filter(_is_go_runnable, lines)))
|
||||||
|
|
||||||
|
|
||||||
def _is_go_runnable(line):
|
def _is_go_runnable(line):
|
||||||
return line.startswith('package main')
|
return line.startswith('package main')
|
||||||
|
|
||||||
|
|
||||||
|
def _go_version():
|
||||||
|
return check_output('go version'.split()).decode('utf-8').split()[2]
|
||||||
|
|
||||||
|
|
||||||
def _combine_coverprofiles(coverprofiles):
|
def _combine_coverprofiles(coverprofiles):
|
||||||
combined = tempfile.NamedTemporaryFile(
|
tmp_args = dict(suffix='.coverprofile', mode='w', delete=False)
|
||||||
suffix='.coverprofile', delete=False
|
if _PY3:
|
||||||
)
|
tmp_args['encoding'] = 'utf-8'
|
||||||
|
|
||||||
|
combined = tempfile.NamedTemporaryFile(**tmp_args)
|
||||||
combined.write('mode: set\n')
|
combined.write('mode: set\n')
|
||||||
|
|
||||||
for coverprofile in coverprofiles:
|
for coverprofile in coverprofiles:
|
||||||
with open(coverprofile, 'r') as infile:
|
with codecs.open(coverprofile, 'r', 'utf-8') as infile:
|
||||||
for line in infile.readlines():
|
for line in infile.readlines():
|
||||||
if not line.startswith('mode: '):
|
if not line.startswith('mode: '):
|
||||||
combined.write(line)
|
combined.write(line)
|
||||||
|
Loading…
Reference in New Issue
Block a user