From un-tagged pervious version : https://github.com/urfave/cli/compare/d86a009f5e13...mastertags/v1.9.0-rc1
@@ -109,7 +109,7 @@ require ( | |||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect | |||
github.com/tinylib/msgp v0.0.0-20180516164116-c8cf64dff200 // indirect | |||
github.com/tstranex/u2f v1.0.0 | |||
github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 | |||
github.com/urfave/cli v1.20.0 | |||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 // indirect | |||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 | |||
go.etcd.io/bbolt v1.3.2 // indirect |
@@ -221,8 +221,6 @@ github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK86 | |||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | |||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= | |||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= | |||
github.com/mcuadros/go-version v0.0.0-20171003094716-88e56e02bea1 h1:4yXas1DDHpauOfuyfmAMm+EB+SiqPKEoTdc88XEJHsc= | |||
github.com/mcuadros/go-version v0.0.0-20171003094716-88e56e02bea1/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= | |||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 h1:Pijfgr7ZuvX7QIQiEwLdRVr3RoMG+i0SbBO1Qu+7yVk= | |||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo= | |||
github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a h1:d18LCO3ctH2kugUqt0pEyKKP8L+IYrocaPqGFilhTKk= | |||
@@ -299,8 +297,8 @@ github.com/tinylib/msgp v0.0.0-20180516164116-c8cf64dff200 h1:ZVvr38DYEyOPyelySq | |||
github.com/tinylib/msgp v0.0.0-20180516164116-c8cf64dff200/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= | |||
github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ= | |||
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo= | |||
github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 h1:niRuEF0NOlFnqraxzjuvvOdCM6gxmHiaBABjvg3/kDo= | |||
github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= | |||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= | |||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= | |||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 h1:E8u341JM/N8LCnPXBV6ZFD1RKo/j+qHl1XOqSV+GstA= | |||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= | |||
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro= | |||
@@ -359,8 +357,6 @@ gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek | |||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= | |||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs= | |||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= | |||
gopkg.in/src-d/go-git.v4 v4.8.0 h1:dDEbgvfNG9vUDM54uhCYPExiGa8uYgXpQ/MR8YvxcAM= | |||
gopkg.in/src-d/go-git.v4 v4.8.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= | |||
gopkg.in/src-d/go-git.v4 v4.10.0 h1:NWjTJTQnk8UpIGlssuefyDZ6JruEjo5s88vm88uASbw= | |||
gopkg.in/src-d/go-git.v4 v4.10.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= | |||
gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M= |
@@ -0,0 +1,2 @@ | |||
[flake8] | |||
max-line-length = 120 |
@@ -1,32 +1,20 @@ | |||
language: go | |||
sudo: false | |||
dist: trusty | |||
osx_image: xcode8.3 | |||
go: 1.8.x | |||
os: | |||
- linux | |||
- osx | |||
cache: | |||
directories: | |||
- node_modules | |||
go: | |||
- 1.2.x | |||
- 1.3.x | |||
- 1.4.2 | |||
- 1.5.x | |||
- 1.6.x | |||
- 1.7.x | |||
- master | |||
matrix: | |||
allow_failures: | |||
- go: master | |||
include: | |||
- go: 1.6.x | |||
os: osx | |||
- go: 1.7.x | |||
os: osx | |||
before_script: | |||
- go get github.com/urfave/gfmrun/... || true | |||
- go get golang.org/x/tools/... || true | |||
- go get golang.org/x/tools/cmd/goimports | |||
- if [ ! -f node_modules/.bin/markdown-toc ] ; then | |||
npm install markdown-toc ; | |||
fi |
@@ -3,14 +3,105 @@ | |||
**ATTN**: This project uses [semantic versioning](http://semver.org/). | |||
## [Unreleased] | |||
## 1.20.0 - 2017-08-10 | |||
### Fixed | |||
* `HandleExitCoder` is now correctly iterates over all errors in | |||
a `MultiError`. The exit code is the exit code of the last error or `1` if | |||
there are no `ExitCoder`s in the `MultiError`. | |||
* Fixed YAML file loading on Windows (previously would fail validate the file path) | |||
* Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly | |||
propogated | |||
* `ErrWriter` is now passed downwards through command structure to avoid the | |||
need to redefine it | |||
* Pass `Command` context into `OnUsageError` rather than parent context so that | |||
all fields are avaiable | |||
* Errors occuring in `Before` funcs are no longer double printed | |||
* Use `UsageText` in the help templates for commands and subcommands if | |||
defined; otherwise build the usage as before (was previously ignoring this | |||
field) | |||
* `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if | |||
a program calls `Set` or `GlobalSet` directly after flag parsing (would | |||
previously only return `true` if the flag was set during parsing) | |||
### Changed | |||
* No longer exit the program on command/subcommand error if the error raised is | |||
not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was | |||
determined to be a regression in functionality. See [the | |||
PR](https://github.com/urfave/cli/pull/595) for discussion. | |||
### Added | |||
* `CommandsByName` type was added to make it easy to sort `Command`s by name, | |||
alphabetically | |||
* `altsrc` now handles loading of string and int arrays from TOML | |||
* Support for definition of custom help templates for `App` via | |||
`CustomAppHelpTemplate` | |||
* Support for arbitrary key/value fields on `App` to be used with | |||
`CustomAppHelpTemplate` via `ExtraInfo` | |||
* `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be | |||
`cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag` | |||
interface to be used. | |||
## [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` | |||
- Write to stderr and exit 1 if action returns non-nil error | |||
- 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 | |||
- 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 | |||
### Added | |||
- `./runtests` test runner with coverage tracking by default | |||
@@ -29,6 +120,10 @@ | |||
- No longer swallows `panic`s that occur within the `Action`s themselves when | |||
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 | |||
### Added | |||
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` | |||
@@ -50,6 +145,10 @@ | |||
- cleanups based on [Go Report Card | |||
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 | |||
### Added | |||
- `Hidden` field on all flag struct types to omit from generated help text |
@@ -455,13 +455,13 @@ error. | |||
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` | |||
with `sort`. | |||
or `CommandsByName` with `sort`. | |||
For example this: | |||
<!-- { | |||
"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 | |||
package main | |||
@@ -488,7 +488,27 @@ func main() { | |||
}, | |||
} | |||
app.Commands = []cli.Command{ | |||
{ | |||
Name: "complete", | |||
Aliases: []string{"c"}, | |||
Usage: "complete a task on the list", | |||
Action: func(c *cli.Context) error { | |||
return nil | |||
}, | |||
}, | |||
{ | |||
Name: "add", | |||
Aliases: []string{"a"}, | |||
Usage: "add a task to the list", | |||
Action: func(c *cli.Context) error { | |||
return nil | |||
}, | |||
}, | |||
} | |||
sort.Sort(cli.FlagsByName(app.Flags)) | |||
sort.Sort(cli.CommandsByName(app.Commands)) | |||
app.Run(os.Args) | |||
} | |||
@@ -940,16 +960,13 @@ SUPPORT: support@awesometown.example.com | |||
cli.AppHelpTemplate = `NAME: | |||
{{.Name}} - {{.Usage}} | |||
USAGE: | |||
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command | |||
[command options]{{end}} {{if | |||
.ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} | |||
{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} | |||
{{if len .Authors}} | |||
AUTHOR(S): | |||
AUTHOR: | |||
{{range .Authors}}{{ . }}{{end}} | |||
{{end}}{{if .Commands}} | |||
COMMANDS: | |||
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t" | |||
}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} | |||
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} | |||
GLOBAL OPTIONS: | |||
{{range .VisibleFlags}}{{.}} | |||
{{end}}{{end}}{{if .Copyright }} |
@@ -85,6 +85,12 @@ type App struct { | |||
ErrWriter io.Writer | |||
// Other custom info | |||
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 | |||
} | |||
@@ -145,10 +151,6 @@ func (a *App) Setup() { | |||
} | |||
} | |||
if a.EnableBashCompletion { | |||
a.appendFlag(BashCompletionFlag) | |||
} | |||
if !a.HideVersion { | |||
a.appendFlag(VersionFlag) | |||
} | |||
@@ -173,8 +175,20 @@ func (a *App) Setup() { | |||
func (a *App) Run(arguments []string) (err error) { | |||
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 | |||
set := flagSet(a.Name, a.Flags) | |||
set, err := flagSet(a.Name, a.Flags) | |||
if err != nil { | |||
return err | |||
} | |||
set.SetOutput(ioutil.Discard) | |||
err = set.Parse(arguments[1:]) | |||
nerr := normalizeFlags(a.Flags, set) | |||
@@ -184,6 +198,7 @@ func (a *App) Run(arguments []string) (err error) { | |||
ShowAppHelp(context) | |||
return nerr | |||
} | |||
context.shellComplete = shellComplete | |||
if checkCompletions(context) { | |||
return nil | |||
@@ -225,7 +240,6 @@ func (a *App) Run(arguments []string) (err error) { | |||
if a.Before != nil { | |||
beforeErr := a.Before(context) | |||
if beforeErr != nil { | |||
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr) | |||
ShowAppHelp(context) | |||
HandleExitCoder(beforeErr) | |||
err = beforeErr | |||
@@ -242,6 +256,10 @@ func (a *App) Run(arguments []string) (err error) { | |||
} | |||
} | |||
if a.Action == nil { | |||
a.Action = helpCommand.Action | |||
} | |||
// Run default Action | |||
err = HandleAction(a.Action, context) | |||
@@ -283,13 +301,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { | |||
} | |||
a.Commands = newCmds | |||
// append flags | |||
if a.EnableBashCompletion { | |||
a.appendFlag(BashCompletionFlag) | |||
// parse flags | |||
set, err := flagSet(a.Name, a.Flags) | |||
if err != nil { | |||
return err | |||
} | |||
// parse flags | |||
set := flagSet(a.Name, a.Flags) | |||
set.SetOutput(ioutil.Discard) | |||
err = set.Parse(ctx.Args().Tail()) | |||
nerr := normalizeFlags(a.Flags, set) | |||
@@ -467,7 +484,9 @@ func (a Author) String() string { | |||
// it's an ActionFunc or a func with the legacy signature for Action, the func | |||
// is run! | |||
func HandleAction(action interface{}, context *Context) (err error) { | |||
if a, ok := action.(func(*Context) error); ok { | |||
if a, ok := action.(ActionFunc); ok { | |||
return a(context) | |||
} else if a, ok := action.(func(*Context) error); ok { | |||
return a(context) | |||
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature | |||
a(context) |
@@ -1,14 +1,16 @@ | |||
version: "{build}" | |||
os: Windows Server 2012 R2 | |||
os: Windows Server 2016 | |||
image: Visual Studio 2017 | |||
clone_folder: c:\gopath\src\github.com\urfave\cli | |||
environment: | |||
GOPATH: C:\gopath | |||
GOVERSION: 1.6 | |||
PYTHON: C:\Python27-x64 | |||
PYTHON_VERSION: 2.7.x | |||
GOVERSION: 1.8.x | |||
PYTHON: C:\Python36-x64 | |||
PYTHON_VERSION: 3.6.x | |||
PYTHON_ARCH: 64 | |||
install: |
@@ -12,6 +12,7 @@ | |||
// app.Usage = "say a greeting" | |||
// app.Action = func(c *cli.Context) error { | |||
// println("Greetings") | |||
// return nil | |||
// } | |||
// | |||
// app.Run(os.Args) |
@@ -59,6 +59,25 @@ type Command struct { | |||
// Full name of command for help, defaults to full command name, including parent commands. | |||
HelpName 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. | |||
@@ -87,11 +106,10 @@ func (c Command) Run(ctx *Context) (err error) { | |||
) | |||
} | |||
if ctx.App.EnableBashCompletion { | |||
c.Flags = append(c.Flags, BashCompletionFlag) | |||
set, err := flagSet(c.Name, c.Flags) | |||
if err != nil { | |||
return err | |||
} | |||
set := flagSet(c.Name, c.Flags) | |||
set.SetOutput(ioutil.Discard) | |||
if c.SkipFlagParsing { | |||
@@ -132,18 +150,6 @@ func (c Command) Run(ctx *Context) (err error) { | |||
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) | |||
if nerr != nil { | |||
fmt.Fprintln(ctx.App.Writer, nerr) | |||
@@ -153,11 +159,23 @@ func (c Command) Run(ctx *Context) (err error) { | |||
} | |||
context := NewContext(ctx.App, set, ctx) | |||
context.Command = c | |||
if checkCommandCompletions(context, c.Name) { | |||
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) { | |||
return nil | |||
} | |||
@@ -179,15 +197,16 @@ func (c Command) Run(ctx *Context) (err error) { | |||
if c.Before != nil { | |||
err = c.Before(context) | |||
if err != nil { | |||
fmt.Fprintln(ctx.App.Writer, err) | |||
fmt.Fprintln(ctx.App.Writer) | |||
ShowCommandHelp(ctx, c.Name) | |||
ShowCommandHelp(context, c.Name) | |||
HandleExitCoder(err) | |||
return err | |||
} | |||
} | |||
context.Command = c | |||
if c.Action == nil { | |||
c.Action = helpSubcommand.Action | |||
} | |||
err = HandleAction(c.Action, context) | |||
if err != nil { | |||
@@ -228,14 +247,13 @@ func (c Command) startApp(ctx *Context) error { | |||
app.HelpName = app.Name | |||
} | |||
if c.Description != "" { | |||
app.Usage = c.Description | |||
} else { | |||
app.Usage = c.Usage | |||
} | |||
app.Usage = c.Usage | |||
app.Description = c.Description | |||
app.ArgsUsage = c.ArgsUsage | |||
// set CommandNotFound | |||
app.CommandNotFound = ctx.App.CommandNotFound | |||
app.CustomAppHelpTemplate = c.CustomHelpTemplate | |||
// set the flags and commands | |||
app.Commands = c.Subcommands | |||
@@ -248,6 +266,7 @@ func (c Command) startApp(ctx *Context) error { | |||
app.Author = ctx.App.Author | |||
app.Email = ctx.App.Email | |||
app.Writer = ctx.App.Writer | |||
app.ErrWriter = ctx.App.ErrWriter | |||
app.categories = CommandCategories{} | |||
for _, command := range c.Subcommands { | |||
@@ -270,6 +289,7 @@ func (c Command) startApp(ctx *Context) error { | |||
} else { | |||
app.Action = helpSubcommand.Action | |||
} | |||
app.OnUsageError = c.OnUsageError | |||
for index, cc := range app.Commands { | |||
app.Commands[index].commandNamePath = []string{c.Name, cc.Name} |
@@ -3,9 +3,9 @@ package cli | |||
import ( | |||
"errors" | |||
"flag" | |||
"os" | |||
"reflect" | |||
"strings" | |||
"syscall" | |||
) | |||
// Context is a type that is passed through to | |||
@@ -15,6 +15,7 @@ import ( | |||
type Context struct { | |||
App *App | |||
Command Command | |||
shellComplete bool | |||
flagSet *flag.FlagSet | |||
setFlags map[string]bool | |||
parentContext *Context | |||
@@ -22,7 +23,13 @@ type Context struct { | |||
// 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 { | |||
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 | |||
@@ -32,11 +39,13 @@ func (c *Context) NumFlags() int { | |||
// Set sets a context flag to a value. | |||
func (c *Context) Set(name, value string) error { | |||
c.setFlags = nil | |||
return c.flagSet.Set(name, value) | |||
} | |||
// GlobalSet sets a context flag to a value on the global flagset | |||
func (c *Context) GlobalSet(name, value string) error { | |||
globalContext(c).setFlags = nil | |||
return globalContext(c).flagSet.Set(name, value) | |||
} | |||
@@ -91,7 +100,7 @@ func (c *Context) IsSet(name string) bool { | |||
eachName(envVarValue.String(), func(envVar string) { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if _, ok := syscall.Getenv(envVar); ok { | |||
c.setFlags[name] = true | |||
return | |||
} | |||
@@ -147,6 +156,11 @@ func (c *Context) Parent() *Context { | |||
return c.parentContext | |||
} | |||
// value returns the value of the flag coressponding to `name` | |||
func (c *Context) value(name string) interface{} { | |||
return c.flagSet.Lookup(name).Value.(flag.Getter).Get() | |||
} | |||
// Args contains apps console arguments | |||
type Args []string | |||
@@ -34,6 +34,10 @@ func (m MultiError) Error() string { | |||
return strings.Join(errs, "\n") | |||
} | |||
type ErrorFormatter interface { | |||
Format(s fmt.State, verb rune) | |||
} | |||
// ExitCoder is the interface checked by `App` and `Command` for a custom exit | |||
// code | |||
type ExitCoder interface { | |||
@@ -44,11 +48,11 @@ type ExitCoder interface { | |||
// ExitError fulfills both the builtin `error` interface and `ExitCoder` | |||
type ExitError struct { | |||
exitCode int | |||
message string | |||
message interface{} | |||
} | |||
// NewExitError makes a new *ExitError | |||
func NewExitError(message string, exitCode int) *ExitError { | |||
func NewExitError(message interface{}, exitCode int) *ExitError { | |||
return &ExitError{ | |||
exitCode: exitCode, | |||
message: message, | |||
@@ -58,7 +62,7 @@ func NewExitError(message string, exitCode int) *ExitError { | |||
// Error returns the string message, fulfilling the interface required by | |||
// `error` | |||
func (ee *ExitError) Error() string { | |||
return ee.message | |||
return fmt.Sprintf("%v", ee.message) | |||
} | |||
// ExitCode returns the exit code, fulfilling the interface required by | |||
@@ -70,7 +74,7 @@ func (ee *ExitError) ExitCode() int { | |||
// 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 | |||
// 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) { | |||
if err == nil { | |||
return | |||
@@ -78,21 +82,34 @@ func HandleExitCoder(err error) { | |||
if exitErr, ok := err.(ExitCoder); ok { | |||
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()) | |||
return | |||
} | |||
if multiErr, ok := err.(MultiError); ok { | |||
for _, merr := range multiErr.Errors { | |||
HandleExitCoder(merr) | |||
} | |||
code := handleMultiError(multiErr) | |||
OsExiter(code) | |||
return | |||
} | |||
} | |||
if err.Error() != "" { | |||
fmt.Fprintln(ErrWriter, err) | |||
func handleMultiError(multiErr MultiError) int { | |||
code := 1 | |||
for _, merr := range multiErr.Errors { | |||
if multiErr2, ok := merr.(MultiError); ok { | |||
code = handleMultiError(multiErr2) | |||
} else { | |||
fmt.Fprintln(ErrWriter, merr) | |||
if exitErr, ok := merr.(ExitCoder); ok { | |||
code = exitErr.ExitCode() | |||
} | |||
} | |||
} | |||
OsExiter(1) | |||
return code | |||
} |
@@ -3,24 +3,24 @@ package cli | |||
import ( | |||
"flag" | |||
"fmt" | |||
"os" | |||
"reflect" | |||
"runtime" | |||
"strconv" | |||
"strings" | |||
"syscall" | |||
"time" | |||
) | |||
const defaultPlaceholder = "value" | |||
// BashCompletionFlag enables bash-completion for all commands and subcommands | |||
var BashCompletionFlag = BoolFlag{ | |||
var BashCompletionFlag Flag = BoolFlag{ | |||
Name: "generate-bash-completion", | |||
Hidden: true, | |||
} | |||
// VersionFlag prints the version for the application | |||
var VersionFlag = BoolFlag{ | |||
var VersionFlag Flag = BoolFlag{ | |||
Name: "version, v", | |||
Usage: "print the version", | |||
} | |||
@@ -28,7 +28,7 @@ var VersionFlag = BoolFlag{ | |||
// HelpFlag prints the help for all commands and subcommands | |||
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand | |||
// unless HideHelp is set to true) | |||
var HelpFlag = BoolFlag{ | |||
var HelpFlag Flag = BoolFlag{ | |||
Name: "help, h", | |||
Usage: "show help", | |||
} | |||
@@ -62,13 +62,29 @@ type Flag interface { | |||
GetName() string | |||
} | |||
func flagSet(name string, flags []Flag) *flag.FlagSet { | |||
// errorableFlag is an interface that allows us to return errors during apply | |||
// it allows flags defined in this library to return errors in a fashion backwards compatible | |||
// TODO remove in v2 and modify the existing Flag interface to return errors | |||
type errorableFlag interface { | |||
Flag | |||
ApplyWithError(*flag.FlagSet) error | |||
} | |||
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { | |||
set := flag.NewFlagSet(name, flag.ContinueOnError) | |||
for _, f := range flags { | |||
f.Apply(set) | |||
//TODO remove in v2 when errorableFlag is removed | |||
if ef, ok := f.(errorableFlag); ok { | |||
if err := ef.ApplyWithError(set); err != nil { | |||
return nil, err | |||
} | |||
} else { | |||
f.Apply(set) | |||
} | |||
} | |||
return set | |||
return set, nil | |||
} | |||
func eachName(longName string, fn func(string)) { | |||
@@ -87,13 +103,22 @@ type Generic interface { | |||
// Apply takes the flagset and calls Set on the generic flag with the value | |||
// provided by the user for parsing by the flag | |||
// Ignores parsing errors | |||
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 | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
val.Set(envVal) | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
if err := val.Set(envVal); err != nil { | |||
return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
break | |||
} | |||
} | |||
@@ -102,9 +127,11 @@ func (f GenericFlag) Apply(set *flag.FlagSet) { | |||
eachName(f.Name, func(name string) { | |||
set.Var(f.Value, name, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// StringSlice is an opaque type for []string to satisfy flag.Value | |||
// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter | |||
type StringSlice []string | |||
// Set appends the string value to the list of values | |||
@@ -123,16 +150,29 @@ func (f *StringSlice) Value() []string { | |||
return *f | |||
} | |||
// Get returns the slice of strings set by this flag | |||
func (f *StringSlice) Get() interface{} { | |||
return *f | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f StringSliceFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
newVal := &StringSlice{} | |||
for _, s := range strings.Split(envVal, ",") { | |||
s = strings.TrimSpace(s) | |||
newVal.Set(s) | |||
if err := newVal.Set(s); err != nil { | |||
return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
} | |||
f.Value = newVal | |||
break | |||
@@ -146,9 +186,11 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Var(f.Value, name, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// IntSlice is an opaque type for []int to satisfy flag.Value | |||
// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter | |||
type IntSlice []int | |||
// Set parses the value into an integer and appends it to the list of values | |||
@@ -171,18 +213,28 @@ func (f *IntSlice) Value() []int { | |||
return *f | |||
} | |||
// 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 | |||
// Ignores errors | |||
func (f IntSliceFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
newVal := &IntSlice{} | |||
for _, s := range strings.Split(envVal, ",") { | |||
s = strings.TrimSpace(s) | |||
err := newVal.Set(s) | |||
if err != nil { | |||
fmt.Fprintf(ErrWriter, err.Error()) | |||
if err := newVal.Set(s); err != nil { | |||
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
} | |||
f.Value = newVal | |||
@@ -197,9 +249,11 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Var(f.Value, name, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Int64Slice is an opaque type for []int to satisfy flag.Value | |||
// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter | |||
type Int64Slice []int64 | |||
// Set parses the value into an integer and appends it to the list of values | |||
@@ -222,18 +276,28 @@ func (f *Int64Slice) Value() []int64 { | |||
return *f | |||
} | |||
// 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 | |||
// Ignores errors | |||
func (f Int64SliceFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
newVal := &Int64Slice{} | |||
for _, s := range strings.Split(envVal, ",") { | |||
s = strings.TrimSpace(s) | |||
err := newVal.Set(s) | |||
if err != nil { | |||
fmt.Fprintf(ErrWriter, err.Error()) | |||
if err := newVal.Set(s); err != nil { | |||
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
} | |||
f.Value = newVal | |||
@@ -248,19 +312,33 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Var(f.Value, name, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f BoolFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { | |||
val := false | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
if envVal == "" { | |||
val = false | |||
break | |||
} | |||
envValBool, err := strconv.ParseBool(envVal) | |||
if err == nil { | |||
val = envValBool | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
val = envValBool | |||
break | |||
} | |||
} | |||
@@ -273,20 +351,35 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Bool(name, val, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f BoolTFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { | |||
val := true | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
envValBool, err := strconv.ParseBool(envVal) | |||
if err == nil { | |||
val = envValBool | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
if envVal == "" { | |||
val = false | |||
break | |||
} | |||
envValBool, err := strconv.ParseBool(envVal) | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
val = envValBool | |||
break | |||
} | |||
} | |||
} | |||
@@ -298,14 +391,22 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Bool(name, val, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f StringFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
f.Value = envVal | |||
break | |||
} | |||
@@ -319,19 +420,28 @@ func (f StringFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.String(name, f.Value, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f IntFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
envValInt, err := strconv.ParseInt(envVal, 0, 64) | |||
if err == nil { | |||
f.Value = int(envValInt) | |||
break | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
f.Value = int(envValInt) | |||
break | |||
} | |||
} | |||
} | |||
@@ -343,19 +453,29 @@ func (f IntFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Int(name, f.Value, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f Int64Flag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
envValInt, err := strconv.ParseInt(envVal, 0, 64) | |||
if err == nil { | |||
f.Value = envValInt | |||
break | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
f.Value = envValInt | |||
break | |||
} | |||
} | |||
} | |||
@@ -367,19 +487,29 @@ func (f Int64Flag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Int64(name, f.Value, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f UintFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
envValInt, err := strconv.ParseUint(envVal, 0, 64) | |||
if err == nil { | |||
f.Value = uint(envValInt) | |||
break | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
f.Value = uint(envValInt) | |||
break | |||
} | |||
} | |||
} | |||
@@ -391,19 +521,29 @@ func (f UintFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Uint(name, f.Value, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f Uint64Flag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
envValInt, err := strconv.ParseUint(envVal, 0, 64) | |||
if err == nil { | |||
f.Value = uint64(envValInt) | |||
break | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
f.Value = uint64(envValInt) | |||
break | |||
} | |||
} | |||
} | |||
@@ -415,19 +555,29 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Uint64(name, f.Value, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f DurationFlag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
envValDuration, err := time.ParseDuration(envVal) | |||
if err == nil { | |||
f.Value = envValDuration | |||
break | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) | |||
} | |||
f.Value = envValDuration | |||
break | |||
} | |||
} | |||
} | |||
@@ -439,18 +589,29 @@ func (f DurationFlag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Duration(name, f.Value, f.Usage) | |||
}) | |||
return nil | |||
} | |||
// Apply populates the flag given the flag set and environment | |||
// Ignores errors | |||
func (f Float64Flag) Apply(set *flag.FlagSet) { | |||
f.ApplyWithError(set) | |||
} | |||
// ApplyWithError populates the flag given the flag set and environment | |||
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { | |||
if f.EnvVar != "" { | |||
for _, envVar := range strings.Split(f.EnvVar, ",") { | |||
envVar = strings.TrimSpace(envVar) | |||
if envVal := os.Getenv(envVar); envVal != "" { | |||
if envVal, ok := syscall.Getenv(envVar); ok { | |||
envValFloat, err := strconv.ParseFloat(envVal, 10) | |||
if err == nil { | |||
f.Value = float64(envValFloat) | |||
if err != nil { | |||
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) | |||
} | |||
f.Value = float64(envValFloat) | |||
break | |||
} | |||
} | |||
} | |||
@@ -462,12 +623,15 @@ func (f Float64Flag) Apply(set *flag.FlagSet) { | |||
} | |||
set.Float64(name, f.Value, f.Usage) | |||
}) | |||
return nil | |||
} | |||
func visibleFlags(fl []Flag) []Flag { | |||
visible := []Flag{} | |||
for _, flag := range fl { | |||
if !flagValue(flag).FieldByName("Hidden").Bool() { | |||
field := flagValue(flag).FieldByName("Hidden") | |||
if !field.IsValid() || !field.Bool() { | |||
visible = append(visible, flag) | |||
} | |||
} | |||
@@ -560,9 +724,8 @@ func stringifyFlag(f Flag) string { | |||
needsPlaceholder := false | |||
defaultValueString := "" | |||
val := fv.FieldByName("Value") | |||
if val.IsValid() { | |||
if val := fv.FieldByName("Value"); val.IsValid() { | |||
needsPlaceholder = true | |||
defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface()) | |||
@@ -232,6 +232,13 @@ def _write_altsrc_flag_types(outfile, types): | |||
f.set = 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)) | |||
@@ -13,7 +13,7 @@ import ( | |||
// cli.go uses text/template to render templates. You can | |||
// render custom help text by setting this variable. | |||
var AppHelpTemplate = `NAME: | |||
{{.Name}} - {{.Usage}} | |||
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}} | |||
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}} | |||
@@ -47,7 +47,7 @@ var CommandHelpTemplate = `NAME: | |||
{{.HelpName}} - {{.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}}{{end}}{{if .Description}} | |||
@@ -64,10 +64,10 @@ OPTIONS: | |||
// cli.go uses text/template to render templates. You can | |||
// render custom help text by setting this variable. | |||
var SubcommandHelpTemplate = `NAME: | |||
{{.HelpName}} - {{.Usage}} | |||
{{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} | |||
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}} | |||
{{.Name}}:{{end}}{{range .VisibleCommands}} | |||
@@ -112,17 +112,42 @@ var helpSubcommand = Command{ | |||
// Prints help for the App or Command | |||
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 | |||
// is used. The function signature is: | |||
// func(w io.Writer, templ string, data interface{}) | |||
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 | |||
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. | |||
func ShowAppHelp(c *Context) error { | |||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) | |||
func ShowAppHelp(c *Context) (err error) { | |||
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 | |||
} | |||
@@ -138,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 | |||
func ShowCommandHelp(ctx *Context, command string) error { | |||
// show the subcommand help for a command with subcommands | |||
@@ -148,7 +179,11 @@ func ShowCommandHelp(ctx *Context, command string) error { | |||
for _, c := range ctx.App.Commands { | |||
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 | |||
} | |||
} | |||
@@ -191,10 +226,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{ | |||
"join": strings.Join, | |||
} | |||
if customFunc != nil { | |||
for key, value := range customFunc { | |||
funcMap[key] = value | |||
} | |||
} | |||
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) | |||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) | |||
@@ -210,10 +250,14 @@ func printHelp(out io.Writer, templ string, data interface{}) { | |||
w.Flush() | |||
} | |||
func printHelp(out io.Writer, templ string, data interface{}) { | |||
printHelpCustom(out, templ, data, nil) | |||
} | |||
func checkVersion(c *Context) bool { | |||
found := false | |||
if VersionFlag.Name != "" { | |||
eachName(VersionFlag.Name, func(name string) { | |||
if VersionFlag.GetName() != "" { | |||
eachName(VersionFlag.GetName(), func(name string) { | |||
if c.GlobalBool(name) || c.Bool(name) { | |||
found = true | |||
} | |||
@@ -224,8 +268,8 @@ func checkVersion(c *Context) bool { | |||
func checkHelp(c *Context) bool { | |||
found := false | |||
if HelpFlag.Name != "" { | |||
eachName(HelpFlag.Name, func(name string) { | |||
if HelpFlag.GetName() != "" { | |||
eachName(HelpFlag.GetName(), func(name string) { | |||
if c.GlobalBool(name) || c.Bool(name) { | |||
found = true | |||
} | |||
@@ -252,20 +296,43 @@ func checkSubcommandHelp(c *Context) bool { | |||
return false | |||
} | |||
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { | |||
if !a.EnableBashCompletion { | |||
return false, arguments | |||
} | |||
pos := len(arguments) - 1 | |||
lastArg := arguments[pos] | |||
if lastArg != "--"+BashCompletionFlag.GetName() { | |||
return false, arguments | |||
} | |||
return true, arguments[:pos] | |||
} | |||
func checkCompletions(c *Context) bool { | |||
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { | |||
ShowCompletions(c) | |||
return true | |||
if !c.shellComplete { | |||
return false | |||
} | |||
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 { | |||
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { | |||
ShowCommandCompletions(c, name) | |||
return true | |||
if !c.shellComplete { | |||
return false | |||
} | |||
return false | |||
ShowCommandCompletions(c, name) | |||
return true | |||
} |
@@ -321,7 +321,7 @@ github.com/syndtr/goleveldb/leveldb/util | |||
github.com/tinylib/msgp/msgp | |||
# github.com/tstranex/u2f v1.0.0 | |||
github.com/tstranex/u2f | |||
# github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 | |||
# github.com/urfave/cli v1.20.0 | |||
github.com/urfave/cli | |||
# github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621 | |||
github.com/willf/bitset |