From 8e6aa34a1284bc29e84866ed015bca0f3dd2d88e Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sun, 21 Aug 2016 13:51:55 -0700 Subject: [PATCH 01/17] remove the possiblity of end-user's seeing deprecation warnings Instead use deprecation pattern described in https://blog.golang.org/godoc-documenting-go-code. Fixes #507 --- app.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app.go b/app.go index 0755bb6..3b54518 100644 --- a/app.go +++ b/app.go @@ -62,10 +62,11 @@ type App struct { // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc + // 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{} - // 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 CommandNotFound CommandNotFoundFunc @@ -247,11 +248,12 @@ func (a *App) Run(arguments []string) (err error) { 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() { - fmt.Fprintf(a.errWriter(), - "DEPRECATED cli.App.RunAndExitOnError. %s See %s\n", - contactSysadmin, runAndExitOnErrorDeprecationURL) if err := a.Run(os.Args); err != nil { fmt.Fprintln(a.errWriter(), err) OsExiter(1) @@ -471,7 +473,7 @@ func HandleAction(action interface{}, context *Context) (err error) { // swallowing all panics that may happen when calling an Action func. s := fmt.Sprintf("%v", r) if strings.HasPrefix(s, "reflect: ") && strings.Contains(s, "too many input arguments") { - err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v. See %s", r, appActionDeprecationURL), 2) + err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v."), 2) } else { panic(r) } @@ -485,9 +487,6 @@ func HandleAction(action interface{}, context *Context) (err error) { 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 } From a5ca09a9344c444c8dfcd5c1718d06b75b7aac1e Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sun, 21 Aug 2016 14:06:59 -0700 Subject: [PATCH 02/17] fixup! remove the possiblity of end-user's seeing deprecation warnings --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 3b54518..b9adf46 100644 --- a/app.go +++ b/app.go @@ -473,7 +473,7 @@ func HandleAction(action interface{}, context *Context) (err error) { // swallowing all panics that may happen when calling an Action func. s := fmt.Sprintf("%v", r) if strings.HasPrefix(s, "reflect: ") && strings.Contains(s, "too many input arguments") { - err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v."), 2) + err = NewExitError(fmt.Sprintf("ERROR unknown Action error: %v.", r), 2) } else { panic(r) } From c75c862386f960a352e2e48611de3544d585bac4 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Sat, 27 Aug 2016 19:09:14 -0400 Subject: [PATCH 03/17] Fix import paths in altsrc Uses gopkg.in as the import path for the `altsrc` package. Fixes: #473 --- altsrc/flag.go | 2 +- altsrc/flag_generated.go | 2 +- altsrc/flag_test.go | 2 +- altsrc/input_source_context.go | 2 +- altsrc/map_input_source.go | 2 +- altsrc/toml_command_test.go | 2 +- altsrc/toml_file_loader.go | 2 +- altsrc/yaml_command_test.go | 2 +- altsrc/yaml_file_loader.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 8add2fb..ec14e40 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) // FlagInputSourceExtension is an extension interface of cli.Flag that diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go index fa76724..b6b96a1 100644 --- a/altsrc/flag_generated.go +++ b/altsrc/flag_generated.go @@ -3,7 +3,7 @@ package altsrc import ( "flag" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) // WARNING: This file is generated! diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index 218e9b8..9e9c96d 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) type testApplyInputSource struct { diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index 8ea7e92..276dcda 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -3,7 +3,7 @@ package altsrc import ( "time" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) // InputSourceContext is an interface used to allow diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index b1c8e4f..b720995 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) // MapInputSource implements InputSourceContext to return diff --git a/altsrc/toml_command_test.go b/altsrc/toml_command_test.go index 1d91d56..a5053d4 100644 --- a/altsrc/toml_command_test.go +++ b/altsrc/toml_command_test.go @@ -11,7 +11,7 @@ import ( "os" "testing" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) func TestCommandTomFileTest(t *testing.T) { diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index bc2c11d..39c124f 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -10,7 +10,7 @@ import ( "reflect" "github.com/BurntSushi/toml" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) type tomlMap struct { diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index 31f78ce..9d3f431 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -11,7 +11,7 @@ import ( "os" "testing" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" ) func TestCommandYamlFileTest(t *testing.T) { diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index b4e3365..335356f 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -12,7 +12,7 @@ import ( "net/url" "os" - "github.com/urfave/cli" + "gopkg.in/urfave/cli.v1" "gopkg.in/yaml.v2" ) From df95e0708f6416a3f148c984464cfd417e9eccfe Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sat, 3 Sep 2016 14:22:45 -0700 Subject: [PATCH 04/17] Manually set import in altsrc flag generation Otherwise `goimports` switches it to `github.com/urfave/cli` --- generate-flag-types | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/generate-flag-types b/generate-flag-types index 8567f5d..47a168b 100755 --- a/generate-flag-types +++ b/generate-flag-types @@ -202,6 +202,10 @@ def _write_altsrc_flag_types(outfile, types): _fwrite(outfile, """\ package altsrc + import ( + "gopkg.in/urfave/cli.v1" + ) + // WARNING: This file is generated! """) From 4b62cb6b3397f6046f1e001f1be53daa7b4d970d Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sat, 3 Sep 2016 13:18:48 -0700 Subject: [PATCH 05/17] Update wording around gopkg.in pinning to be more accurate Since we have `v1.X` tags, gopkg.in/urfave/cli.v1 will pull the latest tagged release rather than the `v1` branch. Since there are no tagged `v2` releases, `gopkg.in` will pull the latest commit of that branch. Fixes #513 --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 31b25b8..2c863ae 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ applications in an expressive way. - [Installation](#installation) * [Supported platforms](#supported-platforms) * [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) - [Examples](#examples) * [Arguments](#arguments) @@ -104,11 +104,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 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 @@ -122,6 +122,8 @@ import ( ... ``` +This will pull the latest tagged `v1` release (e.g. `v1.18.1` at the time of writing). + ## Getting Started One of the philosophies behind cli is that an API should be playful and full of From 76164d6e36d466a0b42d9079d2f4789ace074184 Mon Sep 17 00:00:00 2001 From: Nathan Bullock Date: Mon, 5 Sep 2016 07:16:05 -0400 Subject: [PATCH 06/17] Fix typo in README cosumized -> customized --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 31b25b8..2971e26 100644 --- a/README.md +++ b/README.md @@ -847,7 +847,7 @@ func main() { ### Generated Help Text -The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked +The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked by the cli internals in order to print generated help text for the app, command, or subcommand, and break execution. @@ -952,7 +952,7 @@ is checked by the cli internals in order to print the `App.Version` via #### 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.: +``` 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`", + }, + } + + sort.Sort(cli.FlagsByName(app.Flags)) + + 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 You can also have the default value set from the environment via `EnvVar`. e.g. diff --git a/flag.go b/flag.go index e748c02..1ff28d3 100644 --- a/flag.go +++ b/flag.go @@ -37,6 +37,21 @@ var HelpFlag = BoolFlag{ // to display a flag. 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. // For more advanced flag parsing techniques, it is recommended that // this interface be implemented. From d913b71c72fbd5857a4e5219d53023d14f0eb346 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Fri, 21 Oct 2016 10:42:30 +0200 Subject: [PATCH 14/17] .travis.yml: add go 1.7.x Signed-off-by: Antonio Murdaca --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index a6a1386..94836d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ go: - 1.4.2 - 1.5.x - 1.6.x +- 1.7.x - master matrix: @@ -20,6 +21,8 @@ matrix: include: - go: 1.6.x os: osx + - go: 1.7.x + os: osx before_script: - go get github.com/urfave/gfmrun/... || true From 0c143a2a268ff019a99698fe2e6d9e7bfed088c7 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 22 Oct 2016 15:58:07 -0700 Subject: [PATCH 15/17] app: Fix trailing space for Author.String() This code initially landed with lots of space: '{name} <{email}> ' or: '{name} ' in 3d718330 (app, help: add support for multiple authors, 2015-01-31). The doubled space between the name and email was removed in c6592bb4 (app, help: add backwards compatibility for Authors, 2015-02-21), but a trailing space remained in both the email and email-less cases. This commit removes that trailing space. --- app.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index e5c2839..77eb141 100644 --- a/app.go +++ b/app.go @@ -458,10 +458,10 @@ type Author struct { func (a Author) String() string { e := "" 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 From 3c2bce5807d92a07ae1d3adf503a9811525e3e4b Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 22 Oct 2016 16:02:21 -0700 Subject: [PATCH 16/17] help: Cleanup AppHelpTemplate trailing whitespace Most of the changes here remove trailing whitespace, but I also add code to select "AUTHOR" or "AUTHORS" as appropriate instead of the previous "AUTHOR(S)". The template for listing with an entry per line is: {{range $index, $entry := pipeline}}{{if $index}} {{end}}{{$entry}}{{end}} That range syntax is discussed in [1]. Also add a unit test, which tests both these whitespace changes and also the earlier App.Description addition. [1]: https://golang.org/pkg/text/template/#hdr-Variables --- app_test.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++- help.go | 31 +++++++++++++++-------------- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/app_test.go b/app_test.go index 23c8aa6..2b541f8 100644 --- a/app_test.go +++ b/app_test.go @@ -90,7 +90,62 @@ func ExampleApp_Run_subcommand() { // Hello, Jeremy } -func ExampleApp_Run_help() { +func ExampleApp_Run_appHelp() { + // set args for examples sake + os.Args = []string{"greet", "help"} + + app := NewApp() + app.Name = "greet" + app.Version = "0.1.0" + app.Description = "This is how we describe greet the app" + app.Authors = []Author{ + {Name: "Harrison", Email: "harrison@lolwut.com"}, + {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, + } + app.Flags = []Flag{ + StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Commands = []Command{ + { + Name: "describeit", + Aliases: []string{"d"}, + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *Context) error { + fmt.Printf("i like to describe things") + return nil + }, + }, + } + app.Run(os.Args) + // Output: + // NAME: + // greet - A new cli application + // + // USAGE: + // greet [global options] command [command options] [arguments...] + // + // VERSION: + // 0.1.0 + // + // DESCRIPTION: + // This is how we describe greet the app + // + // AUTHORS: + // Harrison + // Oliver Allen + // + // COMMANDS: + // describeit, d use it to see a description + // help, h Shows a list of commands or help for one command + // + // GLOBAL OPTIONS: + // --name value a name to say (default: "bob") + // --help, -h show help + // --version, -v print the version +} + +func ExampleApp_Run_commandHelp() { // set args for examples sake os.Args = []string{"greet", "h", "describeit"} diff --git a/help.go b/help.go index 529e5ea..515f744 100644 --- a/help.go +++ b/help.go @@ -16,27 +16,28 @@ var AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} - {{if .Version}}{{if not .HideVersion}} + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + VERSION: - {{.Version}} - {{end}}{{end}}{{if .Description}} + {{.Version}}{{end}}{{end}}{{if .Description}} + DESCRIPTION: - {{.Description}} -{{end}}{{if len .Authors}} -AUTHOR(S): - {{range .Authors}}{{.}}{{end}} - {{end}}{{if .VisibleCommands}} + {{.Description}}{{end}}{{if len .Authors}} + +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}} {{.Name}}:{{end}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} -{{end}}{{end}}{{if .VisibleFlags}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + GLOBAL OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}}{{if .Copyright}} + {{range $index, $option := .VisibleFlags}}{{if $index}} + {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + COPYRIGHT: - {{.Copyright}} - {{end}} + {{.Copyright}}{{end}} ` // CommandHelpTemplate is the text template for the command help topic. From 03f77c6e740501d14f0596a9a0beadffb23a89a0 Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sat, 29 Oct 2016 14:33:41 -0700 Subject: [PATCH 17/17] Fix new example for v2 branch --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d82a8cd..d46de8d 100644 --- a/README.md +++ b/README.md @@ -480,17 +480,17 @@ import ( ) 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 := &cli.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`", + }, }, }