Merge pull request #121 from jmccann/issue_116

Allow reading tags from file
This commit is contained in:
Brad Rydzewski 2017-04-10 18:30:02 +02:00 committed by GitHub
commit 1ba82511e5
13 changed files with 797 additions and 333 deletions

View File

@ -107,6 +107,7 @@ func main() {
Usage: "build tags", Usage: "build tags",
Value: &cli.StringSlice{"latest"}, Value: &cli.StringSlice{"latest"},
EnvVar: "PLUGIN_TAG,PLUGIN_TAGS", EnvVar: "PLUGIN_TAG,PLUGIN_TAGS",
FilePath: ".tags",
}, },
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "args", Name: "args",

View File

@ -3,13 +3,62 @@
**ATTN**: This project uses [semantic versioning](http://semver.org/). **ATTN**: This project uses [semantic versioning](http://semver.org/).
## [Unreleased] ## [Unreleased]
## [1.19.1] - 2016-11-21
### Fixed
- Fixes regression introduced in 1.19.0 where using an `ActionFunc` as
the `Action` for a command would cause it to error rather than calling the
function. Should not have a affected declarative cases using `func(c
*cli.Context) err)`.
- Shell completion now handles the case where the user specifies
`--generate-bash-completion` immediately after a flag that takes an argument.
Previously it call the application with `--generate-bash-completion` as the
flag value.
## [1.19.0] - 2016-11-19
### Added ### Added
- `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`)
- A `Description` field was added to `App` for a more detailed description of
the application (similar to the existing `Description` field on `Command`)
- Flag type code generation via `go generate` - Flag type code generation via `go generate`
- Write to stderr and exit 1 if action returns non-nil error - Write to stderr and exit 1 if action returns non-nil error
- Added support for TOML to the `altsrc` loader
- `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
@ -28,6 +77,10 @@
- No longer swallows `panic`s that occur within the `Action`s themselves when - No longer swallows `panic`s that occur within the `Action`s themselves when
detecting the signature of the `Action` field detecting the signature of the `Action` field
## [1.17.1] - 2016-08-28
### Fixed
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
## [1.17.0] - 2016-05-09 ## [1.17.0] - 2016-05-09
### Added ### Added
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` - Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc`
@ -49,6 +102,10 @@
- cleanups based on [Go Report Card - cleanups based on [Go Report Card
feedback](https://goreportcard.com/report/github.com/urfave/cli) feedback](https://goreportcard.com/report/github.com/urfave/cli)
## [1.16.1] - 2016-08-28
### Fixed
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
## [1.16.0] - 2016-05-02 ## [1.16.0] - 2016-05-02
### Added ### Added
- `Hidden` field on all flag struct types to omit from generated help text - `Hidden` field on all flag struct types to omit from generated help text

View File

@ -23,15 +23,17 @@ applications in an expressive way.
- [Installation](#installation) - [Installation](#installation)
* [Supported platforms](#supported-platforms) * [Supported platforms](#supported-platforms)
* [Using the `v2` branch](#using-the-v2-branch) * [Using the `v2` branch](#using-the-v2-branch)
* [Pinning to the `v1` branch](#pinning-to-the-v1-branch) * [Pinning to the `v1` releases](#pinning-to-the-v1-releases)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [Examples](#examples) - [Examples](#examples)
* [Arguments](#arguments) * [Arguments](#arguments)
* [Flags](#flags) * [Flags](#flags)
+ [Placeholder Values](#placeholder-values) + [Placeholder Values](#placeholder-values)
+ [Alternate Names](#alternate-names) + [Alternate Names](#alternate-names)
+ [Ordering](#ordering)
+ [Values from the Environment](#values-from-the-environment) + [Values from the Environment](#values-from-the-environment)
+ [Values from alternate input sources (YAML and others)](#values-from-alternate-input-sources-yaml-and-others) + [Values from files](#values-from-files)
+ [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others)
* [Subcommands](#subcommands) * [Subcommands](#subcommands)
* [Subcommands categories](#subcommands-categories) * [Subcommands categories](#subcommands-categories)
* [Exit code](#exit-code) * [Exit code](#exit-code)
@ -104,11 +106,11 @@ import (
... ...
``` ```
### Pinning to the `v1` branch ### Pinning to the `v1` releases
Similarly to the section above describing use of the `v2` branch, if one wants Similarly to the section above describing use of the `v2` branch, if one wants
to avoid any unexpected compatibility pains once `v2` becomes `master`, then to avoid any unexpected compatibility pains once `v2` becomes `master`, then
pinning to the `v1` branch is an acceptable option, e.g.: pinning to `v1` is an acceptable option, e.g.:
``` ```
$ go get gopkg.in/urfave/cli.v1 $ go get gopkg.in/urfave/cli.v1
@ -122,6 +124,8 @@ import (
... ...
``` ```
This will pull the latest tagged `v1` release (e.g. `v1.18.1` at the time of writing).
## Getting Started ## Getting Started
One of the philosophies behind cli is that an API should be playful and full of One of the philosophies behind cli is that an API should be playful and full of
@ -448,6 +452,76 @@ That flag can then be set with `--lang spanish` or `-l spanish`. Note that
giving two different forms of the same flag in the same command invocation is an giving two different forms of the same flag in the same command invocation is an
error. error.
#### Ordering
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`
or `CommandsByName` with `sort`.
For example this:
<!-- {
"args": ["&#45;&#45;help"],
"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
import (
"os"
"sort"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "Language for the greeting",
},
cli.StringFlag{
Name: "config, c",
Usage: "Load configuration from `FILE`",
},
}
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)
}
```
Will result in help output like:
```
--config FILE, -c FILE Load configuration from FILE
--lang value, -l value Language for the greeting (default: "english")
```
#### Values from the Environment #### Values from the Environment
You can also have the default value set from the environment via `EnvVar`. e.g. You can also have the default value set from the environment via `EnvVar`. e.g.
@ -513,10 +587,49 @@ func main() {
} }
``` ```
#### Values from alternate input sources (YAML and others) #### Values from files
You can also have the default value set from file via `FilePath`. e.g.
<!-- {
"args": ["&#45;&#45;help"],
"output": "password for the mysql database"
} -->
``` go
package main
import (
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "password, p",
Usage: "password for the mysql database",
FilePath: "/etc/mysql/password",
},
}
app.Run(os.Args)
}
```
Note that default values set from file (e.g. `FilePath`) take precedence over
default values set from the enviornment (e.g. `EnvVar`).
#### Values from alternate input sources (YAML, TOML, and others)
There is a separate package altsrc that adds support for getting flag values There is a separate package altsrc that adds support for getting flag values
from other input sources like YAML. from other file input sources.
Currently supported input source formats:
* YAML
* TOML
In order to get values for a flag from an alternate input source the following In order to get values for a flag from an alternate input source the following
code would be added to wrap an existing cli.Flag like below: code would be added to wrap an existing cli.Flag like below:
@ -538,9 +651,9 @@ the yaml input source for any flags that are defined on that command. As a note
the "load" flag used would also have to be defined on the command flags in order the "load" flag used would also have to be defined on the command flags in order
for this code snipped to work. for this code snipped to work.
Currently only YAML files are supported but developers can add support for other Currently only the aboved specified formats are supported but developers can
input sources by implementing the altsrc.InputSourceContext for their given add support for other input sources by implementing the
sources. altsrc.InputSourceContext for their given sources.
Here is a more complete sample of a command using YAML support: Here is a more complete sample of a command using YAML support:
@ -883,16 +996,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 }}
@ -948,7 +1058,7 @@ is checked by the cli internals in order to print the `App.Version` via
#### Customization #### Customization
The default flag may be cusomized to something other than `-v/--version` by The default flag may be customized to something other than `-v/--version` by
setting `cli.VersionFlag`, e.g.: setting `cli.VersionFlag`, e.g.:
<!-- { <!-- {

117
vendor/github.com/urfave/cli/app.go generated vendored
View File

@ -6,9 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"sort" "sort"
"strings"
"time" "time"
) )
@ -19,11 +17,8 @@ var (
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
errNonFuncAction = NewExitError("ERROR invalid Action type. "+ errInvalidActionType = NewExitError("ERROR invalid Action type. "+
fmt.Sprintf("Must be a func of type `cli.ActionFunc`. %s", contactSysadmin)+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
errInvalidActionSignature = NewExitError("ERROR invalid Action signature. "+
fmt.Sprintf("Must be `cli.ActionFunc`. %s", contactSysadmin)+
fmt.Sprintf("See %s", appActionDeprecationURL), 2) fmt.Sprintf("See %s", appActionDeprecationURL), 2)
) )
@ -42,6 +37,8 @@ type App struct {
ArgsUsage string ArgsUsage string
// Version of the program // Version of the program
Version string Version string
// Description of the program
Description string
// List of commands to execute // List of commands to execute
Commands []Command Commands []Command
// List of flags to parse // List of flags to parse
@ -62,10 +59,11 @@ type App struct {
// An action to execute after any subcommands are run, but after the subcommand has finished // An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics // It is run even if Action() panics
After AfterFunc After AfterFunc
// The action to execute when no subcommands are specified // The action to execute when no subcommands are specified
// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
// *Note*: support for the deprecated `Action` signature will be removed in a future version
Action interface{} Action interface{}
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
// of deprecation period has passed, maybe?
// Execute this function if the proper command cannot be found // Execute this function if the proper command cannot be found
CommandNotFound CommandNotFoundFunc CommandNotFound CommandNotFoundFunc
@ -147,10 +145,6 @@ func (a *App) Setup() {
} }
} }
if a.EnableBashCompletion {
a.appendFlag(BashCompletionFlag)
}
if !a.HideVersion { if !a.HideVersion {
a.appendFlag(VersionFlag) a.appendFlag(VersionFlag)
} }
@ -164,6 +158,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
@ -171,8 +169,20 @@ func (a *App) Setup() {
func (a *App) Run(arguments []string) (err error) { func (a *App) Run(arguments []string) (err error) {
a.Setup() a.Setup()
// handle the completion flag separately from the flagset since
// completion could be attempted after a flag, but before its value was put
// on the command line. this causes the flagset to interpret the completion
// flag name as the value of the flag before it which is undesirable
// note that we can only do this because the shell autocomplete function
// always appends the completion flag at the end of the command
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
// parse flags // parse flags
set := flagSet(a.Name, a.Flags) set, err := flagSet(a.Name, a.Flags)
if err != nil {
return err
}
set.SetOutput(ioutil.Discard) set.SetOutput(ioutil.Discard)
err = set.Parse(arguments[1:]) err = set.Parse(arguments[1:])
nerr := normalizeFlags(a.Flags, set) nerr := normalizeFlags(a.Flags, set)
@ -182,6 +192,7 @@ func (a *App) Run(arguments []string) (err error) {
ShowAppHelp(context) ShowAppHelp(context)
return nerr return nerr
} }
context.shellComplete = shellComplete
if checkCompletions(context) { if checkCompletions(context) {
return nil return nil
@ -193,7 +204,7 @@ func (a *App) Run(arguments []string) (err error) {
HandleExitCoder(err) HandleExitCoder(err)
return err return err
} }
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
ShowAppHelp(context) ShowAppHelp(context)
return err return err
} }
@ -240,6 +251,10 @@ func (a *App) Run(arguments []string) (err error) {
} }
} }
if a.Action == nil {
a.Action = helpCommand.Action
}
// Run default Action // Run default Action
err = HandleAction(a.Action, context) err = HandleAction(a.Action, context)
@ -247,11 +262,12 @@ func (a *App) Run(arguments []string) (err error) {
return err return err
} }
// DEPRECATED: Another entry point to the cli app, takes care of passing arguments and error handling // RunAndExitOnError calls .Run() and exits non-zero if an error was returned
//
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
// to cli.App.Run. This will cause the application to exit with the given eror
// code in the cli.ExitCoder
func (a *App) RunAndExitOnError() { func (a *App) RunAndExitOnError() {
fmt.Fprintf(a.errWriter(),
"DEPRECATED cli.App.RunAndExitOnError. %s See %s\n",
contactSysadmin, runAndExitOnErrorDeprecationURL)
if err := a.Run(os.Args); err != nil { if err := a.Run(os.Args); err != nil {
fmt.Fprintln(a.errWriter(), err) fmt.Fprintln(a.errWriter(), err)
OsExiter(1) OsExiter(1)
@ -280,13 +296,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
} }
a.Commands = newCmds a.Commands = newCmds
// append flags // parse flags
if a.EnableBashCompletion { set, err := flagSet(a.Name, a.Flags)
a.appendFlag(BashCompletionFlag) if err != nil {
return err
} }
// parse flags
set := flagSet(a.Name, a.Flags)
set.SetOutput(ioutil.Discard) set.SetOutput(ioutil.Discard)
err = set.Parse(ctx.Args().Tail()) err = set.Parse(ctx.Args().Tail())
nerr := normalizeFlags(a.Flags, set) nerr := normalizeFlags(a.Flags, set)
@ -313,7 +328,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
HandleExitCoder(err) HandleExitCoder(err)
return err return err
} }
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.") fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
ShowSubcommandHelp(context) ShowSubcommandHelp(context)
return err return err
} }
@ -454,50 +469,24 @@ type Author struct {
func (a Author) String() string { func (a Author) String() string {
e := "" e := ""
if a.Email != "" { if a.Email != "" {
e = "<" + a.Email + "> " e = " <" + a.Email + ">"
} }
return fmt.Sprintf("%v %v", a.Name, e) return fmt.Sprintf("%v%v", a.Name, e)
} }
// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an // HandleAction attempts to figure out which Action signature was used. If
// ActionFunc, a func with the legacy signature for Action, or some other // it's an ActionFunc or a func with the legacy signature for Action, the func
// invalid thing. If it's an ActionFunc or a func with the legacy signature for // is run!
// Action, the func is run!
func HandleAction(action interface{}, context *Context) (err error) { func HandleAction(action interface{}, context *Context) (err error) {
defer func() { if a, ok := action.(ActionFunc); ok {
if r := recover(); r != nil { return a(context)
// Try to detect a known reflection error from *this scope*, rather than } else if a, ok := action.(func(*Context) error); ok {
// swallowing all panics that may happen when calling an Action func. return a(context)
s := fmt.Sprintf("%v", r) } else if a, ok := action.(func(*Context)); ok { // deprecated function signature
if strings.HasPrefix(s, "reflect: ") && strings.Contains(s, "too many input arguments") { a(context)
err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v. See %s", r, appActionDeprecationURL), 2)
} else {
panic(r)
}
}
}()
if reflect.TypeOf(action).Kind() != reflect.Func {
return errNonFuncAction
}
vals := reflect.ValueOf(action).Call([]reflect.Value{reflect.ValueOf(context)})
if len(vals) == 0 {
fmt.Fprintf(ErrWriter,
"DEPRECATED Action signature. Must be `cli.ActionFunc`. %s See %s\n",
contactSysadmin, appActionDeprecationURL)
return nil return nil
} else {
return errInvalidActionType
} }
if len(vals) > 1 {
return errInvalidActionSignature
}
if retErr, ok := vals[0].Interface().(error); vals[0].IsValid() && ok {
return retErr
}
return err
} }

View File

@ -46,6 +46,11 @@ type Command struct {
Flags []Flag Flags []Flag
// Treat all flags as normal arguments if true // Treat all flags as normal arguments if true
SkipFlagParsing bool SkipFlagParsing bool
// Skip argument reordering which attempts to move flags before arguments,
// but only works if all flags appear after all arguments. This behavior was
// removed n version 2 since it only works under specific conditions so we
// backport here by exposing it as an option for compatibility.
SkipArgReorder bool
// Boolean to hide built-in help command // Boolean to hide built-in help command
HideHelp bool HideHelp bool
// Boolean to hide this command from help or completion // Boolean to hide this command from help or completion
@ -56,6 +61,20 @@ type Command struct {
commandNamePath []string commandNamePath []string
} }
type CommandsByName []Command
func (c CommandsByName) Len() int {
return len(c)
}
func (c CommandsByName) Less(i, j int) bool {
return c[i].Name < c[j].Name
}
func (c CommandsByName) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
// FullName returns the full name of the command. // FullName returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path // For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string { func (c Command) FullName() string {
@ -82,14 +101,15 @@ func (c Command) Run(ctx *Context) (err error) {
) )
} }
if ctx.App.EnableBashCompletion { set, err := flagSet(c.Name, c.Flags)
c.Flags = append(c.Flags, BashCompletionFlag) if err != nil {
return err
} }
set := flagSet(c.Name, c.Flags)
set.SetOutput(ioutil.Discard) set.SetOutput(ioutil.Discard)
if !c.SkipFlagParsing { if c.SkipFlagParsing {
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
} else if !c.SkipArgReorder {
firstFlagIndex := -1 firstFlagIndex := -1
terminatorIndex := -1 terminatorIndex := -1
for index, arg := range ctx.Args() { for index, arg := range ctx.Args() {
@ -122,21 +142,7 @@ func (c Command) Run(ctx *Context) (err error) {
err = set.Parse(ctx.Args().Tail()) err = set.Parse(ctx.Args().Tail())
} }
} else { } else {
if c.SkipFlagParsing { err = set.Parse(ctx.Args().Tail())
err = set.Parse(append([]string{"--"}, 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.")
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return err
} }
nerr := normalizeFlags(c.Flags, set) nerr := normalizeFlags(c.Flags, set)
@ -148,11 +154,22 @@ func (c Command) Run(ctx *Context) (err error) {
} }
context := NewContext(ctx.App, set, ctx) context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) { if checkCommandCompletions(context, c.Name) {
return nil return nil
} }
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
}
if checkCommandHelp(context, c.Name) { if checkCommandHelp(context, c.Name) {
return nil return nil
} }
@ -182,6 +199,10 @@ func (c Command) Run(ctx *Context) (err error) {
} }
} }
if c.Action == nil {
c.Action = helpSubcommand.Action
}
context.Command = c context.Command = c
err = HandleAction(c.Action, context) err = HandleAction(c.Action, context)
@ -223,11 +244,9 @@ func (c Command) startApp(ctx *Context) error {
app.HelpName = app.Name app.HelpName = app.Name
} }
if c.Description != "" { app.Usage = c.Usage
app.Usage = c.Description app.Description = c.Description
} else { app.ArgsUsage = c.ArgsUsage
app.Usage = c.Usage
}
// set CommandNotFound // set CommandNotFound
app.CommandNotFound = ctx.App.CommandNotFound app.CommandNotFound = ctx.App.CommandNotFound
@ -243,6 +262,7 @@ func (c Command) startApp(ctx *Context) error {
app.Author = ctx.App.Author app.Author = ctx.App.Author
app.Email = ctx.App.Email app.Email = ctx.App.Email
app.Writer = ctx.App.Writer app.Writer = ctx.App.Writer
app.ErrWriter = ctx.App.ErrWriter
app.categories = CommandCategories{} app.categories = CommandCategories{}
for _, command := range c.Subcommands { for _, command := range c.Subcommands {

View File

@ -3,7 +3,10 @@ package cli
import ( import (
"errors" "errors"
"flag" "flag"
"os"
"reflect"
"strings" "strings"
"syscall"
) )
// Context is a type that is passed through to // Context is a type that is passed through to
@ -11,17 +14,23 @@ 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
flagSet *flag.FlagSet shellComplete bool
setFlags map[string]bool flagSet *flag.FlagSet
globalSetFlags map[string]bool setFlags map[string]bool
parentContext *Context parentContext *Context
} }
// 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
@ -43,28 +52,86 @@ func (c *Context) GlobalSet(name, value string) error {
func (c *Context) IsSet(name string) bool { func (c *Context) IsSet(name string) bool {
if c.setFlags == nil { if c.setFlags == nil {
c.setFlags = make(map[string]bool) c.setFlags = make(map[string]bool)
c.flagSet.Visit(func(f *flag.Flag) { c.flagSet.Visit(func(f *flag.Flag) {
c.setFlags[f.Name] = true c.setFlags[f.Name] = true
}) })
c.flagSet.VisitAll(func(f *flag.Flag) {
if _, ok := c.setFlags[f.Name]; ok {
return
}
c.setFlags[f.Name] = false
})
// XXX hack to support IsSet for flags with EnvVar
//
// There isn't an easy way to do this with the current implementation since
// whether a flag was set via an environment variable is very difficult to
// determine here. Instead, we intend to introduce a backwards incompatible
// change in version 2 to add `IsSet` to the Flag interface to push the
// responsibility closer to where the information required to determine
// whether a flag is set by non-standard means such as environment
// variables is avaliable.
//
// See https://github.com/urfave/cli/issues/294 for additional discussion
flags := c.Command.Flags
if c.Command.Name == "" { // cannot == Command{} since it contains slice types
if c.App != nil {
flags = c.App.Flags
}
}
for _, f := range flags {
eachName(f.GetName(), func(name string) {
if isSet, ok := c.setFlags[name]; isSet || !ok {
return
}
val := reflect.ValueOf(f)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
filePathValue := val.FieldByName("FilePath")
if filePathValue.IsValid() {
eachName(filePathValue.String(), func(filePath string) {
if _, err := os.Stat(filePath); err == nil {
c.setFlags[name] = true
return
}
})
}
envVarValue := val.FieldByName("EnvVar")
if envVarValue.IsValid() {
eachName(envVarValue.String(), func(envVar string) {
envVar = strings.TrimSpace(envVar)
if _, ok := syscall.Getenv(envVar); ok {
c.setFlags[name] = true
return
}
})
}
})
}
} }
return c.setFlags[name] == true
return c.setFlags[name]
} }
// GlobalIsSet determines if the global flag was actually set // GlobalIsSet determines if the global flag was actually set
func (c *Context) GlobalIsSet(name string) bool { func (c *Context) GlobalIsSet(name string) bool {
if c.globalSetFlags == nil { ctx := c
c.globalSetFlags = make(map[string]bool) if ctx.parentContext != nil {
ctx := c ctx = ctx.parentContext
if ctx.parentContext != nil { }
ctx = ctx.parentContext
} for ; ctx != nil; ctx = ctx.parentContext {
for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext { if ctx.IsSet(name) {
ctx.flagSet.Visit(func(f *flag.Flag) { return true
c.globalSetFlags[f.Name] = true
})
} }
} }
return c.globalSetFlags[name] return false
} }
// FlagNames returns a slice of flag names used in this context. // FlagNames returns a slice of flag names used in this context.
@ -96,6 +163,11 @@ func (c *Context) Parent() *Context {
return c.parentContext return c.parentContext
} }
// value returns the value of the flag coressponding to `name`
func (c *Context) value(name string) interface{} {
return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
}
// Args contains apps console arguments // Args contains apps console arguments
type Args []string type Args []string

View File

@ -24,7 +24,7 @@ func NewMultiError(err ...error) MultiError {
return MultiError{Errors: err} return MultiError{Errors: err}
} }
// Error implents the error interface. // Error implements the error interface.
func (m MultiError) Error() string { func (m MultiError) Error() string {
errs := make([]string, len(m.Errors)) errs := make([]string, len(m.Errors))
for i, err := range m.Errors { for i, err := range m.Errors {
@ -34,6 +34,10 @@ func (m MultiError) Error() string {
return strings.Join(errs, "\n") return strings.Join(errs, "\n")
} }
type ErrorFormatter interface {
Format(s fmt.State, verb rune)
}
// ExitCoder is the interface checked by `App` and `Command` for a custom exit // ExitCoder is the interface checked by `App` and `Command` for a custom exit
// code // code
type ExitCoder interface { type ExitCoder interface {
@ -44,11 +48,11 @@ type ExitCoder interface {
// ExitError fulfills both the builtin `error` interface and `ExitCoder` // ExitError fulfills both the builtin `error` interface and `ExitCoder`
type ExitError struct { type ExitError struct {
exitCode int exitCode int
message string message interface{}
} }
// NewExitError makes a new *ExitError // NewExitError makes a new *ExitError
func NewExitError(message string, exitCode int) *ExitError { func NewExitError(message interface{}, exitCode int) *ExitError {
return &ExitError{ return &ExitError{
exitCode: exitCode, exitCode: exitCode,
message: message, message: message,
@ -58,7 +62,7 @@ func NewExitError(message string, exitCode int) *ExitError {
// Error returns the string message, fulfilling the interface required by // Error returns the string message, fulfilling the interface required by
// `error` // `error`
func (ee *ExitError) Error() string { func (ee *ExitError) Error() string {
return ee.message return fmt.Sprintf("%v", ee.message)
} }
// ExitCode returns the exit code, fulfilling the interface required by // ExitCode returns the exit code, fulfilling the interface required by
@ -70,7 +74,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
@ -78,21 +82,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 {
fmt.Fprintln(ErrWriter, merr)
if exitErr, ok := merr.(ExitCoder); ok {
code = exitErr.ExitCode()
}
}
}
return code
} }

433
vendor/github.com/urfave/cli/flag.go generated vendored
View File

@ -3,11 +3,12 @@ package cli
import ( import (
"flag" "flag"
"fmt" "fmt"
"os" "io/ioutil"
"reflect" "reflect"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"syscall"
"time" "time"
) )
@ -37,6 +38,21 @@ var HelpFlag = BoolFlag{
// to display a flag. // to display a flag.
var FlagStringer FlagStringFunc = stringifyFlag var FlagStringer FlagStringFunc = stringifyFlag
// FlagsByName is a slice of Flag.
type FlagsByName []Flag
func (f FlagsByName) Len() int {
return len(f)
}
func (f FlagsByName) Less(i, j int) bool {
return f[i].GetName() < f[j].GetName()
}
func (f FlagsByName) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
// Flag is a common interface related to parsing flags in cli. // Flag is a common interface related to parsing flags in cli.
// For more advanced flag parsing techniques, it is recommended that // For more advanced flag parsing techniques, it is recommended that
// this interface be implemented. // this interface be implemented.
@ -47,13 +63,29 @@ type Flag interface {
GetName() string GetName() string
} }
func flagSet(name string, flags []Flag) *flag.FlagSet { // errorableFlag is an interface that allows us to return errors during apply
// it allows flags defined in this library to return errors in a fashion backwards compatible
// TODO remove in v2 and modify the existing Flag interface to return errors
type errorableFlag interface {
Flag
ApplyWithError(*flag.FlagSet) error
}
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
set := flag.NewFlagSet(name, flag.ContinueOnError) set := flag.NewFlagSet(name, flag.ContinueOnError)
for _, f := range flags { for _, f := range flags {
f.Apply(set) //TODO remove in v2 when errorableFlag is removed
if ef, ok := f.(errorableFlag); ok {
if err := ef.ApplyWithError(set); err != nil {
return nil, err
}
} else {
f.Apply(set)
}
} }
return set return set, nil
} }
func eachName(longName string, fn func(string)) { func eachName(longName string, fn func(string)) {
@ -72,24 +104,29 @@ type Generic interface {
// Apply takes the flagset and calls Set on the generic flag with the value // Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag // provided by the user for parsing by the flag
// Ignores parsing errors
func (f GenericFlag) Apply(set *flag.FlagSet) { func (f GenericFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error {
val := f.Value val := f.Value
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { if err := val.Set(envVal); err != nil {
envVar = strings.TrimSpace(envVar) return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err)
if envVal := os.Getenv(envVar); envVal != "" {
val.Set(envVal)
break
}
} }
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
}) })
return nil
} }
// StringSlice is an opaque type for []string to satisfy flag.Value // StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
type StringSlice []string type StringSlice []string
// Set appends the string value to the list of values // Set appends the string value to the list of values
@ -108,21 +145,28 @@ func (f *StringSlice) Value() []string {
return *f return *f
} }
// Get returns the slice of strings set by this flag
func (f *StringSlice) Get() interface{} {
return *f
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f StringSliceFlag) Apply(set *flag.FlagSet) { func (f StringSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
newVal := &StringSlice{} func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
for _, s := range strings.Split(envVal, ",") { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
s = strings.TrimSpace(s) newVal := &StringSlice{}
newVal.Set(s) for _, s := range strings.Split(envVal, ",") {
} s = strings.TrimSpace(s)
f.Value = newVal if err := newVal.Set(s); err != nil {
break return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err)
} }
} }
f.Value = newVal
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -131,9 +175,11 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
} }
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
}) })
return nil
} }
// IntSlice is an opaque type for []int to satisfy flag.Value // IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
type IntSlice []int type IntSlice []int
// Set parses the value into an integer and appends it to the list of values // Set parses the value into an integer and appends it to the list of values
@ -156,24 +202,28 @@ func (f *IntSlice) Value() []int {
return *f return *f
} }
// Get returns the slice of ints set by this flag
func (f *IntSlice) Get() interface{} {
return *f
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f IntSliceFlag) Apply(set *flag.FlagSet) { func (f IntSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
newVal := &IntSlice{} func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
for _, s := range strings.Split(envVal, ",") { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
s = strings.TrimSpace(s) newVal := &IntSlice{}
err := newVal.Set(s) for _, s := range strings.Split(envVal, ",") {
if err != nil { s = strings.TrimSpace(s)
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
break
} }
} }
f.Value = newVal
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -182,9 +232,11 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
} }
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
}) })
return nil
} }
// Int64Slice is an opaque type for []int to satisfy flag.Value // Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
type Int64Slice []int64 type Int64Slice []int64
// Set parses the value into an integer and appends it to the list of values // Set parses the value into an integer and appends it to the list of values
@ -207,24 +259,28 @@ func (f *Int64Slice) Value() []int64 {
return *f return *f
} }
// Get returns the slice of ints set by this flag
func (f *Int64Slice) Get() interface{} {
return *f
}
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f Int64SliceFlag) Apply(set *flag.FlagSet) { func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
newVal := &Int64Slice{} func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
for _, s := range strings.Split(envVal, ",") { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
s = strings.TrimSpace(s) newVal := &Int64Slice{}
err := newVal.Set(s) for _, s := range strings.Split(envVal, ",") {
if err != nil { s = strings.TrimSpace(s)
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
break
} }
} }
f.Value = newVal
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -233,21 +289,27 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
} }
set.Var(f.Value, name, f.Usage) set.Var(f.Value, name, f.Usage)
}) })
return nil
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f BoolFlag) Apply(set *flag.FlagSet) { func (f BoolFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
val := false val := false
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { if envVal == "" {
envVar = strings.TrimSpace(envVar) val = false
if envVal := os.Getenv(envVar); envVal != "" { } else {
envValBool, err := strconv.ParseBool(envVal) envValBool, err := strconv.ParseBool(envVal)
if err == nil { if err != nil {
val = envValBool return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
}
break
} }
val = envValBool
} }
} }
@ -258,21 +320,29 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
} }
set.Bool(name, val, f.Usage) set.Bool(name, val, f.Usage)
}) })
return nil
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f BoolTFlag) Apply(set *flag.FlagSet) { func (f BoolTFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
val := true val := true
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
envVar = strings.TrimSpace(envVar) if envVal == "" {
if envVal := os.Getenv(envVar); envVal != "" { val = false
envValBool, err := strconv.ParseBool(envVal) } else {
if err == nil { envValBool, err := strconv.ParseBool(envVal)
val = envValBool if err != nil {
break return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
}
} }
val = envValBool
} }
} }
@ -283,18 +353,20 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
} }
set.Bool(name, val, f.Usage) set.Bool(name, val, f.Usage)
}) })
return nil
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f StringFlag) Apply(set *flag.FlagSet) { func (f StringFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
f.Value = envVal func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
break if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
} f.Value = envVal
}
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -304,21 +376,24 @@ 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) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
envValInt, err := strconv.ParseInt(envVal, 0, 64) func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
if err == nil { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
f.Value = int(envValInt) envValInt, err := strconv.ParseInt(envVal, 0, 64)
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)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -328,21 +403,25 @@ 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) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
envValInt, err := strconv.ParseInt(envVal, 0, 64) func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
if err == nil { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
f.Value = envValInt envValInt, err := strconv.ParseInt(envVal, 0, 64)
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
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -352,21 +431,25 @@ func (f Int64Flag) Apply(set *flag.FlagSet) {
} }
set.Int64(name, f.Value, f.Usage) set.Int64(name, f.Value, f.Usage)
}) })
return nil
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f UintFlag) Apply(set *flag.FlagSet) { func (f UintFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
envValInt, err := strconv.ParseUint(envVal, 0, 64) func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
if err == nil { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
f.Value = uint(envValInt) envValInt, err := strconv.ParseUint(envVal, 0, 64)
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)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -376,21 +459,25 @@ func (f UintFlag) Apply(set *flag.FlagSet) {
} }
set.Uint(name, f.Value, f.Usage) set.Uint(name, f.Value, f.Usage)
}) })
return nil
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f Uint64Flag) Apply(set *flag.FlagSet) { func (f Uint64Flag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
envValInt, err := strconv.ParseUint(envVal, 0, 64) func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
if err == nil { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
f.Value = uint64(envValInt) envValInt, err := strconv.ParseUint(envVal, 0, 64)
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)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -400,21 +487,25 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) {
} }
set.Uint64(name, f.Value, f.Usage) set.Uint64(name, f.Value, f.Usage)
}) })
return nil
} }
// Apply populates the flag given the flag set and environment // Apply populates the flag given the flag set and environment
// Ignores errors
func (f DurationFlag) Apply(set *flag.FlagSet) { func (f DurationFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
envValDuration, err := time.ParseDuration(envVal) func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
if err == nil { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
f.Value = envValDuration envValDuration, err := time.ParseDuration(envVal)
break if err != nil {
} return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err)
}
} }
f.Value = envValDuration
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -424,20 +515,25 @@ 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) {
if f.EnvVar != "" { f.ApplyWithError(set)
for _, envVar := range strings.Split(f.EnvVar, ",") { }
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" { // ApplyWithError populates the flag given the flag set and environment
envValFloat, err := strconv.ParseFloat(envVal, 10) func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
if err == nil { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
f.Value = float64(envValFloat) envValFloat, err := strconv.ParseFloat(envVal, 10)
} 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)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -447,6 +543,8 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
} }
set.Float64(name, f.Value, f.Usage) set.Float64(name, f.Value, f.Usage)
}) })
return nil
} }
func visibleFlags(fl []Flag) []Flag { func visibleFlags(fl []Flag) []Flag {
@ -518,6 +616,14 @@ func withEnvHint(envVar, str string) string {
return str + envText return str + envText
} }
func withFileHint(filePath, str string) string {
fileText := ""
if filePath != "" {
fileText = fmt.Sprintf(" [%s]", filePath)
}
return str + fileText
}
func flagValue(f Flag) reflect.Value { func flagValue(f Flag) reflect.Value {
fv := reflect.ValueOf(f) fv := reflect.ValueOf(f)
for fv.Kind() == reflect.Ptr { for fv.Kind() == reflect.Ptr {
@ -531,14 +637,29 @@ func stringifyFlag(f Flag) string {
switch f.(type) { switch f.(type) {
case IntSliceFlag: case IntSliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(), return withFileHint(
stringifyIntSliceFlag(f.(IntSliceFlag))) fv.FieldByName("FilePath").String(),
withEnvHint(
fv.FieldByName("EnvVar").String(),
stringifyIntSliceFlag(f.(IntSliceFlag)),
),
)
case Int64SliceFlag: case Int64SliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(), return withFileHint(
stringifyInt64SliceFlag(f.(Int64SliceFlag))) fv.FieldByName("FilePath").String(),
withEnvHint(
fv.FieldByName("EnvVar").String(),
stringifyInt64SliceFlag(f.(Int64SliceFlag)),
),
)
case StringSliceFlag: case StringSliceFlag:
return withEnvHint(fv.FieldByName("EnvVar").String(), return withFileHint(
stringifyStringSliceFlag(f.(StringSliceFlag))) fv.FieldByName("FilePath").String(),
withEnvHint(
fv.FieldByName("EnvVar").String(),
stringifyStringSliceFlag(f.(StringSliceFlag)),
),
)
} }
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
@ -566,8 +687,13 @@ func stringifyFlag(f Flag) string {
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString)) usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
return withEnvHint(fv.FieldByName("EnvVar").String(), return withFileHint(
fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) fv.FieldByName("FilePath").String(),
withEnvHint(
fv.FieldByName("EnvVar").String(),
fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault),
),
)
} }
func stringifyIntSliceFlag(f IntSliceFlag) string { func stringifyIntSliceFlag(f IntSliceFlag) string {
@ -619,3 +745,18 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string {
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault) return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault)
} }
func flagFromFileEnv(filePath, envName string) (val string, ok bool) {
if filePath != "" {
if data, err := ioutil.ReadFile(filePath); err == nil {
return string(data), true
}
}
for _, envVar := range strings.Split(envName, ",") {
envVar = strings.TrimSpace(envVar)
if envVal, ok := syscall.Getenv(envVar); ok {
return envVal, true
}
}
return
}

View File

@ -13,6 +13,7 @@ type BoolFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Destination *bool Destination *bool
} }
@ -60,6 +61,7 @@ type BoolTFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Destination *bool Destination *bool
} }
@ -107,6 +109,7 @@ type DurationFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value time.Duration Value time.Duration
Destination *time.Duration Destination *time.Duration
@ -155,6 +158,7 @@ type Float64Flag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value float64 Value float64
Destination *float64 Destination *float64
@ -200,11 +204,12 @@ func lookupFloat64(name string, set *flag.FlagSet) float64 {
// GenericFlag is a flag with type Generic // GenericFlag is a flag with type Generic
type GenericFlag struct { type GenericFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value Generic Hidden bool
Value Generic
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -250,6 +255,7 @@ type Int64Flag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value int64 Value int64
Destination *int64 Destination *int64
@ -298,6 +304,7 @@ type IntFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value int Value int
Destination *int Destination *int
@ -343,11 +350,12 @@ func lookupInt(name string, set *flag.FlagSet) int {
// IntSliceFlag is a flag with type *IntSlice // IntSliceFlag is a flag with type *IntSlice
type IntSliceFlag struct { type IntSliceFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value *IntSlice Hidden bool
Value *IntSlice
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -390,11 +398,12 @@ func lookupIntSlice(name string, set *flag.FlagSet) []int {
// Int64SliceFlag is a flag with type *Int64Slice // Int64SliceFlag is a flag with type *Int64Slice
type Int64SliceFlag struct { type Int64SliceFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value *Int64Slice Hidden bool
Value *Int64Slice
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -440,6 +449,7 @@ type StringFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value string Value string
Destination *string Destination *string
@ -485,11 +495,12 @@ func lookupString(name string, set *flag.FlagSet) string {
// StringSliceFlag is a flag with type *StringSlice // StringSliceFlag is a flag with type *StringSlice
type StringSliceFlag struct { type StringSliceFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value *StringSlice Hidden bool
Value *StringSlice
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -535,6 +546,7 @@ type Uint64Flag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value uint64 Value uint64
Destination *uint64 Destination *uint64
@ -583,6 +595,7 @@ type UintFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value uint Value uint
Destination *uint Destination *uint

View File

@ -142,6 +142,7 @@ def _write_cli_flag_types(outfile, types):
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
""".format(**typedef)) """.format(**typedef))
@ -202,6 +203,10 @@ def _write_altsrc_flag_types(outfile, types):
_fwrite(outfile, """\ _fwrite(outfile, """\
package altsrc package altsrc
import (
"gopkg.in/urfave/cli.v1"
)
// WARNING: This file is generated! // WARNING: This file is generated!
""") """)
@ -228,6 +233,13 @@ def _write_altsrc_flag_types(outfile, types):
f.set = set f.set = set
f.{name}Flag.Apply(set) f.{name}Flag.Apply(set)
}} }}
// ApplyWithError saves the flagSet for later usage calls, then calls the
// wrapped {name}Flag.ApplyWithError
func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{
f.set = set
return f.{name}Flag.ApplyWithError(set)
}}
""".format(**typedef)) """.format(**typedef))

75
vendor/github.com/urfave/cli/help.go generated vendored
View File

@ -13,27 +13,31 @@ 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 .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 .Version}}{{if not .HideVersion}}
VERSION: VERSION:
{{.Version}} {{.Version}}{{end}}{{end}}{{if .Description}}
{{end}}{{end}}{{if len .Authors}}
AUTHOR(S): DESCRIPTION:
{{range .Authors}}{{.}}{{end}} {{.Description}}{{end}}{{if len .Authors}}
{{end}}{{if .VisibleCommands}}
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
{{range $index, $author := .Authors}}{{if $index}}
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
COMMANDS:{{range .VisibleCategories}}{{if .Name}} COMMANDS:{{range .VisibleCategories}}{{if .Name}}
{{.Name}}:{{end}}{{range .VisibleCommands}} {{.Name}}:{{end}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
{{end}}{{end}}{{if .VisibleFlags}}
GLOBAL OPTIONS: GLOBAL OPTIONS:
{{range .VisibleFlags}}{{.}} {{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{end}}{{if .Copyright}} {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}
COPYRIGHT: COPYRIGHT:
{{.Copyright}} {{.Copyright}}{{end}}
{{end}}
` `
// CommandHelpTemplate is the text template for the command help topic. // CommandHelpTemplate is the text template for the command help topic.
@ -60,7 +64,7 @@ OPTIONS:
// cli.go uses text/template to render templates. You can // cli.go uses text/template to render templates. You can
// render custom help text by setting this variable. // render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME: var SubcommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}} {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}}
USAGE: USAGE:
{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
@ -248,20 +252,43 @@ func checkSubcommandHelp(c *Context) bool {
return false return false
} }
func checkCompletions(c *Context) bool { func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { if !a.EnableBashCompletion {
ShowCompletions(c) return false, arguments
return true
} }
return false pos := len(arguments) - 1
lastArg := arguments[pos]
if lastArg != "--"+BashCompletionFlag.Name {
return false, arguments
}
return true, arguments[:pos]
}
func checkCompletions(c *Context) bool {
if !c.shellComplete {
return false
}
if args := c.Args(); args.Present() {
name := args.First()
if cmd := c.App.Command(name); cmd != nil {
// let the command handle the completion
return false
}
}
ShowCompletions(c)
return true
} }
func checkCommandCompletions(c *Context, name string) bool { func checkCommandCompletions(c *Context, name string) bool {
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { if !c.shellComplete {
ShowCommandCompletions(c, name) return false
return true
} }
return false ShowCommandCompletions(c, name)
return true
} }

View File

@ -57,6 +57,10 @@ def _test():
def _gfmrun(): def _gfmrun():
go_version = check_output('go version'.split()).split()[2]
if go_version < 'go1.3':
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
return
_run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md'])

7
vendor/vendor.json vendored
View File

@ -21,10 +21,11 @@
"revisionTime": "2015-09-07T01:02:28Z" "revisionTime": "2015-09-07T01:02:28Z"
}, },
{ {
"checksumSHA1": "W1G9Avuz92Ng1IqblcO/cIe6pHU=", "checksumSHA1": "S8aBnWst2swLRVgReTMCRXGmNT4=",
"origin": "github.com/bradrydzewski/cli",
"path": "github.com/urfave/cli", "path": "github.com/urfave/cli",
"revision": "d9021faab69f92295ef7061bd39e4a76dcbdef32", "revision": "420da9fff3724cc19afe25bef61aeabd5b81e9f7",
"revisionTime": "2016-07-26T01:14:03Z" "revisionTime": "2017-04-10T14:45:51Z"
} }
], ],
"rootPath": "github.com/drone-plugins/drone-docker" "rootPath": "github.com/drone-plugins/drone-docker"