From 7e0532002650b69f219f34f4614656261be45363 Mon Sep 17 00:00:00 2001 From: Jesse Howarth and Michael Ivey Date: Tue, 2 Dec 2014 17:44:55 +0000 Subject: [PATCH 001/215] Implement required flags --- app.go | 32 ++++++++++++++- command.go | 10 +++++ context.go | 18 +++++++++ flag.go | 114 +++++++++++++++++++++++++++++++++++++---------------- 4 files changed, 138 insertions(+), 36 deletions(-) diff --git a/app.go b/app.go index f4c4af8..31a9070 100644 --- a/app.go +++ b/app.go @@ -87,14 +87,25 @@ func (a *App) Run(arguments []string) error { set.SetOutput(ioutil.Discard) err := set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) + cerr := checkRequiredFlags(a.Flags, set) + + context := NewContext(a, set, set) + if nerr != nil { fmt.Println(nerr) - context := NewContext(a, set, set) + fmt.Println("") ShowAppHelp(context) fmt.Println("") return nerr } - context := NewContext(a, set, set) + + if cerr != nil { + fmt.Println(cerr) + fmt.Println("") + ShowAppHelp(context) + fmt.Println("") + return cerr + } if err != nil { fmt.Printf("Incorrect Usage.\n\n") @@ -164,10 +175,13 @@ func (a *App) RunAsSubcommand(ctx *Context) error { set.SetOutput(ioutil.Discard) err := set.Parse(ctx.Args().Tail()) nerr := normalizeFlags(a.Flags, set) + cerr := checkRequiredFlags(a.Flags, set) + context := NewContext(a, set, ctx.globalSet) if nerr != nil { fmt.Println(nerr) + fmt.Println("") if len(a.Commands) > 0 { ShowSubcommandHelp(context) } else { @@ -177,6 +191,20 @@ func (a *App) RunAsSubcommand(ctx *Context) error { return nerr } + if cerr != nil { + fmt.Println(cerr) + fmt.Println("") + if len(a.Commands) > 0 { + ShowSubcommandHelp(context) + fmt.Println("subcommands") + } else { + ShowCommandHelp(ctx, context.Args().First()) + fmt.Println("commands") + } + fmt.Println("") + return cerr + } + if err != nil { fmt.Printf("Incorrect Usage.\n\n") ShowSubcommandHelp(context) diff --git a/command.go b/command.go index 5622b38..2ade6fe 100644 --- a/command.go +++ b/command.go @@ -88,6 +88,16 @@ func (c Command) Run(ctx *Context) error { fmt.Println("") return nerr } + + cerr := checkRequiredFlags(c.Flags, set) + if cerr != nil { + fmt.Println(cerr) + fmt.Println("") + ShowCommandHelp(ctx, c.Name) + fmt.Println("") + return cerr + } + context := NewContext(ctx.App, set, ctx.globalSet) if checkCommandCompletions(context, c.Name) { diff --git a/context.go b/context.go index c9f645b..b8fe7a6 100644 --- a/context.go +++ b/context.go @@ -3,6 +3,7 @@ package cli import ( "errors" "flag" + "fmt" "strconv" "strings" "time" @@ -337,3 +338,20 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error { } return nil } + +func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { + visited := make(map[string]bool) + set.Visit(func(f *flag.Flag) { + visited[f.Name] = true + }) + + for _, f := range flags { + if f.IsRequired() { + key := strings.Split(f.getName(), ",")[0] + if !visited[key] { + return fmt.Errorf("Required flag %s not set", f.getName()) + } + } + } + return nil +} diff --git a/flag.go b/flag.go index b30bca3..eb10368 100644 --- a/flag.go +++ b/flag.go @@ -34,6 +34,7 @@ type Flag interface { // Apply Flag settings to the given flag set Apply(*flag.FlagSet) getName() string + IsRequired() bool } func flagSet(name string, flags []Flag) *flag.FlagSet { @@ -61,10 +62,11 @@ type Generic interface { // GenericFlag is the flag type for types implementing Generic type GenericFlag struct { - Name string - Value Generic - Usage string - EnvVar string + Name string + Value Generic + Usage string + EnvVar string + Required bool } func (f GenericFlag) String() string { @@ -88,6 +90,10 @@ func (f GenericFlag) getName() string { return f.Name } +func (f GenericFlag) IsRequired() bool { + return f.Required +} + type StringSlice []string func (f *StringSlice) Set(value string) error { @@ -104,10 +110,11 @@ func (f *StringSlice) Value() []string { } type StringSliceFlag struct { - Name string - Value *StringSlice - Usage string - EnvVar string + Name string + Value *StringSlice + Usage string + EnvVar string + Required bool } func (f StringSliceFlag) String() string { @@ -136,6 +143,10 @@ func (f StringSliceFlag) getName() string { return f.Name } +func (f StringSliceFlag) IsRequired() bool { + return f.Required +} + type IntSlice []int func (f *IntSlice) Set(value string) error { @@ -158,10 +169,11 @@ func (f *IntSlice) Value() []int { } type IntSliceFlag struct { - Name string - Value *IntSlice - Usage string - EnvVar string + Name string + Value *IntSlice + Usage string + EnvVar string + Required bool } func (f IntSliceFlag) String() string { @@ -193,10 +205,15 @@ func (f IntSliceFlag) getName() string { return f.Name } +func (f IntSliceFlag) IsRequired() bool { + return f.Required +} + type BoolFlag struct { - Name string - Usage string - EnvVar string + Name string + Usage string + EnvVar string + Required bool } func (f BoolFlag) String() string { @@ -223,10 +240,15 @@ func (f BoolFlag) getName() string { return f.Name } +func (f BoolFlag) IsRequired() bool { + return f.Required +} + type BoolTFlag struct { - Name string - Usage string - EnvVar string + Name string + Usage string + EnvVar string + Required bool } func (f BoolTFlag) String() string { @@ -253,11 +275,16 @@ func (f BoolTFlag) getName() string { return f.Name } +func (f BoolTFlag) IsRequired() bool { + return f.Required +} + type StringFlag struct { - Name string - Value string - Usage string - EnvVar string + Name string + Value string + Usage string + EnvVar string + Required bool } func (f StringFlag) String() string { @@ -289,11 +316,16 @@ func (f StringFlag) getName() string { return f.Name } +func (f StringFlag) IsRequired() bool { + return f.Required +} + type IntFlag struct { - Name string - Value int - Usage string - EnvVar string + Name string + Value int + Usage string + EnvVar string + Required bool } func (f IntFlag) String() string { @@ -319,11 +351,16 @@ func (f IntFlag) getName() string { return f.Name } +func (f IntFlag) IsRequired() bool { + return f.Required +} + type DurationFlag struct { - Name string - Value time.Duration - Usage string - EnvVar string + Name string + Value time.Duration + Usage string + EnvVar string + Required bool } func (f DurationFlag) String() string { @@ -349,11 +386,16 @@ func (f DurationFlag) getName() string { return f.Name } +func (f DurationFlag) IsRequired() bool { + return f.Required +} + type Float64Flag struct { - Name string - Value float64 - Usage string - EnvVar string + Name string + Value float64 + Usage string + EnvVar string + Required bool } func (f Float64Flag) String() string { @@ -379,6 +421,10 @@ func (f Float64Flag) getName() string { return f.Name } +func (f Float64Flag) IsRequired() bool { + return f.Required +} + func prefixFor(name string) (prefix string) { if len(name) == 1 { prefix = "-" From 73e64a14fde90fc3e85fdebb3647af6024e48de0 Mon Sep 17 00:00:00 2001 From: Jesse Howarth and Michael Ivey Date: Tue, 2 Dec 2014 19:02:56 +0000 Subject: [PATCH 002/215] Add (required) to help of flags that are required. --- flag.go | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/flag.go b/flag.go index eb10368..83dbba1 100644 --- a/flag.go +++ b/flag.go @@ -70,7 +70,7 @@ type GenericFlag struct { } func (f GenericFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage)) } func (f GenericFlag) Apply(set *flag.FlagSet) { @@ -120,7 +120,7 @@ type StringSliceFlag struct { func (f StringSliceFlag) String() string { firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") pref := prefixFor(firstName) - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) } func (f StringSliceFlag) Apply(set *flag.FlagSet) { @@ -179,7 +179,7 @@ type IntSliceFlag struct { func (f IntSliceFlag) String() string { firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") pref := prefixFor(firstName) - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) } func (f IntSliceFlag) Apply(set *flag.FlagSet) { @@ -217,7 +217,7 @@ type BoolFlag struct { } func (f BoolFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) } func (f BoolFlag) Apply(set *flag.FlagSet) { @@ -252,7 +252,7 @@ type BoolTFlag struct { } func (f BoolTFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) } func (f BoolTFlag) Apply(set *flag.FlagSet) { @@ -297,7 +297,7 @@ func (f StringFlag) String() string { fmtString = "%s %v\t%v" } - return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) } func (f StringFlag) Apply(set *flag.FlagSet) { @@ -329,7 +329,7 @@ type IntFlag struct { } func (f IntFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) } func (f IntFlag) Apply(set *flag.FlagSet) { @@ -364,7 +364,7 @@ type DurationFlag struct { } func (f DurationFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) } func (f DurationFlag) Apply(set *flag.FlagSet) { @@ -399,7 +399,7 @@ type Float64Flag struct { } func (f Float64Flag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) + return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) } func (f Float64Flag) Apply(set *flag.FlagSet) { @@ -454,3 +454,15 @@ func withEnvHint(envVar, str string) string { } return str + envText } + +func withRequiredHint(isRequired bool, str string) string { + if isRequired { + return str + " (required)" + } + + return str +} + +func withHints(envVar string, isRequired bool, str string) string { + return withRequiredHint(isRequired, withEnvHint(envVar, str)) +} From 4b2fcdb1ade79300c56074de8e7a7bf754cd407e Mon Sep 17 00:00:00 2001 From: Jesse Howarth and Michael Ivey Date: Tue, 2 Dec 2014 21:08:24 +0000 Subject: [PATCH 003/215] Add tests for required flags --- required_flags_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 required_flags_test.go diff --git a/required_flags_test.go b/required_flags_test.go new file mode 100644 index 0000000..f9abe7a --- /dev/null +++ b/required_flags_test.go @@ -0,0 +1,60 @@ +package cli + +import ( + "flag" + "testing" +) + +func TestContext_CheckRequiredFlagsSuccess(t *testing.T) { + flags := []Flag{ + StringFlag{ + Name: "required", + Required: true, + }, + StringFlag{ + Name: "optional", + }, + } + + set := flag.NewFlagSet("test", 0) + for _, f := range flags { + f.Apply(set) + } + + e := set.Parse([]string{"--required", "foo"}) + if e != nil { + t.Errorf("Expected no error parsing but there was one: %s", e) + } + + err := checkRequiredFlags(flags, set) + if err != nil { + t.Error("Expected flag parsing to be successful") + } +} + +func TestContext_CheckRequiredFlagsFailure(t *testing.T) { + flags := []Flag{ + StringFlag{ + Name: "required", + Required: true, + }, + StringFlag{ + Name: "optional", + }, + } + + set := flag.NewFlagSet("test", 0) + for _, f := range flags { + f.Apply(set) + } + + e := set.Parse([]string{"--optional", "foo"}) + if e != nil { + t.Errorf("Expected no error parsing but there was one: %s", e) + } + + err := checkRequiredFlags(flags, set) + if err == nil { + t.Error("Expected flag parsing to be unsuccessful") + } +} From cbd95292ac9c4ba7eb30ca121fbe3825ced64f72 Mon Sep 17 00:00:00 2001 From: jhowarth Date: Mon, 2 Mar 2015 11:18:59 -0800 Subject: [PATCH 004/215] Remove debugging --- app.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/app.go b/app.go index 31a9070..e87ed98 100644 --- a/app.go +++ b/app.go @@ -196,10 +196,8 @@ func (a *App) RunAsSubcommand(ctx *Context) error { fmt.Println("") if len(a.Commands) > 0 { ShowSubcommandHelp(context) - fmt.Println("subcommands") } else { ShowCommandHelp(ctx, context.Args().First()) - fmt.Println("commands") } fmt.Println("") return cerr From e67e05f617978eec7bba579a6c86f3d0c11ad96b Mon Sep 17 00:00:00 2001 From: jhowarth Date: Mon, 2 Mar 2015 11:56:29 -0800 Subject: [PATCH 005/215] DRY error handling --- app.go | 19 +++++++++---------- command.go | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app.go b/app.go index e87ed98..d91e6e3 100644 --- a/app.go +++ b/app.go @@ -179,8 +179,9 @@ func (a *App) RunAsSubcommand(ctx *Context) error { context := NewContext(a, set, ctx.globalSet) - if nerr != nil { - fmt.Println(nerr) + // Define here so it closes over the above variables + showErrAndHelp := func(err error) { + fmt.Println(err) fmt.Println("") if len(a.Commands) > 0 { ShowSubcommandHelp(context) @@ -188,18 +189,16 @@ func (a *App) RunAsSubcommand(ctx *Context) error { ShowCommandHelp(ctx, context.Args().First()) } fmt.Println("") + + } + + if nerr != nil { + showErrAndHelp(nerr) return nerr } if cerr != nil { - fmt.Println(cerr) - fmt.Println("") - if len(a.Commands) > 0 { - ShowSubcommandHelp(context) - } else { - ShowCommandHelp(ctx, context.Args().First()) - } - fmt.Println("") + showErrAndHelp(cerr) return cerr } diff --git a/command.go b/command.go index 2ade6fe..833552c 100644 --- a/command.go +++ b/command.go @@ -73,28 +73,28 @@ func (c Command) Run(ctx *Context) error { err = set.Parse(ctx.Args().Tail()) } - if err != nil { - fmt.Printf("Incorrect Usage.\n\n") + // Define here so it closes over the above variables + showErrAndHelp := func(err error) { + fmt.Println(err) + fmt.Println("") ShowCommandHelp(ctx, c.Name) fmt.Println("") + } + + if err != nil { + showErrAndHelp(fmt.Errorf("Incorrect Usage.")) return err } nerr := normalizeFlags(c.Flags, set) if nerr != nil { - fmt.Println(nerr) - fmt.Println("") - ShowCommandHelp(ctx, c.Name) - fmt.Println("") + showErrAndHelp(nerr) return nerr } cerr := checkRequiredFlags(c.Flags, set) if cerr != nil { - fmt.Println(cerr) - fmt.Println("") - ShowCommandHelp(ctx, c.Name) - fmt.Println("") + showErrAndHelp(cerr) return cerr } From 6023f370c1dfea78d4ff99a6ecc6be261347bfc9 Mon Sep 17 00:00:00 2001 From: jhowarth Date: Mon, 2 Mar 2015 12:00:21 -0800 Subject: [PATCH 006/215] dry error messages --- app.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app.go b/app.go index d91e6e3..b2cdb57 100644 --- a/app.go +++ b/app.go @@ -91,26 +91,26 @@ func (a *App) Run(arguments []string) error { context := NewContext(a, set, set) - if nerr != nil { - fmt.Println(nerr) + // Define here so it closes over the above variables + showErrAndHelp := func(err error) { + fmt.Println(err) fmt.Println("") ShowAppHelp(context) fmt.Println("") + } + + if nerr != nil { + showErrAndHelp(nerr) return nerr } if cerr != nil { - fmt.Println(cerr) - fmt.Println("") - ShowAppHelp(context) - fmt.Println("") + showErrAndHelp(cerr) return cerr } if err != nil { - fmt.Printf("Incorrect Usage.\n\n") - ShowAppHelp(context) - fmt.Println("") + showErrAndHelp(fmt.Errorf("Incorrect Usage.")) return err } From 145da3210f41f401b1f42a08385d11ee8a80ec97 Mon Sep 17 00:00:00 2001 From: jhowarth Date: Mon, 2 Mar 2015 12:06:42 -0800 Subject: [PATCH 007/215] don't require flags when the help flag is included --- context.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/context.go b/context.go index b8fe7a6..f9b6f92 100644 --- a/context.go +++ b/context.go @@ -340,6 +340,13 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error { } func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { + // If the help flag is included then none of the other flags are required. + for _, f := range flags { + if f.getName() == "help" { + return nil + } + } + visited := make(map[string]bool) set.Visit(func(f *flag.Flag) { visited[f.Name] = true From aba73cedacbb7b1cec2efb9962460683cd00a90c Mon Sep 17 00:00:00 2001 From: jhowarth Date: Tue, 3 Mar 2015 14:02:42 -0800 Subject: [PATCH 008/215] Copy the writer of the App to the subcommand App --- command.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command.go b/command.go index b040395..5f2b47c 100644 --- a/command.go +++ b/command.go @@ -166,5 +166,8 @@ func (c Command) startApp(ctx *Context) error { app.Action = helpSubcommand.Action } + // set the writer to the original App's writer + app.Writer = ctx.App.Writer + return app.RunAsSubcommand(ctx) } From ceeebaba04790bab2ecd03caded488528b0caf97 Mon Sep 17 00:00:00 2001 From: Andrzej Ressel Date: Thu, 21 Jul 2016 00:02:16 +0200 Subject: [PATCH 009/215] [PoC] Improve zsh autocompletions --- autocomplete/zsh_autocomplete | 14 ++++++++++---- help.go | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/autocomplete/zsh_autocomplete b/autocomplete/zsh_autocomplete index 5430a18..371420c 100644 --- a/autocomplete/zsh_autocomplete +++ b/autocomplete/zsh_autocomplete @@ -1,5 +1,11 @@ -autoload -U compinit && compinit -autoload -U bashcompinit && bashcompinit +_cli_zsh_autocomplete() { -script_dir=$(dirname $0) -source ${script_dir}/bash_autocomplete + local -a opts + opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}") + + _describe 'values' opts + + return +} + +compdef _cli_zsh_autocomplete $PROG diff --git a/help.go b/help.go index ba34719..5d24f04 100644 --- a/help.go +++ b/help.go @@ -129,7 +129,7 @@ func DefaultAppComplete(c *Context) { continue } for _, name := range command.Names() { - fmt.Fprintln(c.App.Writer, name) + fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) } } } From 363d9c9a314cdb9ed68cad1a27c767b45eee8840 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Jul 2016 17:29:13 -0400 Subject: [PATCH 010/215] Add a hack so that zsh completion only runs for zsh --- app_test.go | 38 +++++++++++++++++++++++++++++++++++ autocomplete/zsh_autocomplete | 2 +- help.go | 10 +++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/app_test.go b/app_test.go index 23c8aa6..e86ea2d 100644 --- a/app_test.go +++ b/app_test.go @@ -160,6 +160,44 @@ func ExampleApp_Run_bashComplete() { // h } +func ExampleApp_Run_zshComplete() { + // set args for examples sake + os.Args = []string{"greet", "--generate-bash-completion"} + os.Setenv("_CLI_ZSH_AUTOCOMPLETE_HACK", "1") + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + 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 + }, + }, { + Name: "next", + Usage: "next example", + Description: "more stuff to see when generating bash completion", + Action: func(c *Context) error { + fmt.Printf("the next example") + return nil + }, + }, + } + + app.Run(os.Args) + // Output: + // describeit:use it to see a description + // d:use it to see a description + // next:next example + // help:Shows a list of commands or help for one command + // h:Shows a list of commands or help for one command +} + func TestApp_Run(t *testing.T) { s := "" diff --git a/autocomplete/zsh_autocomplete b/autocomplete/zsh_autocomplete index 371420c..8b747ae 100644 --- a/autocomplete/zsh_autocomplete +++ b/autocomplete/zsh_autocomplete @@ -1,7 +1,7 @@ _cli_zsh_autocomplete() { local -a opts - opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}") + opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}") _describe 'values' opts diff --git a/help.go b/help.go index 5d24f04..700bacf 100644 --- a/help.go +++ b/help.go @@ -128,8 +128,14 @@ func DefaultAppComplete(c *Context) { if command.Hidden { continue } - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + } + } else { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s\n", name) + } } } } From 6a70c4cc923c7359bacfa0500dc234d62e0ca986 Mon Sep 17 00:00:00 2001 From: John Weldon Date: Sat, 2 Jul 2016 12:35:48 -0700 Subject: [PATCH 011/215] Add JSON InputSource to altsrc package - Implement NewJSONSource* functions for returning an InputSource from various JSON data sources. - Copy and modify YAML tests for the JSON InputSource Changes: * Reverted the method calls and structs to match the v1 interface --- README.md | 6 +- altsrc/json_command_test.go | 324 ++++++++++++++++++++++++++++++++++ altsrc/json_source_context.go | 208 ++++++++++++++++++++++ 3 files changed, 535 insertions(+), 3 deletions(-) create mode 100644 altsrc/json_command_test.go create mode 100644 altsrc/json_source_context.go diff --git a/README.md b/README.md index 2bbbd8e..0a442a9 100644 --- a/README.md +++ b/README.md @@ -615,9 +615,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 for this code snipped to work. -Currently only the aboved specified formats are supported but developers can -add support for other input sources by implementing the -altsrc.InputSourceContext for their given sources. +Currently only YAML and JSON files are supported but developers can add support +for other input sources by implementing the altsrc.InputSourceContext for their +given sources. Here is a more complete sample of a command using YAML support: diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go new file mode 100644 index 0000000..1f9af36 --- /dev/null +++ b/altsrc/json_command_test.go @@ -0,0 +1,324 @@ +package altsrc + +import ( + "flag" + "io/ioutil" + "os" + "testing" + + "gopkg.in/urfave/cli.v1" +) + +const ( + fileName = "current.json" + simpleJSON = `{"test": 15}` + nestedJSON = `{"top": {"test": 15}}` +) + +func TestCommandJSONFileTest(t *testing.T) { + cleanup := writeTempFile(t, fileName, simpleJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"test-cmd", "--load", fileName} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileTestGlobalEnvVarWins(t *testing.T) { + cleanup := writeTempFile(t, fileName, simpleJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + os.Setenv("THE_TEST", "10") + defer os.Setenv("THE_TEST", "") + + test := []string{"test-cmd", "--load", fileName} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 10) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileTestGlobalEnvVarWinsNested(t *testing.T) { + cleanup := writeTempFile(t, fileName, nestedJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + os.Setenv("THE_TEST", "10") + defer os.Setenv("THE_TEST", "") + + test := []string{"test-cmd", "--load", fileName} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("top.test") + expect(t, val, 10) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileTestSpecifiedFlagWins(t *testing.T) { + cleanup := writeTempFile(t, fileName, simpleJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"test-cmd", "--load", fileName, "--test", "7"} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 7) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileTestSpecifiedFlagWinsNested(t *testing.T) { + cleanup := writeTempFile(t, fileName, nestedJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"test-cmd", "--load", fileName, "--top.test", "7"} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("top.test") + expect(t, val, 7) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "top.test"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileTestDefaultValueFileWins(t *testing.T) { + cleanup := writeTempFile(t, fileName, simpleJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"test-cmd", "--load", fileName} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileTestDefaultValueFileWinsNested(t *testing.T) { + cleanup := writeTempFile(t, fileName, nestedJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"test-cmd", "--load", fileName} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("top.test") + expect(t, val, 15) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWins(t *testing.T) { + cleanup := writeTempFile(t, fileName, simpleJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + os.Setenv("THE_TEST", "11") + defer os.Setenv("THE_TEST", "") + + test := []string{"test-cmd", "--load", fileName} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("test") + expect(t, val, 11) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + err := command.Run(c) + + expect(t, err, nil) +} + +func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *testing.T) { + cleanup := writeTempFile(t, fileName, nestedJSON) + defer cleanup() + + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + os.Setenv("THE_TEST", "11") + defer os.Setenv("THE_TEST", "") + + test := []string{"test-cmd", "--load", fileName} + set.Parse(test) + + c := cli.NewContext(app, set, nil) + + command := &cli.Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(c *cli.Context) error { + val := c.Int("top.test") + expect(t, val, 11) + return nil + }, + Flags: []cli.Flag{ + NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), + &cli.StringFlag{Name: "load"}}, + } + command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) + err := command.Run(c) + + expect(t, err, nil) +} + +func writeTempFile(t *testing.T, name string, content string) func() { + if err := ioutil.WriteFile(name, []byte(content), 0666); err != nil { + t.Fatalf("cannot write %q: %v", name, err) + } + return func() { + if err := os.Remove(name); err != nil { + t.Errorf("cannot remove %q: %v", name, err) + } + } +} diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go new file mode 100644 index 0000000..47ce82c --- /dev/null +++ b/altsrc/json_source_context.go @@ -0,0 +1,208 @@ +package altsrc + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "strings" + "time" + + "gopkg.in/urfave/cli.v1" +) + +// NewJSONSourceFromFlagFunc returns a func that takes a cli.Context +// and returns an InputSourceContext suitable for retrieving config +// variables from a file containing JSON data with the file name defined +// by the given flag. +func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) { + return func(context *cli.Context) (InputSourceContext, error) { + return NewJSONSourceFromFile(context.String(flag)) + } +} + +// NewJSONSourceFromFile returns an InputSourceContext suitable for +// retrieving config variables from a file (or url) containing JSON +// data. +func NewJSONSourceFromFile(f string) (InputSourceContext, error) { + data, err := loadDataFrom(f) + if err != nil { + return nil, err + } + return NewJSONSource(data) +} + +// NewJSONSourceFromReader returns an InputSourceContext suitable for +// retrieving config variables from an io.Reader that returns JSON data. +func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) { + data, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + return NewJSONSource(data) +} + +// NewJSONSource returns an InputSourceContext suitable for retrieving +// config variables from raw JSON data. +func NewJSONSource(data []byte) (InputSourceContext, error) { + var deserialized map[string]interface{} + if err := json.Unmarshal(data, &deserialized); err != nil { + return nil, err + } + return &jsonSource{deserialized: deserialized}, nil +} + +func (x *jsonSource) Int(name string) (int, error) { + i, err := x.getValue(name) + if err != nil { + return 0, err + } + switch v := i.(type) { + default: + return 0, fmt.Errorf("unexpected type %T for %q", i, name) + case int: + return v, nil + case float64: + return int(float64(v)), nil + case float32: + return int(float32(v)), nil + } +} + +func (x *jsonSource) Duration(name string) (time.Duration, error) { + i, err := x.getValue(name) + if err != nil { + return 0, err + } + v, ok := (time.Duration)(0), false + if v, ok = i.(time.Duration); !ok { + return v, fmt.Errorf("unexpected type %T for %q", i, name) + } + return v, nil +} + +func (x *jsonSource) Float64(name string) (float64, error) { + i, err := x.getValue(name) + if err != nil { + return 0, err + } + v, ok := (float64)(0), false + if v, ok = i.(float64); !ok { + return v, fmt.Errorf("unexpected type %T for %q", i, name) + } + return v, nil +} + +func (x *jsonSource) String(name string) (string, error) { + i, err := x.getValue(name) + if err != nil { + return "", err + } + v, ok := "", false + if v, ok = i.(string); !ok { + return v, fmt.Errorf("unexpected type %T for %q", i, name) + } + return v, nil +} + +func (x *jsonSource) StringSlice(name string) ([]string, error) { + i, err := x.getValue(name) + if err != nil { + return nil, err + } + switch v := i.(type) { + default: + return nil, fmt.Errorf("unexpected type %T for %q", i, name) + case []string: + return v, nil + case []interface{}: + c := []string{} + for _, s := range v { + if str, ok := s.(string); ok { + c = append(c, str) + } else { + return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name) + } + } + return c, nil + } +} + +func (x *jsonSource) IntSlice(name string) ([]int, error) { + i, err := x.getValue(name) + if err != nil { + return nil, err + } + switch v := i.(type) { + default: + return nil, fmt.Errorf("unexpected type %T for %q", i, name) + case []int: + return v, nil + case []interface{}: + c := []int{} + for _, s := range v { + if i2, ok := s.(int); ok { + c = append(c, i2) + } else { + return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name) + } + } + return c, nil + } +} + +func (x *jsonSource) Generic(name string) (cli.Generic, error) { + i, err := x.getValue(name) + if err != nil { + return nil, err + } + v, ok := (cli.Generic)(nil), false + if v, ok = i.(cli.Generic); !ok { + return v, fmt.Errorf("unexpected type %T for %q", i, name) + } + return v, nil +} + +func (x *jsonSource) Bool(name string) (bool, error) { + i, err := x.getValue(name) + if err != nil { + return false, err + } + v, ok := false, false + if v, ok = i.(bool); !ok { + return v, fmt.Errorf("unexpected type %T for %q", i, name) + } + return v, nil +} + +// since this source appears to require all configuration to be specified, the +// concept of a boolean defaulting to true seems inconsistent with no defaults +func (x *jsonSource) BoolT(name string) (bool, error) { + return false, fmt.Errorf("unsupported type BoolT for JSONSource") +} + +func (x *jsonSource) getValue(key string) (interface{}, error) { + return jsonGetValue(key, x.deserialized) +} + +func jsonGetValue(key string, m map[string]interface{}) (interface{}, error) { + var ret interface{} + var ok bool + working := m + keys := strings.Split(key, ".") + for ix, k := range keys { + if ret, ok = working[k]; !ok { + return ret, fmt.Errorf("missing key %q", key) + } + if working, ok = ret.(map[string]interface{}); !ok { + if ix < len(keys)-1 { + return ret, fmt.Errorf("unexpected intermediate value at %q segment of %q: %T", k, key, ret) + } + } + } + return ret, nil +} + +type jsonSource struct { + deserialized map[string]interface{} +} From c48a82964028acd0f19ee17257789f7c9f5afc78 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Apr 2017 09:29:43 -0700 Subject: [PATCH 012/215] Allow custom exit err handlers --- app.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/app.go b/app.go index 51fc45d..5d7ed8b 100644 --- a/app.go +++ b/app.go @@ -83,6 +83,8 @@ type App struct { Writer io.Writer // ErrWriter writes error output ErrWriter io.Writer + // Execute this function to handle ExitErrors + ExitErrHandler ExitErrHandlerFunc // Other custom info Metadata map[string]interface{} // Carries a function which returns app specific info. @@ -207,7 +209,7 @@ func (a *App) Run(arguments []string) (err error) { if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) - HandleExitCoder(err) + a.handleExitCoder(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -241,7 +243,7 @@ func (a *App) Run(arguments []string) (err error) { beforeErr := a.Before(context) if beforeErr != nil { ShowAppHelp(context) - HandleExitCoder(beforeErr) + a.handleExitCoder(beforeErr) err = beforeErr return err } @@ -263,7 +265,7 @@ func (a *App) Run(arguments []string) (err error) { // Run default Action err = HandleAction(a.Action, context) - HandleExitCoder(err) + a.handleExitCoder(err) return err } @@ -330,7 +332,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if err != nil { if a.OnUsageError != nil { err = a.OnUsageError(context, err, true) - HandleExitCoder(err) + a.handleExitCoder(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -352,7 +354,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { defer func() { afterErr := a.After(context) if afterErr != nil { - HandleExitCoder(err) + a.handleExitCoder(err) if err != nil { err = NewMultiError(err, afterErr) } else { @@ -365,7 +367,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { - HandleExitCoder(beforeErr) + a.handleExitCoder(beforeErr) err = beforeErr return err } @@ -383,7 +385,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { // Run default Action err = HandleAction(a.Action, context) - HandleExitCoder(err) + a.handleExitCoder(err) return err } @@ -464,6 +466,14 @@ func (a *App) appendFlag(flag Flag) { } } +func (a *App) handleExitCoder(err error) { + if a.ExitErrHandler != nil { + a.ExitErrHandler(err) + } else { + HandleExitCoder(err) + } +} + // Author represents someone who has contributed to a cli project. type Author struct { Name string // The Authors name From 538742687bbd979a7b4f975468af76ce5cffb972 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Apr 2017 09:31:53 -0700 Subject: [PATCH 013/215] Add ExitErrHandlerFunc type --- funcs.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/funcs.go b/funcs.go index cba5e6c..4737faf 100644 --- a/funcs.go +++ b/funcs.go @@ -26,3 +26,7 @@ type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error // FlagStringFunc is used by the help generation to display a flag, which is // expected to be a single line. type FlagStringFunc func(Flag) string + +// ExitErrHandlerFunc is executed if provided in order to handle ExitError values +// returned by Actions and Before/After functions. +type ExitErrHandlerFunc func(error) From 827da610b4bff0ffbc06cd2d92eddae552f7d1a2 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Apr 2017 09:33:54 -0700 Subject: [PATCH 014/215] Add a bit more documentation --- app.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index 5d7ed8b..4138f32 100644 --- a/app.go +++ b/app.go @@ -83,7 +83,8 @@ type App struct { Writer io.Writer // ErrWriter writes error output ErrWriter io.Writer - // Execute this function to handle ExitErrors + // Execute this function to handle ExitErrors. If not provided, HandleExitCoder is provided to + // function as a default, so this is optional. ExitErrHandler ExitErrHandlerFunc // Other custom info Metadata map[string]interface{} From 80b09a4d1117ad69430582685e59dfe560caa948 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Apr 2017 11:20:41 -0700 Subject: [PATCH 015/215] Fix how to do defaults in app.go --- app.go | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/app.go b/app.go index 4138f32..f1a8f27 100644 --- a/app.go +++ b/app.go @@ -121,6 +121,7 @@ func NewApp() *App { Action: helpCommand.Action, Compiled: compileTime(), Writer: os.Stdout, + ExitErrHandler: HandleExitCoder, } } @@ -210,7 +211,7 @@ func (a *App) Run(arguments []string) (err error) { if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) - a.handleExitCoder(err) + a.ExitErrHandler(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -244,7 +245,7 @@ func (a *App) Run(arguments []string) (err error) { beforeErr := a.Before(context) if beforeErr != nil { ShowAppHelp(context) - a.handleExitCoder(beforeErr) + a.ExitErrHandler(beforeErr) err = beforeErr return err } @@ -266,7 +267,7 @@ func (a *App) Run(arguments []string) (err error) { // Run default Action err = HandleAction(a.Action, context) - a.handleExitCoder(err) + a.ExitErrHandler(err) return err } @@ -333,7 +334,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if err != nil { if a.OnUsageError != nil { err = a.OnUsageError(context, err, true) - a.handleExitCoder(err) + a.ExitErrHandler(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -355,7 +356,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { defer func() { afterErr := a.After(context) if afterErr != nil { - a.handleExitCoder(err) + a.ExitErrHandler(err) if err != nil { err = NewMultiError(err, afterErr) } else { @@ -368,7 +369,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { - a.handleExitCoder(beforeErr) + a.ExitErrHandler(beforeErr) err = beforeErr return err } @@ -386,7 +387,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { // Run default Action err = HandleAction(a.Action, context) - a.handleExitCoder(err) + a.ExitErrHandler(err) return err } @@ -467,14 +468,6 @@ func (a *App) appendFlag(flag Flag) { } } -func (a *App) handleExitCoder(err error) { - if a.ExitErrHandler != nil { - a.ExitErrHandler(err) - } else { - HandleExitCoder(err) - } -} - // Author represents someone who has contributed to a cli project. type Author struct { Name string // The Authors name From ceee6408d5cbbb9f113157d0a62b1ffed1f2b510 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Apr 2017 13:02:05 -0700 Subject: [PATCH 016/215] Revert "Fix how to do defaults in app.go" This reverts commit 8906567dc2ad52fd31c50cf02fa606505a1323ba. --- app.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app.go b/app.go index f1a8f27..e9ed7ab 100644 --- a/app.go +++ b/app.go @@ -121,7 +121,6 @@ func NewApp() *App { Action: helpCommand.Action, Compiled: compileTime(), Writer: os.Stdout, - ExitErrHandler: HandleExitCoder, } } @@ -211,7 +210,7 @@ func (a *App) Run(arguments []string) (err error) { if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) - a.ExitErrHandler(err) + a.handleExitCoder(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -244,8 +243,9 @@ 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) - a.ExitErrHandler(beforeErr) + a.handleExitCoder(beforeErr) err = beforeErr return err } @@ -267,7 +267,7 @@ func (a *App) Run(arguments []string) (err error) { // Run default Action err = HandleAction(a.Action, context) - a.ExitErrHandler(err) + a.handleExitCoder(err) return err } @@ -334,7 +334,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if err != nil { if a.OnUsageError != nil { err = a.OnUsageError(context, err, true) - a.ExitErrHandler(err) + a.handleExitCoder(err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -356,7 +356,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { defer func() { afterErr := a.After(context) if afterErr != nil { - a.ExitErrHandler(err) + a.handleExitCoder(err) if err != nil { err = NewMultiError(err, afterErr) } else { @@ -369,7 +369,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { - a.ExitErrHandler(beforeErr) + a.handleExitCoder(beforeErr) err = beforeErr return err } @@ -387,7 +387,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { // Run default Action err = HandleAction(a.Action, context) - a.ExitErrHandler(err) + a.handleExitCoder(err) return err } @@ -468,6 +468,14 @@ func (a *App) appendFlag(flag Flag) { } } +func (a *App) handleExitCoder(err error) { + if a.ExitErrHandler != nil { + a.ExitErrHandler(err) + } else { + HandleExitCoder(err) + } +} + // Author represents someone who has contributed to a cli project. type Author struct { Name string // The Authors name From 9d61cbad0260bc7f2a72b07142a0120072e3800a Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Tue, 25 Apr 2017 12:45:08 -0700 Subject: [PATCH 017/215] Updated command.go to use App handleExitCoder --- command.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/command.go b/command.go index 23de294..fffa02f 100644 --- a/command.go +++ b/command.go @@ -167,7 +167,7 @@ func (c Command) Run(ctx *Context) (err error) { if err != nil { if c.OnUsageError != nil { err := c.OnUsageError(context, err, false) - HandleExitCoder(err) + context.App.handleExitCoder(err) return err } fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) @@ -184,7 +184,7 @@ func (c Command) Run(ctx *Context) (err error) { defer func() { afterErr := c.After(context) if afterErr != nil { - HandleExitCoder(err) + ctx.App.handleExitCoder(err) if err != nil { err = NewMultiError(err, afterErr) } else { @@ -198,7 +198,7 @@ func (c Command) Run(ctx *Context) (err error) { err = c.Before(context) if err != nil { ShowCommandHelp(context, c.Name) - HandleExitCoder(err) + context.App.handleExitCoder(err) return err } } @@ -210,7 +210,7 @@ func (c Command) Run(ctx *Context) (err error) { err = HandleAction(c.Action, context) if err != nil { - HandleExitCoder(err) + ctx.App.handleExitCoder(err) } return err } From 530df59178874f8d792d2d9cfd745464076f1eda Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Wed, 28 Jun 2017 09:52:12 -0700 Subject: [PATCH 018/215] Pass context into handleExitCoder --- app.go | 18 +++++++++--------- command.go | 8 ++++---- funcs.go | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app.go b/app.go index e9ed7ab..48de351 100644 --- a/app.go +++ b/app.go @@ -210,7 +210,7 @@ func (a *App) Run(arguments []string) (err error) { if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) - a.handleExitCoder(err) + a.handleExitCoder(context, err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -245,7 +245,7 @@ func (a *App) Run(arguments []string) (err error) { if beforeErr != nil { fmt.Fprintf(a.Writer, "%v\n\n", beforeErr) ShowAppHelp(context) - a.handleExitCoder(beforeErr) + a.handleExitCoder(context, beforeErr) err = beforeErr return err } @@ -267,7 +267,7 @@ func (a *App) Run(arguments []string) (err error) { // Run default Action err = HandleAction(a.Action, context) - a.handleExitCoder(err) + a.handleExitCoder(context, err) return err } @@ -334,7 +334,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if err != nil { if a.OnUsageError != nil { err = a.OnUsageError(context, err, true) - a.handleExitCoder(err) + a.handleExitCoder(context, err) return err } fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) @@ -356,7 +356,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { defer func() { afterErr := a.After(context) if afterErr != nil { - a.handleExitCoder(err) + a.handleExitCoder(context, err) if err != nil { err = NewMultiError(err, afterErr) } else { @@ -369,7 +369,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { - a.handleExitCoder(beforeErr) + a.handleExitCoder(context, beforeErr) err = beforeErr return err } @@ -387,7 +387,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { // Run default Action err = HandleAction(a.Action, context) - a.handleExitCoder(err) + a.handleExitCoder(context, err) return err } @@ -468,9 +468,9 @@ func (a *App) appendFlag(flag Flag) { } } -func (a *App) handleExitCoder(err error) { +func (a *App) handleExitCoder(context *Context, error) { if a.ExitErrHandler != nil { - a.ExitErrHandler(err) + a.ExitErrHandler(context, err) } else { HandleExitCoder(err) } diff --git a/command.go b/command.go index fffa02f..502fc9f 100644 --- a/command.go +++ b/command.go @@ -167,7 +167,7 @@ func (c Command) Run(ctx *Context) (err error) { if err != nil { if c.OnUsageError != nil { err := c.OnUsageError(context, err, false) - context.App.handleExitCoder(err) + context.App.handleExitCoder(context, err) return err } fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) @@ -184,7 +184,7 @@ func (c Command) Run(ctx *Context) (err error) { defer func() { afterErr := c.After(context) if afterErr != nil { - ctx.App.handleExitCoder(err) + context.App.handleExitCoder(context, err) if err != nil { err = NewMultiError(err, afterErr) } else { @@ -198,7 +198,7 @@ func (c Command) Run(ctx *Context) (err error) { err = c.Before(context) if err != nil { ShowCommandHelp(context, c.Name) - context.App.handleExitCoder(err) + context.App.handleExitCoder(context, err) return err } } @@ -210,7 +210,7 @@ func (c Command) Run(ctx *Context) (err error) { err = HandleAction(c.Action, context) if err != nil { - ctx.App.handleExitCoder(err) + context.App.handleExitCoder(context, err) } return err } diff --git a/funcs.go b/funcs.go index 4737faf..42023e2 100644 --- a/funcs.go +++ b/funcs.go @@ -29,4 +29,4 @@ type FlagStringFunc func(Flag) string // ExitErrHandlerFunc is executed if provided in order to handle ExitError values // returned by Actions and Before/After functions. -type ExitErrHandlerFunc func(error) +type ExitErrHandlerFunc func(context *Context, error) From 172bb92059ed885c8b4249230f3ccbe9e3e1272b Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Wed, 28 Jun 2017 10:07:25 -0700 Subject: [PATCH 019/215] fix named parameter issue --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 48de351..1876b5c 100644 --- a/app.go +++ b/app.go @@ -468,7 +468,7 @@ func (a *App) appendFlag(flag Flag) { } } -func (a *App) handleExitCoder(context *Context, error) { +func (a *App) handleExitCoder(context *Context, err error) { if a.ExitErrHandler != nil { a.ExitErrHandler(context, err) } else { From 71bdf81f5a65dc253482cb727c2ae973ae3b3830 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Wed, 28 Jun 2017 10:10:11 -0700 Subject: [PATCH 020/215] sigh... fix one more named parameter issue --- funcs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/funcs.go b/funcs.go index 42023e2..2274415 100644 --- a/funcs.go +++ b/funcs.go @@ -29,4 +29,4 @@ type FlagStringFunc func(Flag) string // ExitErrHandlerFunc is executed if provided in order to handle ExitError values // returned by Actions and Before/After functions. -type ExitErrHandlerFunc func(context *Context, error) +type ExitErrHandlerFunc func(context *Context, err error) From 58450552ee1bada60f4175897aff8d69f7c904a1 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Wed, 28 Jun 2017 12:52:50 -0700 Subject: [PATCH 021/215] Add Test --- app_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/app_test.go b/app_test.go index e14ddaf..63e28c7 100644 --- a/app_test.go +++ b/app_test.go @@ -1661,6 +1661,42 @@ func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) { } } +func TestHandleExitCoder_Default(t *testing.T) { + app := NewApp() + fs, err := flagSet(app.Name, app.Flags) + if err != nil { + t.Errorf("error creating FlagSet: %s", err) + } + + ctx := NewContext(app, fs, nil) + app.handleExitCoder(ctx, errors.New("Default Behavior Error")) + + output := fakeErrWriter.String() + if !strings.Contains(output, "Default") { + t.Fatalf("Expected Default Behavior from Error Handler but got: %s", output) + } +} + +func TestHandleExitCoder_Custom(t *testing.T) { + app := NewApp() + fs, err := flagSet(app.Name, app.Flags) + if err != nil { + t.Errorf("error creating FlagSet: %s", err) + } + + app.ExitErrHandler = func(_ *Context, _ error) { + fmt.Fprintln(ErrWriter, "I'm a Custom error handler, I print what I want!") + } + + ctx := NewContext(app, fs, nil) + app.handleExitCoder(ctx, errors.New("Default Behavior Error")) + + output := fakeErrWriter.String() + if !strings.Contains(output, "Custom") { + t.Fatalf("Expected Custom Behavior from Error Handler but got: %s", output) + } +} + func TestHandleAction_WithUnknownPanic(t *testing.T) { defer func() { refute(t, recover(), nil) }() From 5d528e2052b3e7a49293d6aa0fac245047ea61e3 Mon Sep 17 00:00:00 2001 From: Tyler Davis Date: Wed, 28 Jun 2017 13:04:09 -0700 Subject: [PATCH 022/215] use exit errors in uts --- app_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app_test.go b/app_test.go index 63e28c7..9ff6246 100644 --- a/app_test.go +++ b/app_test.go @@ -1669,7 +1669,7 @@ func TestHandleExitCoder_Default(t *testing.T) { } ctx := NewContext(app, fs, nil) - app.handleExitCoder(ctx, errors.New("Default Behavior Error")) + app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42)) output := fakeErrWriter.String() if !strings.Contains(output, "Default") { @@ -1689,7 +1689,7 @@ func TestHandleExitCoder_Custom(t *testing.T) { } ctx := NewContext(app, fs, nil) - app.handleExitCoder(ctx, errors.New("Default Behavior Error")) + app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42)) output := fakeErrWriter.String() if !strings.Contains(output, "Custom") { From e1fa109a3195a9fedcb635841ca1907b764ada1f Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Thu, 10 Aug 2017 17:54:24 -0700 Subject: [PATCH 023/215] Define flag source precedence in README Fixes #646 --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 2bbbd8e..883cc10 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ applications in an expressive way. + [Ordering](#ordering) + [Values from the Environment](#values-from-the-environment) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) + + [Precedence](#precedence) * [Subcommands](#subcommands) * [Subcommands categories](#subcommands-categories) * [Exit code](#exit-code) @@ -656,6 +657,15 @@ func main() { } ``` +#### Precedence + +The precedence for flag value sources is as follows (highest to lowest): + +0. Command line flag value from user +0. Environment variable (if specified) +0. Configuration file (if specified) +0. Default defined on the flag + ### Subcommands Subcommands can be defined for a more git-like command line app. From cfb38830724cc34fedffe9a2a29fb54fa9169cd1 Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Thu, 10 Aug 2017 18:42:03 -0700 Subject: [PATCH 024/215] Prepare CHANGELOG for v1.20.0 release --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07f7546..401eae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,49 @@ ## [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 From 1d334f10ce73c2b9e65c50a2290a86be3c743ff2 Mon Sep 17 00:00:00 2001 From: "Alan D. Cabrera" Date: Fri, 8 Sep 2017 10:37:48 -0700 Subject: [PATCH 025/215] Add newline before command categories The simple formatting change adds a nice blank line before each command category. Documentation in README.md is also updated to be more accurate. --- README.md | 4 ++-- help.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 883cc10..34055fe 100644 --- a/README.md +++ b/README.md @@ -761,11 +761,11 @@ func main() { }, { Name: "add", - Category: "template", + Category: "Template actions", }, { Name: "remove", - Category: "template", + Category: "Template actions", }, } diff --git a/help.go b/help.go index 57ec98d..ed084fc 100644 --- a/help.go +++ b/help.go @@ -29,6 +29,7 @@ AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} COMMANDS:{{range .VisibleCategories}}{{if .Name}} + {{.Name}}:{{end}}{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} From 11d45572f9727acfbc93daa8565f379d396125d6 Mon Sep 17 00:00:00 2001 From: rliebz Date: Sat, 26 Aug 2017 07:42:25 -0400 Subject: [PATCH 026/215] Export funcs to configure flag prefix/env hints This will allow users to customize the prefix section or env hint section of the flag entries in the help menu without having to reimplement the rest of the logic required in defining FlagStringer. --- flag.go | 20 ++++++++++++++------ funcs.go | 8 ++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/flag.go b/flag.go index 877ff35..b17f5b9 100644 --- a/flag.go +++ b/flag.go @@ -37,6 +37,14 @@ var HelpFlag Flag = BoolFlag{ // to display a flag. var FlagStringer FlagStringFunc = stringifyFlag +// FlagNamePrefixer converts a full flag name and its placeholder into the help +// message flag prefix. This is used by the default FlagStringer. +var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames + +// FlagEnvHinter annotates flag help message with the environment variable +// details. This is used by the default FlagStringer. +var FlagEnvHinter FlagEnvHintFunc = withEnvHint + // FlagsByName is a slice of Flag. type FlagsByName []Flag @@ -710,13 +718,13 @@ func stringifyFlag(f Flag) string { switch f.(type) { case IntSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), + return FlagEnvHinter(fv.FieldByName("EnvVar").String(), stringifyIntSliceFlag(f.(IntSliceFlag))) case Int64SliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), + return FlagEnvHinter(fv.FieldByName("EnvVar").String(), stringifyInt64SliceFlag(f.(Int64SliceFlag))) case StringSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), + return FlagEnvHinter(fv.FieldByName("EnvVar").String(), stringifyStringSliceFlag(f.(StringSliceFlag))) } @@ -744,8 +752,8 @@ func stringifyFlag(f Flag) string { usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString)) - return withEnvHint(fv.FieldByName("EnvVar").String(), - fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) + return FlagEnvHinter(fv.FieldByName("EnvVar").String(), + fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) } func stringifyIntSliceFlag(f IntSliceFlag) string { @@ -795,5 +803,5 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string { } 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", FlagNamePrefixer(name, placeholder), usageWithDefault) } diff --git a/funcs.go b/funcs.go index cba5e6c..3ad3c6d 100644 --- a/funcs.go +++ b/funcs.go @@ -26,3 +26,11 @@ type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error // FlagStringFunc is used by the help generation to display a flag, which is // expected to be a single line. type FlagStringFunc func(Flag) string + +// FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix +// text for a flag's full name. +type FlagNamePrefixFunc func(fullName, placeholder string) string + +// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help +// with the environment variable details. +type FlagEnvHintFunc func(envVar, str string) string From cbbe4c1a2c34e52c8ad0937c01c9c15ef407a6d5 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Mon, 18 Sep 2017 00:44:42 -0400 Subject: [PATCH 027/215] Add tests for custom flag prefix/env hints --- flag_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/flag_test.go b/flag_test.go index 1ccb639..bc840b5 100644 --- a/flag_test.go +++ b/flag_test.go @@ -158,6 +158,83 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { } } +var prefixStringFlagTests = []struct { + name string + usage string + value string + prefixer FlagNamePrefixFunc + expected string +}{ + {"foo", "", "", func(a, b string) string { + return fmt.Sprintf("name: %s, ph: %s", a, b) + }, "name: foo, ph: value\t"}, + {"f", "", "", func(a, b string) string { + return fmt.Sprintf("name: %s, ph: %s", a, b) + }, "name: f, ph: value\t"}, + {"f", "The total `foo` desired", "all", func(a, b string) string { + return fmt.Sprintf("name: %s, ph: %s", a, b) + }, "name: f, ph: foo\tThe total foo desired (default: \"all\")"}, + {"test", "", "Something", func(a, b string) string { + return fmt.Sprintf("name: %s, ph: %s", a, b) + }, "name: test, ph: value\t(default: \"Something\")"}, + {"config,c", "Load configuration from `FILE`", "", func(a, b string) string { + return fmt.Sprintf("name: %s, ph: %s", a, b) + }, "name: config,c, ph: FILE\tLoad configuration from FILE"}, + {"config,c", "Load configuration from `CONFIG`", "config.json", func(a, b string) string { + return fmt.Sprintf("name: %s, ph: %s", a, b) + }, "name: config,c, ph: CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"}, +} + +func TestFlagNamePrefixer(t *testing.T) { + defer func() { + FlagNamePrefixer = prefixedNames + }() + + for _, test := range prefixStringFlagTests { + FlagNamePrefixer = test.prefixer + flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value} + output := flag.String() + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +var envHintFlagTests = []struct { + name string + env string + hinter FlagEnvHintFunc + expected string +}{ + {"foo", "", func(a, b string) string { + return fmt.Sprintf("env: %s, str: %s", a, b) + }, "env: , str: --foo value\t"}, + {"f", "", func(a, b string) string { + return fmt.Sprintf("env: %s, str: %s", a, b) + }, "env: , str: -f value\t"}, + {"foo", "ENV_VAR", func(a, b string) string { + return fmt.Sprintf("env: %s, str: %s", a, b) + }, "env: ENV_VAR, str: --foo value\t"}, + {"f", "ENV_VAR", func(a, b string) string { + return fmt.Sprintf("env: %s, str: %s", a, b) + }, "env: ENV_VAR, str: -f value\t"}, +} + +func TestFlagEnvHinter(t *testing.T) { + defer func() { + FlagEnvHinter = withEnvHint + }() + + for _, test := range envHintFlagTests { + FlagEnvHinter = test.hinter + flag := StringFlag{Name: test.name, EnvVar: test.env} + output := flag.String() + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + var stringSliceFlagTests = []struct { name string value *StringSlice From 67ee172e6da2cdad8e48af107eef0fbfd1e85eec Mon Sep 17 00:00:00 2001 From: Sebastian Sprenger Date: Fri, 6 Oct 2017 07:28:18 +0200 Subject: [PATCH 028/215] fix misspelling issue --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index db94191..012b9b5 100644 --- a/context.go +++ b/context.go @@ -73,7 +73,7 @@ func (c *Context) IsSet(name string) bool { // 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. + // variables is available. // // See https://github.com/urfave/cli/issues/294 for additional discussion flags := c.Command.Flags From c3cc74dac756e33c2919ab998481809e8720e068 Mon Sep 17 00:00:00 2001 From: Sebastian Sprenger Date: Fri, 6 Oct 2017 07:28:43 +0200 Subject: [PATCH 029/215] fix ineffective assigns --- app_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/app_test.go b/app_test.go index e14ddaf..54e0951 100644 --- a/app_test.go +++ b/app_test.go @@ -497,7 +497,6 @@ func TestApp_Float64Flag(t *testing.T) { } func TestApp_ParseSliceFlags(t *testing.T) { - var parsedOption, firstArg string var parsedIntSlice []int var parsedStringSlice []string @@ -511,8 +510,6 @@ func TestApp_ParseSliceFlags(t *testing.T) { Action: func(c *Context) error { parsedIntSlice = c.IntSlice("p") parsedStringSlice = c.StringSlice("ip") - parsedOption = c.String("option") - firstArg = c.Args().First() return nil }, } From c202606a17a763fcc1b320cac6cf584662e31364 Mon Sep 17 00:00:00 2001 From: Sebastian Sprenger Date: Fri, 6 Oct 2017 07:29:13 +0200 Subject: [PATCH 030/215] fix golint issues --- altsrc/map_input_source.go | 14 +++++++------- altsrc/toml_file_loader.go | 4 ++-- altsrc/yaml_file_loader.go | 4 ++-- app.go | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index b3169e0..a111eee 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -22,15 +22,15 @@ func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool if sections := strings.Split(name, "."); len(sections) > 1 { node := tree for _, section := range sections[:len(sections)-1] { - if child, ok := node[section]; !ok { + child, ok := node[section] + if !ok { return nil, false - } else { - if ctype, ok := child.(map[interface{}]interface{}); !ok { - return nil, false - } else { - node = ctype - } } + ctype, ok := child.(map[interface{}]interface{}) + if !ok { + return nil, false + } + node = ctype } if val, ok := node[sections[len(sections)-1]]; ok { return val, true diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index 37870fc..87bb9d9 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -66,9 +66,9 @@ func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) { return ret, nil } -func (self *tomlMap) UnmarshalTOML(i interface{}) error { +func (tm *tomlMap) UnmarshalTOML(i interface{}) error { if tmp, err := unmarshalMap(i); err == nil { - self.Map = tmp + tm.Map = tmp } else { return err } diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index dd808d5..202469f 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -86,7 +86,7 @@ func loadDataFrom(filePath string) ([]byte, error) { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } return ioutil.ReadFile(filePath) - } else { - return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) } + + return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) } diff --git a/app.go b/app.go index 51fc45d..5991226 100644 --- a/app.go +++ b/app.go @@ -491,7 +491,7 @@ func HandleAction(action interface{}, context *Context) (err error) { } else if a, ok := action.(func(*Context)); ok { // deprecated function signature a(context) return nil - } else { - return errInvalidActionType } + + return errInvalidActionType } From b44660ac3da2f8e651372c40ae803782bddea283 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Sat, 28 Oct 2017 03:00:11 -0400 Subject: [PATCH 031/215] Consider case when sorting strings This makes sorting flags and other sections consistent with how most command line tools function, by placing both flags `-A` and `-a` before a flag `-B`. --- category.go | 2 +- command.go | 2 +- flag.go | 2 +- sort.go | 29 +++++++++++++++++++++++++++++ sort_test.go | 30 ++++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 sort.go create mode 100644 sort_test.go diff --git a/category.go b/category.go index 1a60550..bf3c73c 100644 --- a/category.go +++ b/category.go @@ -10,7 +10,7 @@ type CommandCategory struct { } func (c CommandCategories) Less(i, j int) bool { - return c[i].Name < c[j].Name + return lexicographicLess(c[i].Name, c[j].Name) } func (c CommandCategories) Len() int { diff --git a/command.go b/command.go index 502fc9f..be8f8f0 100644 --- a/command.go +++ b/command.go @@ -73,7 +73,7 @@ func (c CommandsByName) Len() int { } func (c CommandsByName) Less(i, j int) bool { - return c[i].Name < c[j].Name + return lexicographicLess(c[i].Name, c[j].Name) } func (c CommandsByName) Swap(i, j int) { diff --git a/flag.go b/flag.go index b17f5b9..53fb8bb 100644 --- a/flag.go +++ b/flag.go @@ -53,7 +53,7 @@ func (f FlagsByName) Len() int { } func (f FlagsByName) Less(i, j int) bool { - return f[i].GetName() < f[j].GetName() + return lexicographicLess(f[i].GetName(), f[j].GetName()) } func (f FlagsByName) Swap(i, j int) { diff --git a/sort.go b/sort.go new file mode 100644 index 0000000..23d1c2f --- /dev/null +++ b/sort.go @@ -0,0 +1,29 @@ +package cli + +import "unicode" + +// lexicographicLess compares strings alphabetically considering case. +func lexicographicLess(i, j string) bool { + iRunes := []rune(i) + jRunes := []rune(j) + + lenShared := len(iRunes) + if lenShared > len(jRunes) { + lenShared = len(jRunes) + } + + for index := 0; index < lenShared; index++ { + ir := iRunes[index] + jr := jRunes[index] + + if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr { + return lir < ljr + } + + if ir != jr { + return ir < jr + } + } + + return i < j +} diff --git a/sort_test.go b/sort_test.go new file mode 100644 index 0000000..662ef9b --- /dev/null +++ b/sort_test.go @@ -0,0 +1,30 @@ +package cli + +import "testing" + +var lexicographicLessTests = []struct { + i string + j string + expected bool +}{ + {"", "a", true}, + {"a", "", false}, + {"a", "a", false}, + {"a", "A", false}, + {"A", "a", true}, + {"aa", "a", false}, + {"a", "aa", true}, + {"a", "b", true}, + {"a", "B", true}, + {"A", "b", true}, + {"A", "B", true}, +} + +func TestLexicographicLess(t *testing.T) { + for _, test := range lexicographicLessTests { + actual := lexicographicLess(test.i, test.j) + if test.expected != actual { + t.Errorf(`expected string "%s" to come before "%s"`, test.i, test.j) + } + } +} From 21fcab0dee7dab6969e929cf1740306bae1e16ad Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Fri, 31 Mar 2017 16:24:15 +0900 Subject: [PATCH 032/215] ability to load variable from file --- context.go | 29 +++-- flag.go | 251 ++++++++++++++++---------------------------- flag_generated.go | 53 ++++++---- flag_test.go | 20 ++++ generate-flag-types | 1 + 5 files changed, 166 insertions(+), 188 deletions(-) diff --git a/context.go b/context.go index 012b9b5..552ee74 100644 --- a/context.go +++ b/context.go @@ -3,6 +3,7 @@ package cli import ( "errors" "flag" + "os" "reflect" "strings" "syscall" @@ -93,18 +94,26 @@ func (c *Context) IsSet(name string) bool { val = val.Elem() } - envVarValue := val.FieldByName("EnvVar") - if !envVarValue.IsValid() { - return + 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 + } + }) } - eachName(envVarValue.String(), func(envVar string) { - envVar = strings.TrimSpace(envVar) - if _, ok := syscall.Getenv(envVar); ok { - 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 + } + }) + } }) } } diff --git a/flag.go b/flag.go index 53fb8bb..4042093 100644 --- a/flag.go +++ b/flag.go @@ -3,6 +3,7 @@ package cli import ( "flag" "fmt" + "io/ioutil" "reflect" "runtime" "strconv" @@ -120,15 +121,9 @@ func (f GenericFlag) Apply(set *flag.FlagSet) { // 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, 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 - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.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) } } @@ -171,21 +166,15 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - newVal := &StringSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(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 + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &StringSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(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 } eachName(f.Name, func(name string) { @@ -234,21 +223,15 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - newVal := &IntSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - 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 + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &IntSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + 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 } eachName(f.Name, func(name string) { @@ -297,21 +280,15 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - newVal := &Int64Slice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - 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 + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &Int64Slice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + 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 } eachName(f.Name, func(name string) { @@ -332,23 +309,15 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { // 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, 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 + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if envVal == "" { + val = false + } else { + 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 } } @@ -372,23 +341,16 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) { // 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, 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 + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if envVal == "" { + val = false + } else { + 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 } } @@ -411,14 +373,8 @@ func (f StringFlag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - f.Value = envVal - break - } - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + f.Value = envVal } eachName(f.Name, func(name string) { @@ -440,18 +396,12 @@ func (f IntFlag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - 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 - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + 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) { @@ -473,19 +423,13 @@ func (f Int64Flag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - 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 - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + 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) { @@ -507,19 +451,13 @@ func (f UintFlag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - 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 - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + 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) { @@ -541,19 +479,13 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - 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 - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + 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) { @@ -575,19 +507,13 @@ func (f DurationFlag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - envValDuration, err := time.ParseDuration(envVal) - if err != nil { - return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValDuration - break - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValDuration, err := time.ParseDuration(envVal) + 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) { @@ -609,19 +535,13 @@ func (f Float64Flag) Apply(set *flag.FlagSet) { // 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, ok := syscall.Getenv(envVar); ok { - 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) - break - } + if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + 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) { @@ -805,3 +725,18 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string { usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) return fmt.Sprintf("%s\t%s", FlagNamePrefixer(name, placeholder), usageWithDefault) } + +func flagFromFileEnv(filePath, envName string) (val string, ok bool) { + for _, envVar := range strings.Split(envName, ",") { + envVar = strings.TrimSpace(envVar) + if envVal, ok := syscall.Getenv(envVar); ok { + return envVal, true + } + } + if filePath != "" { + if data, err := ioutil.ReadFile(filePath); err == nil { + return string(data), true + } + } + return +} diff --git a/flag_generated.go b/flag_generated.go index 491b619..001576c 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -13,6 +13,7 @@ type BoolFlag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Destination *bool } @@ -60,6 +61,7 @@ type BoolTFlag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Destination *bool } @@ -107,6 +109,7 @@ type DurationFlag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Value time.Duration Destination *time.Duration @@ -155,6 +158,7 @@ type Float64Flag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Value float64 Destination *float64 @@ -200,11 +204,12 @@ func lookupFloat64(name string, set *flag.FlagSet) float64 { // GenericFlag is a flag with type Generic type GenericFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value Generic + Name string + Usage string + EnvVar string + FilePath string + Hidden bool + Value Generic } // String returns a readable representation of this value @@ -250,6 +255,7 @@ type Int64Flag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Value int64 Destination *int64 @@ -298,6 +304,7 @@ type IntFlag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Value int Destination *int @@ -343,11 +350,12 @@ func lookupInt(name string, set *flag.FlagSet) int { // IntSliceFlag is a flag with type *IntSlice type IntSliceFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value *IntSlice + Name string + Usage string + EnvVar string + FilePath string + Hidden bool + Value *IntSlice } // 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 type Int64SliceFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value *Int64Slice + Name string + Usage string + EnvVar string + FilePath string + Hidden bool + Value *Int64Slice } // String returns a readable representation of this value @@ -440,6 +449,7 @@ type StringFlag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Value string Destination *string @@ -485,11 +495,12 @@ func lookupString(name string, set *flag.FlagSet) string { // StringSliceFlag is a flag with type *StringSlice type StringSliceFlag struct { - Name string - Usage string - EnvVar string - Hidden bool - Value *StringSlice + Name string + Usage string + EnvVar string + FilePath string + Hidden bool + Value *StringSlice } // String returns a readable representation of this value @@ -535,6 +546,7 @@ type Uint64Flag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Value uint64 Destination *uint64 @@ -583,6 +595,7 @@ type UintFlag struct { Name string Usage string EnvVar string + FilePath string Hidden bool Value uint Destination *uint diff --git a/flag_test.go b/flag_test.go index bc840b5..66836f3 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1290,3 +1290,23 @@ func TestParseGenericFromEnvCascade(t *testing.T) { } a.Run([]string{"run"}) } + +// func TestFlagFromFile(t *testing.T) { +// temp, err := ioutil.TempFile("", "urfave_cli_test") +// if err != nil { +// t.Error(err) +// return +// } +// io.WriteString(temp, "abc") +// temp.Close() +// defer func() { +// os.Remove(temp.Name()) +// }() +// +// if want, got := flagFromFileEnv("file-does-not-exist", "123"), "123"; want != got { +// t.Errorf("Did not expect %v - Got %v", want, got) +// } +// if want, got := flagFromFile(temp.Name(), "123"), "abc"; want != got { +// t.Errorf("Did not expect %v - Got %v", want, got) +// } +// } diff --git a/generate-flag-types b/generate-flag-types index 7147381..1358857 100755 --- a/generate-flag-types +++ b/generate-flag-types @@ -142,6 +142,7 @@ def _write_cli_flag_types(outfile, types): Name string Usage string EnvVar string + FilePath string Hidden bool """.format(**typedef)) From c698b821b896e9723d53c4ad1e81680f39a8cdc1 Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Sat, 1 Apr 2017 12:37:06 +0900 Subject: [PATCH 033/215] unit tests for load from file --- flag_test.go | 55 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/flag_test.go b/flag_test.go index 66836f3..98c2fb9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -2,6 +2,8 @@ package cli import ( "fmt" + "io" + "io/ioutil" "os" "reflect" "regexp" @@ -1291,22 +1293,37 @@ func TestParseGenericFromEnvCascade(t *testing.T) { a.Run([]string{"run"}) } -// func TestFlagFromFile(t *testing.T) { -// temp, err := ioutil.TempFile("", "urfave_cli_test") -// if err != nil { -// t.Error(err) -// return -// } -// io.WriteString(temp, "abc") -// temp.Close() -// defer func() { -// os.Remove(temp.Name()) -// }() -// -// if want, got := flagFromFileEnv("file-does-not-exist", "123"), "123"; want != got { -// t.Errorf("Did not expect %v - Got %v", want, got) -// } -// if want, got := flagFromFile(temp.Name(), "123"), "abc"; want != got { -// t.Errorf("Did not expect %v - Got %v", want, got) -// } -// } +func TestFlagFromFile(t *testing.T) { + os.Clearenv() + os.Setenv("APP_FOO", "123") + + temp, err := ioutil.TempFile("", "urfave_cli_test") + if err != nil { + t.Error(err) + return + } + io.WriteString(temp, "abc") + temp.Close() + defer func() { + os.Remove(temp.Name()) + }() + + var filePathTests = []struct { + path string + name string + expected string + }{ + {"file-does-not-exist", "APP_BAR", ""}, + {"file-does-not-exist", "APP_FOO", "123"}, + {"file-does-not-exist", "APP_FOO,APP_BAR", "123"}, + {temp.Name(), "APP_FOO", "123"}, + {temp.Name(), "APP_BAR", "abc"}, + } + + for _, filePathTest := range filePathTests { + got, _ := flagFromFileEnv(filePathTest.path, filePathTest.name) + if want := filePathTest.expected; got != want { + t.Errorf("Did not expect %v - Want %v", got, want) + } + } +} From 4cc453ba6792515a8013340f8919e6c4b44851b7 Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Sat, 1 Apr 2017 12:55:46 +0900 Subject: [PATCH 034/215] document field in README --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 34055fe..aa8f2cb 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ applications in an expressive way. + [Alternate Names](#alternate-names) + [Ordering](#ordering) + [Values from the Environment](#values-from-the-environment) + + [Values from files](#values-from-files) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) + [Precedence](#precedence) * [Subcommands](#subcommands) @@ -587,6 +588,38 @@ func main() { } ``` +#### Values from Files + +You can also have the default value set from file via `FilePath`. e.g. + + +``` 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) +} +``` + #### Values from alternate input sources (YAML, TOML, and others) There is a separate package altsrc that adds support for getting flag values From 18a556e1927fbe11c31fae47a7e3acf275ef6ae4 Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Mon, 10 Apr 2017 16:45:51 +0200 Subject: [PATCH 035/215] fix FilePath documentation in README.md --- README.md | 7 ++++-- flag.go | 62 +++++++++++++++++++++++++++++++++++++++++----------- flag_test.go | 2 +- funcs.go | 3 +++ 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index aa8f2cb..a2fd41d 100644 --- a/README.md +++ b/README.md @@ -588,13 +588,13 @@ func main() { } ``` -#### Values from Files +#### Values from files You can also have the default value set from file via `FilePath`. e.g. ``` go package main @@ -620,6 +620,9 @@ func main() { } ``` +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 diff --git a/flag.go b/flag.go index 4042093..68942ae 100644 --- a/flag.go +++ b/flag.go @@ -46,6 +46,10 @@ var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames // details. This is used by the default FlagStringer. var FlagEnvHinter FlagEnvHintFunc = withEnvHint +// FlagFileHinter annotates flag help message with the environment variable +// details. This is used by the default FlagStringer. +var FlagFileHinter FlagFileHintFunc = withFileHint + // FlagsByName is a slice of Flag. type FlagsByName []Flag @@ -625,6 +629,14 @@ func withEnvHint(envVar, str string) string { 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 { fv := reflect.ValueOf(f) for fv.Kind() == reflect.Ptr { @@ -638,14 +650,33 @@ func stringifyFlag(f Flag) string { switch f.(type) { case IntSliceFlag: - return FlagEnvHinter(fv.FieldByName("EnvVar").String(), - stringifyIntSliceFlag(f.(IntSliceFlag))) + return FlagFileHinter( + fv.FieldByName("FilePath").String(), + FlagEnvHinter( + fv.FieldByName("EnvVar").String(), + stringifyIntSliceFlag(f.(IntSliceFlag)), + ), + ) case Int64SliceFlag: - return FlagEnvHinter(fv.FieldByName("EnvVar").String(), - stringifyInt64SliceFlag(f.(Int64SliceFlag))) + // return FlagEnvHinter(fv.FieldByName("EnvVar").String(), + // stringifyInt64SliceFlag(f.(Int64SliceFlag))) + return FlagFileHinter( + fv.FieldByName("FilePath").String(), + FlagEnvHinter( + fv.FieldByName("EnvVar").String(), + stringifyInt64SliceFlag(f.(Int64SliceFlag)), + ), + ) case StringSliceFlag: - return FlagEnvHinter(fv.FieldByName("EnvVar").String(), - stringifyStringSliceFlag(f.(StringSliceFlag))) + // return FlagEnvHinter(fv.FieldByName("EnvVar").String(), + // stringifyStringSliceFlag(f.(StringSliceFlag))) + return FlagFileHinter( + fv.FieldByName("FilePath").String(), + FlagEnvHinter( + fv.FieldByName("EnvVar").String(), + stringifyStringSliceFlag(f.(StringSliceFlag)), + ), + ) } placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) @@ -672,8 +703,13 @@ func stringifyFlag(f Flag) string { usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString)) - return FlagEnvHinter(fv.FieldByName("EnvVar").String(), - fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) + return FlagFileHinter( + fv.FieldByName("FilePath").String(), + FlagEnvHinter( + fv.FieldByName("EnvVar").String(), + fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault), + ), + ) } func stringifyIntSliceFlag(f IntSliceFlag) string { @@ -727,16 +763,16 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string { } 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 } } - if filePath != "" { - if data, err := ioutil.ReadFile(filePath); err == nil { - return string(data), true - } - } return } diff --git a/flag_test.go b/flag_test.go index 98c2fb9..fb9163b 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1316,7 +1316,7 @@ func TestFlagFromFile(t *testing.T) { {"file-does-not-exist", "APP_BAR", ""}, {"file-does-not-exist", "APP_FOO", "123"}, {"file-does-not-exist", "APP_FOO,APP_BAR", "123"}, - {temp.Name(), "APP_FOO", "123"}, + {temp.Name(), "APP_FOO", "abc"}, {temp.Name(), "APP_BAR", "abc"}, } diff --git a/funcs.go b/funcs.go index b335dbf..0036b11 100644 --- a/funcs.go +++ b/funcs.go @@ -39,3 +39,6 @@ type FlagNamePrefixFunc func(fullName, placeholder string) string // with the environment variable details. type FlagEnvHintFunc func(envVar, str string) string +// FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help +// with the file path details. +type FlagFileHintFunc func(filePath, str string) string From f971fca2b2664c4dec0cee24225dc3c415211498 Mon Sep 17 00:00:00 2001 From: Jacob McCann Date: Thu, 26 Oct 2017 13:08:03 -0500 Subject: [PATCH 036/215] Allow FilePath to take []string --- flag.go | 22 +++++++++------------- flag_test.go | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/flag.go b/flag.go index 68942ae..f8d4f51 100644 --- a/flag.go +++ b/flag.go @@ -125,9 +125,9 @@ func (f GenericFlag) Apply(set *flag.FlagSet) { // provided by the user for parsing by the flag func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { val := f.Value - if envVal, ok := flagFromFileEnv(f.FilePath, f.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) + if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if err := val.Set(fileEnvVal); err != nil { + return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) } } @@ -658,8 +658,6 @@ func stringifyFlag(f Flag) string { ), ) case Int64SliceFlag: - // return FlagEnvHinter(fv.FieldByName("EnvVar").String(), - // stringifyInt64SliceFlag(f.(Int64SliceFlag))) return FlagFileHinter( fv.FieldByName("FilePath").String(), FlagEnvHinter( @@ -668,8 +666,6 @@ func stringifyFlag(f Flag) string { ), ) case StringSliceFlag: - // return FlagEnvHinter(fv.FieldByName("EnvVar").String(), - // stringifyStringSliceFlag(f.(StringSliceFlag))) return FlagFileHinter( fv.FieldByName("FilePath").String(), FlagEnvHinter( @@ -763,16 +759,16 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string { } 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 + for _, fileVar := range strings.Split(filePath, ",") { + if data, err := ioutil.ReadFile(fileVar); err == nil { + return string(data), true + } + } + return "", false } diff --git a/flag_test.go b/flag_test.go index fb9163b..98c2fb9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1316,7 +1316,7 @@ func TestFlagFromFile(t *testing.T) { {"file-does-not-exist", "APP_BAR", ""}, {"file-does-not-exist", "APP_FOO", "123"}, {"file-does-not-exist", "APP_FOO,APP_BAR", "123"}, - {temp.Name(), "APP_FOO", "abc"}, + {temp.Name(), "APP_FOO", "123"}, {temp.Name(), "APP_BAR", "abc"}, } From 43c8c02cf5a10196e5a4c458fdbfee90a561e97c Mon Sep 17 00:00:00 2001 From: zhuchensong Date: Mon, 17 Apr 2017 00:47:04 +0800 Subject: [PATCH 037/215] Support POSIX-style short flag combining --- command.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index be8f8f0..189209d 100644 --- a/command.go +++ b/command.go @@ -142,7 +142,19 @@ func (c Command) Run(ctx *Context) (err error) { flagArgs = args[firstFlagIndex:] } - err = set.Parse(append(flagArgs, regularArgs...)) + // separate combined flags + var flagArgsSeparated []string + for _, flagArg := range flagArgs { + if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) >2 { + for _, flagChar := range flagArg[1:] { + flagArgsSeparated = append(flagArgsSeparated, "-" + string(flagChar)) + } + } else { + flagArgsSeparated = append(flagArgsSeparated, flagArg) + } + } + + err = set.Parse(append(flagArgsSeparated, regularArgs...)) } else { err = set.Parse(ctx.Args().Tail()) } From 9838c8bcaa19fdb33259f6e0f9740d9fd3cbe13c Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sat, 11 Nov 2017 16:23:24 -0800 Subject: [PATCH 038/215] Update README examples to check for errors To encourage good practices. --- README.md | 138 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 114 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index a2fd41d..ca5c0cf 100644 --- a/README.md +++ b/README.md @@ -140,13 +140,17 @@ discovery. So a cli app can be as little as one line of code in `main()`. package main import ( + "log" "os" "github.com/urfave/cli" ) func main() { - cli.NewApp().Run(os.Args) + err := cli.NewApp().Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -161,6 +165,7 @@ package main import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -175,7 +180,10 @@ func main() { return nil } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -199,6 +207,7 @@ package main import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -213,7 +222,10 @@ func main() { return nil } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -262,6 +274,7 @@ package main import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -275,7 +288,10 @@ func main() { return nil } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -291,6 +307,7 @@ package main import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -320,7 +337,10 @@ func main() { return nil } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -334,6 +354,7 @@ scanned. package main import ( + "log" "os" "fmt" @@ -367,7 +388,10 @@ func main() { return nil } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -388,6 +412,7 @@ For example this: package main import ( + "log" "os" "github.com/urfave/cli" @@ -403,7 +428,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -429,6 +457,7 @@ list for the `Name`. e.g. package main import ( + "log" "os" "github.com/urfave/cli" @@ -445,7 +474,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -469,6 +501,7 @@ For example this: package main import ( + "log" "os" "sort" @@ -512,7 +545,10 @@ func main() { sort.Sort(cli.FlagsByName(app.Flags)) sort.Sort(cli.CommandsByName(app.Commands)) - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -535,6 +571,7 @@ You can also have the default value set from the environment via `EnvVar`. e.g. package main import ( + "log" "os" "github.com/urfave/cli" @@ -552,7 +589,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -567,6 +607,7 @@ environment variable that resolves is used as the default. package main import ( + "log" "os" "github.com/urfave/cli" @@ -584,7 +625,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -600,6 +644,7 @@ You can also have the default value set from file via `FilePath`. e.g. package main import ( + "log" "os" "github.com/urfave/cli" @@ -616,7 +661,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -667,6 +715,7 @@ package notmain import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -689,7 +738,10 @@ func main() { app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")) app.Flags = flags - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -715,6 +767,7 @@ package main import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -767,7 +820,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -783,6 +839,7 @@ E.g. package main import ( + "log" "os" "github.com/urfave/cli" @@ -805,7 +862,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -831,6 +891,7 @@ may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a package main import ( + "log" "os" "github.com/urfave/cli" @@ -851,7 +912,10 @@ func main() { return nil } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -871,6 +935,7 @@ package main import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -902,7 +967,10 @@ func main() { }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -942,6 +1010,7 @@ The default bash completion flag (`--generate-bash-completion`) is defined as package main import ( + "log" "os" "github.com/urfave/cli" @@ -960,7 +1029,10 @@ func main() { Name: "wat", }, } - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -986,6 +1058,7 @@ package main import ( "fmt" + "log" "io" "os" @@ -1029,7 +1102,10 @@ VERSION: fmt.Println("Ha HA. I pwnd the help!!1") } - cli.NewApp().Run(os.Args) + err := cli.NewApp().Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -1044,6 +1120,7 @@ setting `cli.HelpFlag`, e.g.: package main import ( + "log" "os" "github.com/urfave/cli" @@ -1056,7 +1133,10 @@ func main() { EnvVar: "SHOW_HALP,HALPPLZ", } - cli.NewApp().Run(os.Args) + err := cli.NewApp().Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -1079,6 +1159,7 @@ setting `cli.VersionFlag`, e.g.: package main import ( + "log" "os" "github.com/urfave/cli" @@ -1093,7 +1174,10 @@ func main() { app := cli.NewApp() app.Name = "partay" app.Version = "19.99.0" - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -1108,6 +1192,7 @@ package main import ( "fmt" + "log" "os" "github.com/urfave/cli" @@ -1125,7 +1210,10 @@ func main() { app := cli.NewApp() app.Name = "partay" app.Version = "19.99.0" - app.Run(os.Args) + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } ``` @@ -1387,7 +1475,7 @@ func main() { ec := cli.NewExitError("ohwell", 86) fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode()) fmt.Printf("made it!\n") - return ec + return nil } if os.Getenv("HEXY") != "" { @@ -1401,7 +1489,9 @@ func main() { "whatever-values": 19.99, } - app.Run(os.Args) + + // ignore error so we don't exit non-zero and break gfmrun README example tests + _ = app.Run(os.Args) } func wopAction(c *cli.Context) error { From fd5382e7a539858cc19d7eed7755f7102bae5da9 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 13 Nov 2017 15:28:23 -0600 Subject: [PATCH 039/215] Combine bool short names Adds the ability to allow the combination of bool short-name options. For example, cmd foobar -ov This is done through a bool "UseShortOptionHandler" set in the command struct. Built upon PR #621 Signed-off-by: baude --- README.md | 21 +++++++++++++++++++++ command.go | 26 ++++++++++++++++---------- command_test.go | 36 ++++++++++++++++++++---------------- flag_test.go | 25 +++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a2fd41d..6096701 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ applications in an expressive way. * [Version Flag](#version-flag) + [Customization](#customization-2) + [Full API Example](#full-api-example) + * [Combining short Bool options](#combining-short-bool-options) - [Contribution Guidelines](#contribution-guidelines) @@ -1410,6 +1411,26 @@ func wopAction(c *cli.Context) error { } ``` +### Combining short Bool options + +Traditional use of boolean options using their shortnames look like this: +``` +# cmd foobar -s -o +``` + +Suppose you want users to be able to combine your bool options with their shortname. This +can be done using the **UseShortOptionHandling** bool in your commands. Suppose your program +has a two bool flags such as *serve* and *option* with the short options of *-o* and +*-s* respectively. With **UseShortOptionHandling** set to *true*, a user can use a syntax +like: +``` +# cmd foobar -so +``` + +If you enable the **UseShortOptionHandling*, then you must not use any flags that have a single +leading *-* or this will result in failures. For example, **-option** can no longer be used. Flags +with two leading dashes (such as **--options**) are still valid. + ## Contribution Guidelines Feel free to put up a pull request to fix a bug or maybe add a feature. I will diff --git a/command.go b/command.go index 189209d..b559811 100644 --- a/command.go +++ b/command.go @@ -55,6 +55,10 @@ type Command struct { HideHelp bool // Boolean to hide this command from help or completion Hidden bool + // Boolean to enable short-option handling so user can combine several + // single-character bool arguements into one + // i.e. foobar -o -v -> foobar -ov + UseShortOptionHandling bool // Full name of command for help, defaults to full command name, including parent commands. HelpName string @@ -141,20 +145,22 @@ func (c Command) Run(ctx *Context) (err error) { } else { flagArgs = args[firstFlagIndex:] } - // separate combined flags - var flagArgsSeparated []string - for _, flagArg := range flagArgs { - if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) >2 { - for _, flagChar := range flagArg[1:] { - flagArgsSeparated = append(flagArgsSeparated, "-" + string(flagChar)) + if c.UseShortOptionHandling { + var flagArgsSeparated []string + for _, flagArg := range flagArgs { + if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 { + for _, flagChar := range flagArg[1:] { + flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar)) + } + } else { + flagArgsSeparated = append(flagArgsSeparated, flagArg) } - } else { - flagArgsSeparated = append(flagArgsSeparated, flagArg) } + err = set.Parse(append(flagArgsSeparated, regularArgs...)) + } else { + err = set.Parse(append(flagArgs, regularArgs...)) } - - err = set.Parse(append(flagArgsSeparated, regularArgs...)) } else { err = set.Parse(ctx.Args().Tail()) } diff --git a/command_test.go b/command_test.go index 4ad994c..d9d7094 100644 --- a/command_test.go +++ b/command_test.go @@ -11,20 +11,23 @@ import ( func TestCommandFlagParsing(t *testing.T) { cases := []struct { - testArgs []string - skipFlagParsing bool - skipArgReorder bool - expectedErr error + testArgs []string + skipFlagParsing bool + skipArgReorder bool + expectedErr error + UseShortOptionHandling bool }{ // Test normal "not ignoring flags" flow - {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break")}, + {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break"), false}, // Test no arg reorder - {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil}, + {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false}, + + {[]string{"test-cmd", "blah", "blah"}, true, false, nil, false}, // Test SkipFlagParsing without any args that look like flags + {[]string{"test-cmd", "blah", "-break"}, true, false, nil, false}, // Test SkipFlagParsing with random flag arg + {[]string{"test-cmd", "blah", "-help"}, true, false, nil, false}, // Test SkipFlagParsing with "special" help flag arg + {[]string{"test-cmd", "blah"}, false, false, nil, true}, // Test UseShortOptionHandling - {[]string{"test-cmd", "blah", "blah"}, true, false, nil}, // Test SkipFlagParsing without any args that look like flags - {[]string{"test-cmd", "blah", "-break"}, true, false, nil}, // Test SkipFlagParsing with random flag arg - {[]string{"test-cmd", "blah", "-help"}, true, false, nil}, // Test SkipFlagParsing with "special" help flag arg } for _, c := range cases { @@ -36,13 +39,14 @@ func TestCommandFlagParsing(t *testing.T) { context := NewContext(app, set, nil) command := Command{ - Name: "test-cmd", - Aliases: []string{"tc"}, - Usage: "this is for testing", - Description: "testing", - Action: func(_ *Context) error { return nil }, - SkipFlagParsing: c.skipFlagParsing, - SkipArgReorder: c.skipArgReorder, + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(_ *Context) error { return nil }, + SkipFlagParsing: c.skipFlagParsing, + SkipArgReorder: c.skipArgReorder, + UseShortOptionHandling: c.UseShortOptionHandling, } err := command.Run(context) diff --git a/flag_test.go b/flag_test.go index 98c2fb9..da9fd73 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1048,6 +1048,31 @@ func TestParseMultiBool(t *testing.T) { a.Run([]string{"run", "--serve"}) } +func TestParseBoolShortOptionHandle(t *testing.T) { + a := App{ + Commands: []Command{ + { + Name: "foobar", + UseShortOptionHandling: true, + Action: func(ctx *Context) error { + if ctx.Bool("serve") != true { + t.Errorf("main name not set") + } + if ctx.Bool("option") != true { + t.Errorf("short name not set") + } + return nil + }, + Flags: []Flag{ + BoolFlag{Name: "serve, s"}, + BoolFlag{Name: "option, o"}, + }, + }, + }, + } + a.Run([]string{"run", "foobar", "-so"}) +} + func TestParseDestinationBool(t *testing.T) { var dest bool a := App{ From 37b7abb1c491c8c3630a2a98bb02a7051efbcc06 Mon Sep 17 00:00:00 2001 From: Joshua Rubin Date: Tue, 21 Nov 2017 15:21:31 -0700 Subject: [PATCH 040/215] dont clobber slices with envvar Signed-off-by: Joshua Rubin --- flag.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/flag.go b/flag.go index f8d4f51..d4a4d41 100644 --- a/flag.go +++ b/flag.go @@ -178,7 +178,11 @@ func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) } } - f.Value = newVal + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } } eachName(f.Name, func(name string) { @@ -235,7 +239,11 @@ func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) } } - f.Value = newVal + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } } eachName(f.Name, func(name string) { @@ -292,7 +300,11 @@ func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) } } - f.Value = newVal + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } } eachName(f.Name, func(name string) { From ceaac7c9152121e6ba0f3b492b3254d61346f92a Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 20 Nov 2017 09:32:03 -0600 Subject: [PATCH 041/215] Handle ShortOptions and SkipArgReorder There was a bug in parsing when both ShortOptions and SkipArgReorder were being used together. Signed-off-by: baude --- command.go | 129 ++++++++++++++++++++++++++++++++---------------- command_test.go | 1 + 2 files changed, 87 insertions(+), 43 deletions(-) diff --git a/command.go b/command.go index b559811..7d0357b 100644 --- a/command.go +++ b/command.go @@ -115,57 +115,29 @@ func (c Command) Run(ctx *Context) (err error) { return err } set.SetOutput(ioutil.Discard) - + firstFlagIndex, terminatorIndex := getIndexes(ctx) + flagArgs, regularArgs := getAllArgs(ctx.Args(), firstFlagIndex, terminatorIndex) + if c.UseShortOptionHandling { + flagArgs = translateShortOptions(flagArgs) + } if c.SkipFlagParsing { err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) } else if !c.SkipArgReorder { - firstFlagIndex := -1 - terminatorIndex := -1 - for index, arg := range ctx.Args() { - if arg == "--" { - terminatorIndex = index - break - } else if arg == "-" { - // Do nothing. A dash alone is not really a flag. - continue - } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { - firstFlagIndex = index - } - } - if firstFlagIndex > -1 { - args := ctx.Args() - regularArgs := make([]string, len(args[1:firstFlagIndex])) - copy(regularArgs, args[1:firstFlagIndex]) - - var flagArgs []string - if terminatorIndex > -1 { - flagArgs = args[firstFlagIndex:terminatorIndex] - regularArgs = append(regularArgs, args[terminatorIndex:]...) - } else { - flagArgs = args[firstFlagIndex:] - } - // separate combined flags - if c.UseShortOptionHandling { - var flagArgsSeparated []string - for _, flagArg := range flagArgs { - if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 { - for _, flagChar := range flagArg[1:] { - flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar)) - } - } else { - flagArgsSeparated = append(flagArgsSeparated, flagArg) - } - } - err = set.Parse(append(flagArgsSeparated, regularArgs...)) - } else { - err = set.Parse(append(flagArgs, regularArgs...)) - } + err = set.Parse(append(flagArgs, regularArgs...)) } else { err = set.Parse(ctx.Args().Tail()) } + } else if c.UseShortOptionHandling { + if terminatorIndex == -1 && firstFlagIndex > -1 { + // Handle shortname AND no options + err = set.Parse(append(regularArgs, flagArgs...)) + } else { + // Handle shortname and options + err = set.Parse(flagArgs) + } } else { - err = set.Parse(ctx.Args().Tail()) + err = set.Parse(append(regularArgs, flagArgs...)) } nerr := normalizeFlags(c.Flags, set) @@ -233,6 +205,77 @@ func (c Command) Run(ctx *Context) (err error) { return err } +func getIndexes(ctx *Context) (int, int) { + firstFlagIndex := -1 + terminatorIndex := -1 + for index, arg := range ctx.Args() { + if arg == "--" { + terminatorIndex = index + break + } else if arg == "-" { + // Do nothing. A dash alone is not really a flag. + continue + } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { + firstFlagIndex = index + } + } + if len(ctx.Args()) > 0 && !strings.HasPrefix(ctx.Args()[0], "-") && firstFlagIndex == -1 { + return -1, -1 + } + + return firstFlagIndex, terminatorIndex + +} + +// copyStringslice takes a string slice and copies it +func copyStringSlice(slice []string, start, end int) []string { + newSlice := make([]string, end-start) + copy(newSlice, slice[start:end]) + return newSlice +} + +// getAllArgs extracts and returns two string slices representing +// regularArgs and flagArgs +func getAllArgs(args []string, firstFlagIndex, terminatorIndex int) ([]string, []string) { + var regularArgs []string + // if there are no options, the we set the index to 1 manually + if firstFlagIndex == -1 { + firstFlagIndex = 1 + regularArgs = copyStringSlice(args, 0, len(args)) + } else { + regularArgs = copyStringSlice(args, 1, firstFlagIndex) + } + var flagArgs []string + // a flag terminatorIndex was found in the input. we need to collect + // flagArgs based on it. + if terminatorIndex > -1 { + flagArgs = copyStringSlice(args, firstFlagIndex, terminatorIndex) + additionalRegularArgs := copyStringSlice(args, terminatorIndex, len(args)) + regularArgs = append(regularArgs, additionalRegularArgs...) + for _, i := range additionalRegularArgs { + regularArgs = append(regularArgs, i) + } + } else { + flagArgs = args[firstFlagIndex:] + } + return flagArgs, regularArgs +} + +func translateShortOptions(flagArgs Args) []string { + // separate combined flags + var flagArgsSeparated []string + for _, flagArg := range flagArgs { + if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 { + for _, flagChar := range flagArg[1:] { + flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar)) + } + } else { + flagArgsSeparated = append(flagArgsSeparated, flagArg) + } + } + return flagArgsSeparated +} + // Names returns the names including short names and aliases. func (c Command) Names() []string { names := []string{c.Name} diff --git a/command_test.go b/command_test.go index d9d7094..e69750a 100644 --- a/command_test.go +++ b/command_test.go @@ -22,6 +22,7 @@ func TestCommandFlagParsing(t *testing.T) { // Test no arg reorder {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false}, + {[]string{"test-cmd", "blah", "blah", "-break", "ls", "-l"}, false, true, nil, true}, {[]string{"test-cmd", "blah", "blah"}, true, false, nil, false}, // Test SkipFlagParsing without any args that look like flags {[]string{"test-cmd", "blah", "-break"}, true, false, nil, false}, // Test SkipFlagParsing with random flag arg From c6eb2a051026c083d4e33591f8d6e95d5f4189dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Thu, 30 Nov 2017 19:43:12 -0500 Subject: [PATCH 042/215] Correct go vet for Go tip https://travis-ci.org/cloudflare/logshare/jobs/309796141#L646 --- app.go | 1 - 1 file changed, 1 deletion(-) diff --git a/app.go b/app.go index 60599b0..9add067 100644 --- a/app.go +++ b/app.go @@ -453,7 +453,6 @@ func (a *App) hasFlag(flag Flag) bool { } func (a *App) errWriter() io.Writer { - // When the app ErrWriter is nil use the package level one. if a.ErrWriter == nil { return ErrWriter From df562bf1a8626f2d16f91fcbf7230a5bdca3d592 Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Sun, 3 Dec 2017 13:38:50 -0800 Subject: [PATCH 043/215] Refactor flag handling logic Refactor logic introduced by #686 --- command.go | 134 +++++++++++++++++++++-------------------------------- 1 file changed, 52 insertions(+), 82 deletions(-) diff --git a/command.go b/command.go index 7d0357b..66a58b5 100644 --- a/command.go +++ b/command.go @@ -1,6 +1,7 @@ package cli import ( + "flag" "fmt" "io/ioutil" "sort" @@ -110,43 +111,7 @@ func (c Command) Run(ctx *Context) (err error) { ) } - set, err := flagSet(c.Name, c.Flags) - if err != nil { - return err - } - set.SetOutput(ioutil.Discard) - firstFlagIndex, terminatorIndex := getIndexes(ctx) - flagArgs, regularArgs := getAllArgs(ctx.Args(), firstFlagIndex, terminatorIndex) - if c.UseShortOptionHandling { - flagArgs = translateShortOptions(flagArgs) - } - if c.SkipFlagParsing { - err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...)) - } else if !c.SkipArgReorder { - if firstFlagIndex > -1 { - err = set.Parse(append(flagArgs, regularArgs...)) - } else { - err = set.Parse(ctx.Args().Tail()) - } - } else if c.UseShortOptionHandling { - if terminatorIndex == -1 && firstFlagIndex > -1 { - // Handle shortname AND no options - err = set.Parse(append(regularArgs, flagArgs...)) - } else { - // Handle shortname and options - err = set.Parse(flagArgs) - } - } else { - err = set.Parse(append(regularArgs, flagArgs...)) - } - - nerr := normalizeFlags(c.Flags, set) - if nerr != nil { - fmt.Fprintln(ctx.App.Writer, nerr) - fmt.Fprintln(ctx.App.Writer) - ShowCommandHelp(ctx, c.Name) - return nerr - } + set, err := c.parseFlags(ctx.Args().Tail()) context := NewContext(ctx.App, set, ctx) context.Command = c @@ -205,60 +170,65 @@ func (c Command) Run(ctx *Context) (err error) { return err } -func getIndexes(ctx *Context) (int, int) { - firstFlagIndex := -1 - terminatorIndex := -1 - for index, arg := range ctx.Args() { - if arg == "--" { - terminatorIndex = index - break - } else if arg == "-" { - // Do nothing. A dash alone is not really a flag. - continue - } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { - firstFlagIndex = index - } +func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { + set, err := flagSet(c.Name, c.Flags) + if err != nil { + return nil, err } - if len(ctx.Args()) > 0 && !strings.HasPrefix(ctx.Args()[0], "-") && firstFlagIndex == -1 { - return -1, -1 + set.SetOutput(ioutil.Discard) + + if c.SkipFlagParsing { + return set, set.Parse(append([]string{c.Name, "--"}, args...)) } - return firstFlagIndex, terminatorIndex + if c.UseShortOptionHandling { + args = translateShortOptions(args) + } -} + if !c.SkipArgReorder { + args = reorderArgs(args) + } -// copyStringslice takes a string slice and copies it -func copyStringSlice(slice []string, start, end int) []string { - newSlice := make([]string, end-start) - copy(newSlice, slice[start:end]) - return newSlice -} + err = set.Parse(args) + if err != nil { + return nil, err + } -// getAllArgs extracts and returns two string slices representing -// regularArgs and flagArgs -func getAllArgs(args []string, firstFlagIndex, terminatorIndex int) ([]string, []string) { - var regularArgs []string - // if there are no options, the we set the index to 1 manually - if firstFlagIndex == -1 { - firstFlagIndex = 1 - regularArgs = copyStringSlice(args, 0, len(args)) - } else { - regularArgs = copyStringSlice(args, 1, firstFlagIndex) + err = normalizeFlags(c.Flags, set) + if err != nil { + return nil, err } - var flagArgs []string - // a flag terminatorIndex was found in the input. we need to collect - // flagArgs based on it. - if terminatorIndex > -1 { - flagArgs = copyStringSlice(args, firstFlagIndex, terminatorIndex) - additionalRegularArgs := copyStringSlice(args, terminatorIndex, len(args)) - regularArgs = append(regularArgs, additionalRegularArgs...) - for _, i := range additionalRegularArgs { - regularArgs = append(regularArgs, i) + + return set, nil +} + +// reorderArgs moves all flags before arguments as this is what flag expects +func reorderArgs(args []string) []string { + var nonflags, flags []string + + readFlagValue := false + for i, arg := range args { + if arg == "--" { + nonflags = append(nonflags, args[i:]...) + break + } + + if readFlagValue { + readFlagValue = false + flags = append(flags, arg) + continue + } + + if arg != "-" && strings.HasPrefix(arg, "-") { + flags = append(flags, arg) + + readFlagValue = !strings.Contains(arg, "=") + } else { + nonflags = append(nonflags, arg) } - } else { - flagArgs = args[firstFlagIndex:] } - return flagArgs, regularArgs + + return append(flags, nonflags...) } func translateShortOptions(flagArgs Args) []string { From 0671b166dcacb3dc1215ba65bf986dab194581dc Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Mon, 4 Dec 2017 09:23:40 -0800 Subject: [PATCH 044/215] Add tests for flag reordering --- command_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/command_test.go b/command_test.go index e69750a..c235e4a 100644 --- a/command_test.go +++ b/command_test.go @@ -243,3 +243,41 @@ func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { t.Fatal(err) } } + +func TestCommandFlagReordering(t *testing.T) { + cases := []struct { + testArgs []string + expectedValue string + expectedArgs []string + expectedErr error + }{ + {[]string{"some-exec", "some-command", "some-arg", "--flag", "foo"}, "foo", []string{"some-arg"}, nil}, + {[]string{"some-exec", "some-command", "some-arg", "--flag=foo"}, "foo", []string{"some-arg"}, nil}, + {[]string{"some-exec", "some-command", "--flag=foo", "some-arg"}, "foo", []string{"some-arg"}, nil}, + } + + for _, c := range cases { + value := "" + args := []string{} + app := &App{ + Commands: []Command{ + { + Name: "some-command", + Flags: []Flag{ + StringFlag{Name: "flag"}, + }, + Action: func(c *Context) { + fmt.Printf("%+v\n", c.String("flag")) + value = c.String("flag") + args = c.Args() + }, + }, + }, + } + + err := app.Run(c.testArgs) + expect(t, err, c.expectedErr) + expect(t, value, c.expectedValue) + expect(t, args, c.expectedArgs) + } +} From e38e4ae2d05acf5b5164c160a67fb7048e1358b0 Mon Sep 17 00:00:00 2001 From: Jesse Szwedko Date: Fri, 29 Dec 2017 13:38:18 -0500 Subject: [PATCH 045/215] Fix regression of SkipFlagParsing behavior Introduced by df562bf1a8626f2d16f91fcbf7230a5bdca3d592 Was mistakenly prepending the command name. --- command.go | 2 +- command_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 66a58b5..ed4a81a 100644 --- a/command.go +++ b/command.go @@ -178,7 +178,7 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { set.SetOutput(ioutil.Discard) if c.SkipFlagParsing { - return set, set.Parse(append([]string{c.Name, "--"}, args...)) + return set, set.Parse(append([]string{"--"}, args...)) } if c.UseShortOptionHandling { diff --git a/command_test.go b/command_test.go index c235e4a..c84b762 100644 --- a/command_test.go +++ b/command_test.go @@ -281,3 +281,39 @@ func TestCommandFlagReordering(t *testing.T) { expect(t, args, c.expectedArgs) } } + +func TestCommandSkipFlagParsing(t *testing.T) { + cases := []struct { + testArgs []string + expectedArgs []string + expectedErr error + }{ + {[]string{"some-exec", "some-command", "some-arg", "--flag", "foo"}, []string{"some-arg", "--flag", "foo"}, nil}, + {[]string{"some-exec", "some-command", "some-arg", "--flag=foo"}, []string{"some-arg", "--flag=foo"}, nil}, + } + + for _, c := range cases { + value := "" + args := []string{} + app := &App{ + Commands: []Command{ + { + SkipFlagParsing: true, + Name: "some-command", + Flags: []Flag{ + StringFlag{Name: "flag"}, + }, + Action: func(c *Context) { + fmt.Printf("%+v\n", c.String("flag")) + value = c.String("flag") + args = c.Args() + }, + }, + }, + } + + err := app.Run(c.testArgs) + expect(t, err, c.expectedErr) + expect(t, args, c.expectedArgs) + } +} From d7555e172994da8d058334aa1fe69533b1685924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Mengu=C3=A9?= Date: Fri, 26 Jan 2018 21:14:34 +0100 Subject: [PATCH 046/215] Fix unnecessary uses of Sprintf - use strconv directly - use concatenation for "%s%s" --- flag.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/flag.go b/flag.go index d4a4d41..b0cffc0 100644 --- a/flag.go +++ b/flag.go @@ -636,7 +636,7 @@ func withEnvHint(envVar, str string) string { suffix = "%" sep = "%, %" } - envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix) + envText = " [" + prefix + strings.Join(strings.Split(envVar, ","), sep) + suffix + "]" } return str + envText } @@ -709,13 +709,13 @@ func stringifyFlag(f Flag) string { placeholder = defaultPlaceholder } - usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString)) + usageWithDefault := strings.TrimSpace(usage + defaultValueString) return FlagFileHinter( fv.FieldByName("FilePath").String(), FlagEnvHinter( fv.FieldByName("EnvVar").String(), - fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault), + FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder)+"\t"+usageWithDefault, ), ) } @@ -724,7 +724,7 @@ func stringifyIntSliceFlag(f IntSliceFlag) string { defaultVals := []string{} if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) + defaultVals = append(defaultVals, strconv.Itoa(i)) } } @@ -735,7 +735,7 @@ func stringifyInt64SliceFlag(f Int64SliceFlag) string { defaultVals := []string{} if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { - defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) + defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) } } @@ -747,7 +747,7 @@ func stringifyStringSliceFlag(f StringSliceFlag) string { if f.Value != nil && len(f.Value.Value()) > 0 { for _, s := range f.Value.Value() { if len(s) > 0 { - defaultVals = append(defaultVals, fmt.Sprintf("%q", s)) + defaultVals = append(defaultVals, strconv.Quote(s)) } } } @@ -766,8 +766,8 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string { defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", ")) } - usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) - return fmt.Sprintf("%s\t%s", FlagNamePrefixer(name, placeholder), usageWithDefault) + usageWithDefault := strings.TrimSpace(usage + defaultVal) + return FlagNamePrefixer(name, placeholder) + "\t" + usageWithDefault } func flagFromFileEnv(filePath, envName string) (val string, ok bool) { From 3a87b13b01ac8628694f1e1b20bdb452cc0f54d2 Mon Sep 17 00:00:00 2001 From: Nico Windler Date: Sat, 10 Feb 2018 13:35:23 +0100 Subject: [PATCH 047/215] Fix args reordering when bool flags are present --- app_test.go | 33 +++++++++++++++++++++++++++++++++ command.go | 3 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app_test.go b/app_test.go index 5db9882..42fb149 100644 --- a/app_test.go +++ b/app_test.go @@ -329,6 +329,39 @@ func TestApp_CommandWithArgBeforeFlags(t *testing.T) { expect(t, firstArg, "my-arg") } +func TestApp_CommandWithArgBeforeBoolFlags(t *testing.T) { + var parsedOption, parsedSecondOption, firstArg string + var parsedBool, parsedSecondBool bool + + app := NewApp() + command := Command{ + Name: "cmd", + Flags: []Flag{ + StringFlag{Name: "option", Value: "", Usage: "some option"}, + StringFlag{Name: "secondOption", Value: "", Usage: "another option"}, + BoolFlag{Name: "boolflag", Usage: "some bool"}, + BoolFlag{Name: "b", Usage: "another bool"}, + }, + Action: func(c *Context) error { + parsedOption = c.String("option") + parsedSecondOption = c.String("secondOption") + parsedBool = c.Bool("boolflag") + parsedSecondBool = c.Bool("b") + firstArg = c.Args().First() + return nil + }, + } + app.Commands = []Command{command} + + app.Run([]string{"", "cmd", "my-arg", "--boolflag", "--option", "my-option", "-b", "--secondOption", "fancy-option"}) + + expect(t, parsedOption, "my-option") + expect(t, parsedSecondOption, "fancy-option") + expect(t, parsedBool, true) + expect(t, parsedSecondBool, true) + expect(t, firstArg, "my-arg") +} + func TestApp_RunAsSubcommandParseFlags(t *testing.T) { var context *Context diff --git a/command.go b/command.go index ed4a81a..2acb976 100644 --- a/command.go +++ b/command.go @@ -213,11 +213,12 @@ func reorderArgs(args []string) []string { break } - if readFlagValue { + if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") { readFlagValue = false flags = append(flags, arg) continue } + readFlagValue = false if arg != "-" && strings.HasPrefix(arg, "-") { flags = append(flags, arg) From 45289ea7a0de564a71532e13b9916961a38abc8e Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Tue, 20 Feb 2018 12:40:43 -0500 Subject: [PATCH 048/215] Adjust contribution and maintainer prose per current reality --- CODE_OF_CONDUCT.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 19 ++++++++++++ MAINTAINERS.md | 1 + README.md | 20 +++---------- 4 files changed, 98 insertions(+), 16 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 MAINTAINERS.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..41ba294 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting Dan Buch at dan@meatballhat.com. All complaints will be +reviewed and investigated and will result in a response that is deemed necessary +and appropriate to the circumstances. The project team is obligated to maintain +confidentiality with regard to the reporter of an incident. Further details of +specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..329195e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +## Contributing + +**NOTE**: the primary maintainer(s) may be found in +[./MAINTAINERS.md](./MAINTAINERS.md). + +Feel free to put up a pull request to fix a bug or maybe add a feature. I will +give it a code review and make sure that it does not break backwards +compatibility. If I or any other collaborators agree that it is in line with +the vision of the project, we will work with you to get the code into +a mergeable state and merge it into the master branch. + +If you have contributed something significant to the project, we will most +likely add you as a collaborator. As a collaborator you are given the ability +to merge others pull requests. It is very important that new code does not +break existing code, so be careful about what code you do choose to merge. + +If you feel like you have contributed to the project but have not yet been added +as a collaborator, we probably forgot to add you :sweat_smile:. Please open an +issue! diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000..f6bdd99 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1 @@ +- @meatballhat diff --git a/README.md b/README.md index 6096701..7750de7 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ cli [![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) / [![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc) -**Notice:** This is the library formerly known as -`github.com/codegangsta/cli` -- Github will automatically redirect requests -to this repository, but we recommend updating your references for clarity. +This is the library formerly known as `github.com/codegangsta/cli` -- Github +will automatically redirect requests to this repository, but we recommend +updating your references for clarity. cli is a simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line @@ -1433,16 +1433,4 @@ with two leading dashes (such as **--options**) are still valid. ## Contribution Guidelines -Feel free to put up a pull request to fix a bug or maybe add a feature. I will -give it a code review and make sure that it does not break backwards -compatibility. If I or any other collaborators agree that it is in line with -the vision of the project, we will work with you to get the code into -a mergeable state and merge it into the master branch. - -If you have contributed something significant to the project, we will most -likely add you as a collaborator. As a collaborator you are given the ability -to merge others pull requests. It is very important that new code does not -break existing code, so be careful about what code you do choose to merge. - -If you feel like you have contributed to the project but have not yet been -added as a collaborator, we probably forgot to add you, please open an issue. +See [./CONTRIBUTING.md](./CONTRIBUTING.md) From c23dfba7018a4666892af705d89150a5f1ac8293 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Thu, 28 Jun 2018 16:41:02 +0200 Subject: [PATCH 049/215] short opt handling: fix parsing Only split a given string (e.g., "-abc") into short options (e.g., "-a", "-b", "-c") if all those are flags. To further avoid mistakenly transform common arguments, catch "flag provided but not defined" errors to iteratively transform short options. Signed-off-by: Valentin Rothberg Fixes: https://github.com/projectatomic/libpod/issues/714 --- command.go | 57 ++++++++++++++++++++++++++++++++++++++++++++----- command_test.go | 46 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/command.go b/command.go index 2acb976..56b633c 100644 --- a/command.go +++ b/command.go @@ -181,16 +181,49 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { return set, set.Parse(append([]string{"--"}, args...)) } - if c.UseShortOptionHandling { - args = translateShortOptions(args) - } - if !c.SkipArgReorder { args = reorderArgs(args) } +PARSE: err = set.Parse(args) if err != nil { + if c.UseShortOptionHandling { + // To enable short-option handling (e.g., "-it" vs "-i -t") + // we have to iteratively catch parsing errors. This way + // we achieve LR parsing without transforming any arguments. + // Otherwise, there is no way we can discriminate combined + // short options from common arguments that should be left + // untouched. + errStr := err.Error() + trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ") + if errStr == trimmed { + return nil, err + } + // regenerate the initial args with the split short opts + newArgs := Args{} + for i, arg := range args { + if arg != trimmed { + newArgs = append(newArgs, arg) + continue + } + shortOpts := translateShortOptions(set, Args{trimmed}) + if len(shortOpts) == 1 { + return nil, err + } + // add each short option and all remaining arguments + newArgs = append(newArgs, shortOpts...) + newArgs = append(newArgs, args[i+1:]...) + args = newArgs + // now reset the flagset parse again + set, err = flagSet(c.Name, c.Flags) + if err != nil { + return nil, err + } + set.SetOutput(ioutil.Discard) + goto PARSE + } + } return nil, err } @@ -232,11 +265,25 @@ func reorderArgs(args []string) []string { return append(flags, nonflags...) } -func translateShortOptions(flagArgs Args) []string { +func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { + allCharsFlags := func (s string) bool { + for i := range s { + f := set.Lookup(string(s[i])) + if f == nil { + return false + } + } + return true + } + // separate combined flags var flagArgsSeparated []string for _, flagArg := range flagArgs { if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 { + if !allCharsFlags(flagArg[1:]) { + flagArgsSeparated = append(flagArgsSeparated, flagArg) + continue + } for _, flagChar := range flagArg[1:] { flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar)) } diff --git a/command_test.go b/command_test.go index c84b762..9388f6a 100644 --- a/command_test.go +++ b/command_test.go @@ -57,6 +57,52 @@ func TestCommandFlagParsing(t *testing.T) { } } +func TestParseAndRunShortOpts(t *testing.T) { + cases := []struct { + testArgs []string + expectedErr error + expectedArgs []string + }{ + {[]string{"foo", "test", "-a"}, nil, []string{}}, + {[]string{"foo", "test", "-c", "arg1", "arg2"}, nil, []string{"arg1", "arg2"}}, + {[]string{"foo", "test", "-f"}, nil, []string{}}, + {[]string{"foo", "test", "-ac", "--fgh"}, nil, []string{}}, + {[]string{"foo", "test", "-af"}, nil, []string{}}, + {[]string{"foo", "test", "-cf"}, nil, []string{}}, + {[]string{"foo", "test", "-acf"}, nil, []string{}}, + {[]string{"foo", "test", "-invalid"}, errors.New("flag provided but not defined: -invalid"), []string{}}, + {[]string{"foo", "test", "-acf", "arg1", "-invalid"}, nil, []string{"arg1" ,"-invalid"}}, + } + + var args []string + cmd := Command{ + Name: "test", + Usage: "this is for testing", + Description: "testing", + Action: func(c *Context) error { + args = c.Args() + return nil + }, + SkipArgReorder: true, + UseShortOptionHandling: true, + Flags: []Flag{ + BoolFlag{Name: "abc, a"}, + BoolFlag{Name: "cde, c"}, + BoolFlag{Name: "fgh, f"}, + }, + } + + for _, c := range cases { + app := NewApp() + app.Commands = []Command{cmd} + + err := app.Run(c.testArgs) + + expect(t, err, c.expectedErr) + expect(t, args, c.expectedArgs) + } +} + func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() app.Commands = []Command{ From 3e5a935ed3cafadcddc6f5ab2fe7ddd2aa0c3cea Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Tue, 21 Aug 2018 08:33:42 +0200 Subject: [PATCH 050/215] fix `go vet` warning command_test.go:342:3 value declared but not used Signed-off-by: Valentin Rothberg --- command_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/command_test.go b/command_test.go index 9388f6a..8c2650e 100644 --- a/command_test.go +++ b/command_test.go @@ -339,7 +339,6 @@ func TestCommandSkipFlagParsing(t *testing.T) { } for _, c := range cases { - value := "" args := []string{} app := &App{ Commands: []Command{ @@ -351,7 +350,6 @@ func TestCommandSkipFlagParsing(t *testing.T) { }, Action: func(c *Context) { fmt.Printf("%+v\n", c.String("flag")) - value = c.String("flag") args = c.Args() }, }, From d7c3be82673f869fed4ea77a0c5e3f13bd65ba89 Mon Sep 17 00:00:00 2001 From: Agis Anastasopoulos <827224+agis@users.noreply.github.com> Date: Tue, 21 Aug 2018 11:19:37 +0300 Subject: [PATCH 051/215] Fix README typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2baef4..6eb2996 100644 --- a/README.md +++ b/README.md @@ -670,7 +670,7 @@ func main() { ``` Note that default values set from file (e.g. `FilePath`) take precedence over -default values set from the enviornment (e.g. `EnvVar`). +default values set from the environment (e.g. `EnvVar`). #### Values from alternate input sources (YAML, TOML, and others) From 21dfc6eb8302c4db3547a22b0843c43e36fe058e Mon Sep 17 00:00:00 2001 From: teresy Date: Wed, 10 Oct 2018 14:54:48 -0400 Subject: [PATCH 052/215] Remove redundant nil checks --- help.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/help.go b/help.go index 65874fa..d4c183b 100644 --- a/help.go +++ b/help.go @@ -237,10 +237,8 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc m funcMap := template.FuncMap{ "join": strings.Join, } - if customFunc != nil { - for key, value := range customFunc { - funcMap[key] = value - } + for key, value := range customFunc { + funcMap[key] = value } w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) From 3e145076abdbaf5c6e47e311b5e659251604a49b Mon Sep 17 00:00:00 2001 From: Christopher Waldon Date: Fri, 12 Oct 2018 11:30:46 -0400 Subject: [PATCH 053/215] Clarify that altsrc supports both TOML and JSON --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f2baef4..e2df4ec 100644 --- a/README.md +++ b/README.md @@ -679,6 +679,7 @@ from other file input sources. Currently supported input source formats: * YAML +* JSON * TOML In order to get values for a flag from an alternate input source the following @@ -701,7 +702,7 @@ 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 for this code snipped to work. -Currently only YAML and JSON files are supported but developers can add support +Currently only YAML, JSON, and TOML files are supported but developers can add support for other input sources by implementing the altsrc.InputSourceContext for their given sources. From 9587fc27bd923141975eac8c34288bcf8de5cca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Thu, 18 Oct 2018 20:56:13 -0400 Subject: [PATCH 054/215] Correct typo --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index 56b633c..3d44404 100644 --- a/command.go +++ b/command.go @@ -57,7 +57,7 @@ type Command struct { // Boolean to hide this command from help or completion Hidden bool // Boolean to enable short-option handling so user can combine several - // single-character bool arguements into one + // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool From 769f6d543bd3c9b36b98e3a46ad646cf63769120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Thu, 18 Oct 2018 21:00:02 -0400 Subject: [PATCH 055/215] Bring Go version current --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cf8d098..8bb0e9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: go sudo: false dist: trusty osx_image: xcode8.3 -go: 1.8.x +go: 1.11.x os: - linux From d63733fe14aad10beca5490a453904bc1d67fe16 Mon Sep 17 00:00:00 2001 From: Ben Zvan Date: Wed, 26 Dec 2018 12:41:27 -0600 Subject: [PATCH 056/215] adds test coverage to context --- context_test.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/context_test.go b/context_test.go index 7acca10..545dc1b 100644 --- a/context_test.go +++ b/context_test.go @@ -253,18 +253,24 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) { timeoutIsSet, tIsSet bool noEnvVarIsSet, nIsSet bool passwordIsSet, pIsSet bool + passwordValue string unparsableIsSet, uIsSet bool + overrideIsSet, oIsSet bool + overrideValue string + ) clearenv() os.Setenv("APP_TIMEOUT_SECONDS", "15.5") - os.Setenv("APP_PASSWORD", "") + os.Setenv("APP_PASSWORD", "badpass") + os.Setenv("APP_OVERRIDE", "overridden") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"}, Float64Flag{Name: "no-env-var, n"}, Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"}, + StringFlag{Name: "overrides-default, o", Value: "default", EnvVar: "APP_OVERRIDE"}, }, Commands: []Command{ { @@ -274,10 +280,14 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) { tIsSet = ctx.GlobalIsSet("t") passwordIsSet = ctx.GlobalIsSet("password") pIsSet = ctx.GlobalIsSet("p") + passwordValue = ctx.GlobalString("password") unparsableIsSet = ctx.GlobalIsSet("unparsable") uIsSet = ctx.GlobalIsSet("u") noEnvVarIsSet = ctx.GlobalIsSet("no-env-var") nIsSet = ctx.GlobalIsSet("n") + overrideIsSet = ctx.GlobalIsSet("overrides-default") + oIsSet = ctx.GlobalIsSet("o") + overrideValue = ctx.GlobalString("overrides-default") return nil }, }, @@ -290,8 +300,13 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) { expect(t, tIsSet, true) expect(t, passwordIsSet, true) expect(t, pIsSet, true) + expect(t, passwordValue, "badpass") + expect(t, unparsableIsSet, false) expect(t, noEnvVarIsSet, false) expect(t, nIsSet, false) + expect(t, overrideIsSet, true) + expect(t, oIsSet, true) + expect(t, overrideValue, "overridden") os.Setenv("APP_UNPARSABLE", "foobar") if err := a.Run([]string{"run"}); err != nil { From 4a76377775cebfc3dca4af752ba2837f9694b9d8 Mon Sep 17 00:00:00 2001 From: Ben Zvan Date: Wed, 26 Dec 2018 12:48:12 -0600 Subject: [PATCH 057/215] go fmt --- context_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/context_test.go b/context_test.go index 545dc1b..c3aaeb8 100644 --- a/context_test.go +++ b/context_test.go @@ -253,11 +253,10 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) { timeoutIsSet, tIsSet bool noEnvVarIsSet, nIsSet bool passwordIsSet, pIsSet bool - passwordValue string + passwordValue string unparsableIsSet, uIsSet bool - overrideIsSet, oIsSet bool + overrideIsSet, oIsSet bool overrideValue string - ) clearenv() From 5b83c895a70b7714548f0aa4f43deb3fa5fc1601 Mon Sep 17 00:00:00 2001 From: "Iskander (Alex) Sharipov" Date: Tue, 29 Jan 2019 22:51:02 +0300 Subject: [PATCH 058/215] use type switch instead of if/else This reduces the syntax noise of the code by removing excessive type assertions. Signed-off-by: Iskander Sharipov --- app.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index 9add067..b54e1ab 100644 --- a/app.go +++ b/app.go @@ -495,11 +495,12 @@ 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.(ActionFunc); ok { + switch a := action.(type) { + case ActionFunc: return a(context) - } else if a, ok := action.(func(*Context) error); ok { + case func(*Context) error: return a(context) - } else if a, ok := action.(func(*Context)); ok { // deprecated function signature + case func(*Context): // deprecated function signature a(context) return nil } From a0453b2200cafa97ce263a4a5df87f5087d2abda Mon Sep 17 00:00:00 2001 From: mingrammer Date: Wed, 6 Mar 2019 23:51:22 +0900 Subject: [PATCH 059/215] Fix the unaligned indents for the commands that have no categories --- app_test.go | 6 +++--- help.go | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app_test.go b/app_test.go index 629681e..e88b03d 100644 --- a/app_test.go +++ b/app_test.go @@ -136,8 +136,8 @@ func ExampleApp_Run_appHelp() { // Oliver Allen // // COMMANDS: - // describeit, d use it to see a description - // help, h Shows a list of commands or help for one command + // 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") @@ -190,7 +190,7 @@ func ExampleApp_Run_noAction() { // [global options] command [command options] [arguments...] // // COMMANDS: - // help, h Shows a list of commands or help for one command + // help, h Shows a list of commands or help for one command // // GLOBAL OPTIONS: // --help, -h show help diff --git a/help.go b/help.go index 65874fa..126cc7c 100644 --- a/help.go +++ b/help.go @@ -30,8 +30,9 @@ AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{end}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range $index, $option := .VisibleFlags}}{{if $index}} @@ -71,9 +72,11 @@ USAGE: {{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}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} -{{end}}{{if .VisibleFlags}} + + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} From ddc3453179ea450663473db4689f7c256225a72b Mon Sep 17 00:00:00 2001 From: mingrammer Date: Thu, 7 Mar 2019 00:04:18 +0900 Subject: [PATCH 060/215] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6eb2996..9904334 100644 --- a/README.md +++ b/README.md @@ -874,7 +874,7 @@ Will include: ``` COMMANDS: - noop + noop Template actions: add From 58a072d5733d4bb2dc61ffbc3557ec9592e34adc Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Wed, 20 Mar 2019 20:28:51 +0530 Subject: [PATCH 061/215] Add bash completion support for flags --- autocomplete/bash_autocomplete | 9 +++- help.go | 79 ++++++++++++++++++++++++++++++---- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index 37d9c14..d9305db 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -6,7 +6,14 @@ _cli_bash_autocomplete() { local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + if [[ $cur == -* ]]; then + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) + else + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + fi + if [[ "$opts1" == "$cur1" ]]; then + return 0 + fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 } diff --git a/help.go b/help.go index 65874fa..be79e88 100644 --- a/help.go +++ b/help.go @@ -4,9 +4,11 @@ import ( "fmt" "io" "os" + "regexp" "strings" "text/tabwriter" "text/template" + "unicode/utf8" ) // AppHelpTemplate is the text template for the Default help topic. @@ -152,19 +154,80 @@ func ShowAppHelp(c *Context) (err error) { return nil } +var shortFlagRegex = regexp.MustCompile(`^-`) + // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { - for _, command := range c.App.Commands { - if command.Hidden { - continue + DefaultAppCompleteWithFlags(nil)(c) +} + +func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { + return func(c *Context) { + if len(os.Args) > 2 { + lastArg := os.Args[len(os.Args)-2] + if strings.HasPrefix(lastArg, "-") { + lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") + lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") + for _, flag := range c.App.Flags { + for _, name := range strings.Split(flag.GetName(), ",") { + name = strings.Trim(name, " ") + if strings.HasPrefix(name, lastArg) && lastArg != name { + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + } + } + } + if cmd != nil { + for _, flag := range cmd.Flags { + for _, name := range strings.Split(flag.GetName(), ",") { + name = strings.Trim(name, " ") + if strings.HasPrefix(name, lastArg) && lastArg != name { + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + } + } + } + } + return + } } - if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + if cmd != nil { + for _, command := range cmd.Subcommands { + if command.Hidden { + continue + } + if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + } + } else { + for _, name := range command.Names() { + if name != "h" { + fmt.Fprintf(c.App.Writer, "%s\n", name) + } + } + } } } else { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s\n", name) + for _, command := range c.App.Commands { + if command.Hidden { + continue + } + if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + } + } else { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s\n", name) + } + } } } } From fb1421d9031313c5e0f3c4a92625ed9cf5739b0d Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Wed, 20 Mar 2019 21:34:56 +0530 Subject: [PATCH 062/215] Fix duplicate completion of existing flag --- autocomplete/bash_autocomplete | 3 --- help.go | 42 ++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index d9305db..303d126 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -11,9 +11,6 @@ _cli_bash_autocomplete() { else opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) fi - if [[ "$opts1" == "$cur1" ]]; then - return 0 - fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 } diff --git a/help.go b/help.go index be79e88..bd68295 100644 --- a/help.go +++ b/help.go @@ -162,6 +162,22 @@ func DefaultAppComplete(c *Context) { } func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { + cliArgContains := func(flagName string) bool { + for _, name := range strings.Split(flagName, ",") { + name = strings.Trim(name, " ") + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + for _, a := range os.Args { + if a == flag { + return true + } + } + } + return false + } return func(c *Context) { if len(os.Args) > 2 { lastArg := os.Args[len(os.Args)-2] @@ -171,12 +187,13 @@ func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { for _, flag := range c.App.Flags { for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") - if strings.HasPrefix(name, lastArg) && lastArg != name { - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { + fmt.Fprintln(c.App.Writer, flagCompletion) } } } @@ -184,12 +201,13 @@ func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { for _, flag := range cmd.Flags { for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") - if strings.HasPrefix(name, lastArg) && lastArg != name { - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { + fmt.Fprintln(c.App.Writer, flagCompletion) } } } From 1d7a2b08d6f8e9764e2f2b911b1bb9fa49596f92 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Thu, 21 Mar 2019 13:01:48 +0530 Subject: [PATCH 063/215] Add default completion on commands, test cases, refactor code --- app_test.go | 54 ++++++++++++++++++++++ help.go | 130 ++++++++++++++++++++++++---------------------------- 2 files changed, 114 insertions(+), 70 deletions(-) diff --git a/app_test.go b/app_test.go index 629681e..e7841e6 100644 --- a/app_test.go +++ b/app_test.go @@ -221,6 +221,60 @@ func ExampleApp_Run_subcommandNoAction() { } +func ExampleApp_Run_bashComplete_withShortFlag() { + os.Args = []string{"greet", "-", "--generate-bash-completion"} + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Flags = []Flag{ + IntFlag{ + Name: "other,o", + }, + StringFlag{ + Name: "xyz,x", + }, + } + + app.Run(os.Args) + // Output: + // --other + // -o + // --xyz + // -x + // --help + // -h + // --version + // -v +} + +func ExampleApp_Run_bashComplete_withLongFlag() { + os.Args = []string{"greet", "--s", "--generate-bash-completion"} + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Flags = []Flag{ + IntFlag{ + Name: "other,o", + }, + StringFlag{ + Name: "xyz,x", + }, + StringFlag{ + Name: "some-flag,s", + }, + StringFlag{ + Name: "similar-flag", + }, + } + + app.Run(os.Args) + // Output: + // --some-flag + // --similar-flag +} + func ExampleApp_Run_bashComplete() { // set args for examples sake os.Args = []string{"greet", "--generate-bash-completion"} diff --git a/help.go b/help.go index bd68295..c20f5a4 100644 --- a/help.go +++ b/help.go @@ -158,95 +158,80 @@ var shortFlagRegex = regexp.MustCompile(`^-`) // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { - DefaultAppCompleteWithFlags(nil)(c) + DefaultCompleteWithFlags(nil)(c) } -func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { - cliArgContains := func(flagName string) bool { - for _, name := range strings.Split(flagName, ",") { +func printCommandSuggestions(commands []Command, writer io.Writer) { + for _, command := range commands { + if command.Hidden { + continue + } + if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { + for _, name := range command.Names() { + fmt.Fprintf(writer, "%s:%s\n", name, command.Usage) + } + } else { + for _, name := range command.Names() { + fmt.Fprintf(writer, "%s\n", name) + } + } + } +} + +func cliArgContains(flagName string) bool { + for _, name := range strings.Split(flagName, ",") { + name = strings.Trim(name, " ") + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + for _, a := range os.Args { + if a == flag { + return true + } + } + } + return false +} + +func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { + cur := shortFlagRegex.ReplaceAllString(lastArg, "") + cur = shortFlagRegex.ReplaceAllString(cur, "") + for _, flag := range flags { + for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") count := utf8.RuneCountInString(name) if count > 2 { count = 2 } - flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - for _, a := range os.Args { - if a == flag { - return true - } + if strings.HasPrefix(lastArg, "--") && count == 1 { + continue + } + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(flag.GetName()) { + fmt.Fprintln(writer, flagCompletion) } } - return false } +} + +func DefaultCompleteWithFlags(cmd *Command) func(c *Context) { return func(c *Context) { if len(os.Args) > 2 { lastArg := os.Args[len(os.Args)-2] if strings.HasPrefix(lastArg, "-") { - lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") - lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") - for _, flag := range c.App.Flags { - for _, name := range strings.Split(flag.GetName(), ",") { - name = strings.Trim(name, " ") - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { - fmt.Fprintln(c.App.Writer, flagCompletion) - } - } - } + printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer) if cmd != nil { - for _, flag := range cmd.Flags { - for _, name := range strings.Split(flag.GetName(), ",") { - name = strings.Trim(name, " ") - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { - fmt.Fprintln(c.App.Writer, flagCompletion) - } - } - } + printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer) } return } } if cmd != nil { - for _, command := range cmd.Subcommands { - if command.Hidden { - continue - } - if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) - } - } else { - for _, name := range command.Names() { - if name != "h" { - fmt.Fprintf(c.App.Writer, "%s\n", name) - } - } - } - } + printCommandSuggestions(cmd.Subcommands, c.App.Writer) } else { - for _, command := range c.App.Commands { - if command.Hidden { - continue - } - if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) - } - } else { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s\n", name) - } - } - } + printCommandSuggestions(c.App.Commands, c.App.Writer) } } } @@ -309,9 +294,14 @@ func ShowCompletions(c *Context) { // ShowCommandCompletions prints the custom completions for a given command func ShowCommandCompletions(ctx *Context, command string) { c := ctx.App.Command(command) - if c != nil && c.BashComplete != nil { - c.BashComplete(ctx) + if c != nil { + if c.BashComplete != nil { + c.BashComplete(ctx) + } else { + DefaultCompleteWithFlags(c)(ctx) + } } + } func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { From 62f02f21ef0b5c3c0aa67d3240aee15bc8a53457 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Thu, 11 Apr 2019 10:57:58 +0530 Subject: [PATCH 064/215] Don't complete hidden flags --- help.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/help.go b/help.go index c20f5a4..72c59b1 100644 --- a/help.go +++ b/help.go @@ -199,6 +199,9 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { cur := shortFlagRegex.ReplaceAllString(lastArg, "") cur = shortFlagRegex.ReplaceAllString(cur, "") for _, flag := range flags { + if bflag, ok := flag.(BoolFlag); ok && bflag.Hidden { + continue + } for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") count := utf8.RuneCountInString(name) From 83b99c4109dce6ac7a6b5d2048e26f1ad60ef3f3 Mon Sep 17 00:00:00 2001 From: Ben Zvan Date: Tue, 25 Jun 2019 18:47:58 -0500 Subject: [PATCH 065/215] Created using Colaboratory --- ..._Intro_to_Visualizations_with_Python.ipynb | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 Attendee_Intro_to_Visualizations_with_Python.ipynb diff --git a/Attendee_Intro_to_Visualizations_with_Python.ipynb b/Attendee_Intro_to_Visualizations_with_Python.ipynb new file mode 100644 index 0000000..3cf5c98 --- /dev/null +++ b/Attendee_Intro_to_Visualizations_with_Python.ipynb @@ -0,0 +1,219 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "Copy of Attendee Intro to Visualizations with Python.ipynb", + "version": "0.3.2", + "provenance": [], + "collapsed_sections": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cpwsxUdOthZh", + "colab_type": "text" + }, + "source": [ + "Import the California Housing dataset. \n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "fuynGZ8jDsca", + "colab_type": "code", + "colab": {} + }, + "source": [ + "DATASETS_URL = \"https://github.com/ageron/handson-ml/raw/master/datasets\"\n", + "import os\n", + "import tarfile\n", + "from six.moves import urllib\n", + "\n", + "\n", + "HOUSING_PATH = \"datasets/housing\"\n", + "HOUSING_URL = DATASETS_URL + \"/housing/housing.tgz\"\n", + "\n", + "def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):\n", + " if not os.path.exists(housing_path):\n", + " os.makedirs(housing_path)\n", + " tgz_path = os.path.join(housing_path, \"housing.tgz\")\n", + " urllib.request.urlretrieve(housing_url, tgz_path)\n", + " housing_tgz = tarfile.open(tgz_path)\n", + " housing_tgz.extractall(path=housing_path)\n", + " housing_tgz.close()\n", + " \n", + "fetch_housing_data()\n", + "import pandas as pd\n", + "\n", + "def load_housing_data(housing_path=HOUSING_PATH):\n", + " csv_path = os.path.join(housing_path, \"housing.csv\")\n", + " return pd.read_csv(csv_path)\n", + "housing = load_housing_data()\n" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "WosdC2Qq1t2e", + "colab_type": "code", + "outputId": "af80b7d0-6c88-4b82-9907-169b14bf0fc0", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 102 + } + }, + "source": [ + "h_ocean = 9136/20640\n", + "inland = 6551/20640\n", + "n_ocean = 2658/20640\n", + "n_bay = 2290/20640\n", + "island = 5/20640\n", + "\n", + "\n", + "print(h_ocean)\n", + "print(inland)\n", + "print(n_ocean)\n", + "print(n_bay)\n", + "print(island)" + ], + "execution_count": 0, + "outputs": [ + { + "output_type": "stream", + "text": [ + "0.44263565891472867\n", + "0.31739341085271316\n", + "0.12877906976744186\n", + "0.11094961240310078\n", + "0.00024224806201550387\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "hNfIJI1Y1TRY", + "colab_type": "code", + "outputId": "84cc951a-a7a4-49be-8e8e-c2d43beaae84", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 406 + } + }, + "source": [ + "# Pie chart\n", + "labels = ['<1H OCEAN', 'INLAND', 'NEAR OCEAN', 'NEAR BAY', 'ISLAND']\n", + "sizes = [44, 32, 13, 11, 0.2]\n", + "#colors\n", + "colors = ['#66b3ff','#A5D6A7', '#D7CCC8', '#FDD835', '#263238']\n", + " \n", + "fig1, ax1 = plt.subplots()\n", + "patches, texts, autotexts = ax1.pie(sizes, colors = colors, labels=labels, autopct='%1.1f%%', startangle=90)\n", + "for text in texts:\n", + " text.set_color('black')\n", + "for autotext in autotexts:\n", + " autotext.set_color('black')\n", + "# Equal aspect ratio ensures that pie is drawn as a circle\n", + "ax1.axis('equal') \n", + "plt.tight_layout()\n", + "plt.legend(loc = 'upper left')\n", + "plt.show()" + ], + "execution_count": 0, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P1i1ZlpF8w_G", + "colab_type": "text" + }, + "source": [ + "Let's move on to another type of plot that is useful for showing correlations. We would expect to see a correlation between the income and the house value, as more income would most likely mean a nicer home." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "TQ3EUT1q1s0B", + "colab_type": "code", + "outputId": "ff85fdb6-9259-414e-b591-8885f623e533", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 701 + } + }, + "source": [ + "# Set up the matplotlib figure\n", + "f, ax = plt.subplots(figsize=(12, 10))\n", + "plt.title('Pearson Correlation of Housing Features')\n", + "\n", + "# Draw the heatmap using seaborn\n", + "sns.heatmap(housing.corr(),linewidths=0.25, square=True, cmap = \"BrBG\", linecolor='black', annot=True)" + ], + "execution_count": 0, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 45 + }, + { + "output_type": "display_data", + "data": { + "image/png": "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\ny3zq1q2r6ZI+Lqe2bdtiY2ODqakpfn5+Wjn5+vpiZWWFjY0NXl5e3LhxQ2cfT3ruHiUvL09nzfWK\nFSu0cuvUqRNmZmZYWVnRqlWrJ+7X0dGREydOcOjQIfLz8xk7dizVqlXTiWvQoAEODg6YmZnx5ptv\ncuPGDS5fvkxOTg4NGjQACj4ZyM/Pf+SxpkyZopV769atgYKx6tKlCxYWFtjY2NCmTRvNWG3YsEHz\nZqtOnTpPHJ+i3L9/X7PkJj4+HmdnZz788EMAWrZsSUJCAtevX6d06dIcOnSIuLg4LCwsmDp1Kk5O\nTs98PCHEy0vWjAshnln37t0xNTVFrVbj7u7OwoULKVGiBOnp6cTFxeHr66uJtbW1JSUlhfT0dEJD\nQ7l06RIKhYKbN2/i5+enibO3t9f8e+LEicydO5fevXtjZWXF4MGD8fX1JTk5GTs7O02cnZ2dVhex\nZMmSAJiamgLoTNLs7e1RKBTcunULd3f3ImtLTk7WmgzZ2dlpTeoL5/nP2+np6SxatIg1a9YABRPZ\nf66jz8vLY+bMmZo3KZmZmVSoUKHIXArnVPg49vb2WjnZ2tpq/m1qalrk0pgnPXeP8ncnuLBZs2Zp\nlp88KbeiNG/enNTUVGbMmMGlS5do3bp1kV/c/Hs8C9eVmpqqVceTJq6PWjOenp7OpEmTmDp1KlCw\nbOWtt94CYPPmzSxbtozMzEzy8/OLXPbzJKampppxSUtLIzExUeu8sLCwIDk5mV69emnekNy+fZuu\nXbvy1VdfYWJi8szHFEK8nGQyLoR4ZoW/wFmYk5MTH3zwATNnztTZNmzYMGrUqMHs2bMxNTXF39//\nkfsvU6YMwcHBBAcHc+jQIb766iu8vb0pU6YMKSkpuLm5AQVLAcqUKfPUeVtbW/PWW2+xc+dOevfu\nrbVtyZIlfPTRR5pj/O1ZjuHk5MRHH32kWSdclG3btvHzzz+zYsUKHB0diYyMZPPmzY/d77/J6Z/7\neN7n7nlyUygUmjcGqampWo/z9/fH39+fW7du8dVXX7FhwwbMzJ78K8nW1lazXh547iujODk50adP\nHxo1aqR1/61btwgKCmLt2rVUq1aNy5cv4+PjU+Q+FAqF1hu+f9ZY+FgVK1YkOjq6yO39+/enf//+\n/PXXX3z66afUqVNH00UXQrz6ZJmKEOKFqVevHnFxcZqP9X/77TcmTJgAFKyVrlatGqamphw+fJgr\n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\nsHug/QC2j3gv7GxtcbC3p06tahQN0FUs3u/cns/7Gp4x2b7zFzp2aPsi0nkqYTtOsGmHrupnYaHE\n3cVe/1hGZjZaLdjaFPz56vh6dd5pXY29f17gsxHrWDmrGypr3dfEN2v2c/hkFDOGt3/k800pTaPB\n5qFO3E6lIvWBji4+OYlOk0YzrWcfrCwt+W7nVtSZGSSl512k+cHUcaisrJn60aesGjSCzpMNL/4S\nxpem0aCy+ud96+Pqxnef9qXOsIGcjomiUcXKbPx6OGX7fsxHTZtTxNEZtx6dyczOYn7PPszu3pvP\nv19k7FQeK02jxuahAZudjQ2pDww645OS6DQmmGmf9tV9jn/+CXWGhqTUVL5s/y5FXFxxbduUzOxs\n5vcbxOwvvuLz2VOMnQpr165l3br1wP3vYQ93/WMZGRn3v4ftDJ5ja1tQf6PBzs4uX1+k66sMv8e3\nb99Bx475rwe4desWkZGR1K1b91/n9ax07Tc80Mp4KIdH9UO2dnYULV6cz/v316/v0bs3bZo1R61W\n53sfxPNx9+5dAgMD9ctubm7cuXMHBwcH7ty5g5ubm8FjMTExz/Q6ZlMZ79mzJ7Vr1wYgKCiIlStX\nEhwczKRJk2jXrh1FihRhyZIlaLVa/Pz8CA0NZc2aNcyZM+eJX8PDw4OVK1fSrl07Vq5cyZEjR4iL\ni2P16tWsWLGCRYsWodFoWLhwIV988QXLly9H+cCpwHPnzjF9+nSaNGnCqFGjmDVrFqtWrcLZ2Zkt\nW7bw888/Y21tzapVq5g3bx7jxo0DIDc3l4oVKxIWFsaxY8fw8/Njw4YNHD16lOTkZFatWsXQoUNZ\ntWoVb7zxBomJic/3zf0HRYsXN5iSkpqaSmpKCv5FAx6IKWYQo9VqiYuJoXiJEvp1B/bvp049033J\nPaxzp7f1F1V27PAmMTF5p32jY2Lx8HDH0dHwbirFiwcQE5sXl5KaSnJKKkWL+uHj40VKat68PqXS\nwvA0cVo6EafO8Eqdmi8wqyfToVVVVs3uzqrZ3WnXIojYm3mfp9ibibi72uNob2PwnGux8RyJiAJA\noVDQrH550tWZRF/X3SVm6foDnLpwnbmjO+LiZJ5f+udjoijtm3d618nOHldHRy7FxRrE7TwaTo2+\nvanyWQ82H9hPfHIyqWo1TYKqUbFocQAysjJZsnMrLe9XHoVpnb8eazAlxcnWDld7By7dzJuWVLds\n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\nTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "tags": [] + } + } + ] + } + ] +} \ No newline at end of file From 65053360c7533fc585bdb9f53abada1e9b39f564 Mon Sep 17 00:00:00 2001 From: Ben Zvan Date: Wed, 26 Jun 2019 09:41:11 -0500 Subject: [PATCH 066/215] Revert "Created using Colaboratory" This reverts commit 83b99c4109dce6ac7a6b5d2048e26f1ad60ef3f3. This commit was randomly created here when I connected to colaboratory --- ..._Intro_to_Visualizations_with_Python.ipynb | 219 ------------------ 1 file changed, 219 deletions(-) delete mode 100644 Attendee_Intro_to_Visualizations_with_Python.ipynb diff --git a/Attendee_Intro_to_Visualizations_with_Python.ipynb b/Attendee_Intro_to_Visualizations_with_Python.ipynb deleted file mode 100644 index 3cf5c98..0000000 --- a/Attendee_Intro_to_Visualizations_with_Python.ipynb +++ /dev/null @@ -1,219 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "name": "Copy of Attendee Intro to Visualizations with Python.ipynb", - "version": "0.3.2", - "provenance": [], - "collapsed_sections": [], - "include_colab_link": true - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - } - }, - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "view-in-github", - "colab_type": "text" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "cpwsxUdOthZh", - "colab_type": "text" - }, - "source": [ - "Import the California Housing dataset. \n" - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "fuynGZ8jDsca", - "colab_type": "code", - "colab": {} - }, - "source": [ - "DATASETS_URL = \"https://github.com/ageron/handson-ml/raw/master/datasets\"\n", - "import os\n", - "import tarfile\n", - "from six.moves import urllib\n", - "\n", - "\n", - "HOUSING_PATH = \"datasets/housing\"\n", - "HOUSING_URL = DATASETS_URL + \"/housing/housing.tgz\"\n", - "\n", - "def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):\n", - " if not os.path.exists(housing_path):\n", - " os.makedirs(housing_path)\n", - " tgz_path = os.path.join(housing_path, \"housing.tgz\")\n", - " urllib.request.urlretrieve(housing_url, tgz_path)\n", - " housing_tgz = tarfile.open(tgz_path)\n", - " housing_tgz.extractall(path=housing_path)\n", - " housing_tgz.close()\n", - " \n", - "fetch_housing_data()\n", - "import pandas as pd\n", - "\n", - "def load_housing_data(housing_path=HOUSING_PATH):\n", - " csv_path = os.path.join(housing_path, \"housing.csv\")\n", - " return pd.read_csv(csv_path)\n", - "housing = load_housing_data()\n" - ], - "execution_count": 0, - "outputs": [] - }, - { - "cell_type": "code", - "metadata": { - "id": "WosdC2Qq1t2e", - "colab_type": "code", - "outputId": "af80b7d0-6c88-4b82-9907-169b14bf0fc0", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 102 - } - }, - "source": [ - "h_ocean = 9136/20640\n", - "inland = 6551/20640\n", - "n_ocean = 2658/20640\n", - "n_bay = 2290/20640\n", - "island = 5/20640\n", - "\n", - "\n", - "print(h_ocean)\n", - "print(inland)\n", - "print(n_ocean)\n", - "print(n_bay)\n", - "print(island)" - ], - "execution_count": 0, - "outputs": [ - { - "output_type": "stream", - "text": [ - "0.44263565891472867\n", - "0.31739341085271316\n", - "0.12877906976744186\n", - "0.11094961240310078\n", - "0.00024224806201550387\n" - ], - "name": "stdout" - } - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "hNfIJI1Y1TRY", - "colab_type": "code", - "outputId": "84cc951a-a7a4-49be-8e8e-c2d43beaae84", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 406 - } - }, - "source": [ - "# Pie chart\n", - "labels = ['<1H OCEAN', 'INLAND', 'NEAR OCEAN', 'NEAR BAY', 'ISLAND']\n", - "sizes = [44, 32, 13, 11, 0.2]\n", - "#colors\n", - "colors = ['#66b3ff','#A5D6A7', '#D7CCC8', '#FDD835', '#263238']\n", - " \n", - "fig1, ax1 = plt.subplots()\n", - "patches, texts, autotexts = ax1.pie(sizes, colors = colors, labels=labels, autopct='%1.1f%%', startangle=90)\n", - "for text in texts:\n", - " text.set_color('black')\n", - "for autotext in autotexts:\n", - " autotext.set_color('black')\n", - "# Equal aspect ratio ensures that pie is drawn as a circle\n", - "ax1.axis('equal') \n", - "plt.tight_layout()\n", - "plt.legend(loc = 'upper left')\n", - "plt.show()" - ], - "execution_count": 0, - "outputs": [ - { - "output_type": "display_data", - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": { - "tags": [] - } - } - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "P1i1ZlpF8w_G", - "colab_type": "text" - }, - "source": [ - "Let's move on to another type of plot that is useful for showing correlations. We would expect to see a correlation between the income and the house value, as more income would most likely mean a nicer home." - ] - }, - { - "cell_type": "code", - "metadata": { - "id": "TQ3EUT1q1s0B", - "colab_type": "code", - "outputId": "ff85fdb6-9259-414e-b591-8885f623e533", - "colab": { - "base_uri": "https://localhost:8080/", - "height": 701 - } - }, - "source": [ - "# Set up the matplotlib figure\n", - "f, ax = plt.subplots(figsize=(12, 10))\n", - "plt.title('Pearson Correlation of Housing Features')\n", - "\n", - "# Draw the heatmap using seaborn\n", - "sns.heatmap(housing.corr(),linewidths=0.25, square=True, cmap = \"BrBG\", linecolor='black', annot=True)" - ], - "execution_count": 0, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "" - ] - }, - "metadata": { - "tags": [] - }, - "execution_count": 45 - }, - { - "output_type": "display_data", - "data": { - "image/png": "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\ny3zq1q2r6ZI+Lqe2bdtiY2ODqakpfn5+Wjn5+vpiZWWFjY0NXl5e3LhxQ2cfT3ruHiUvL09nzfWK\nFSu0cuvUqRNmZmZYWVnRqlWrJ+7X0dGREydOcOjQIfLz8xk7dizVqlXTiWvQoAEODg6YmZnx5ptv\ncuPGDS5fvkxOTg4NGjQACj4ZyM/Pf+SxpkyZopV769atgYKx6tKlCxYWFtjY2NCmTRvNWG3YsEHz\nZqtOnTpPHJ+i3L9/X7PkJj4+HmdnZz788EMAWrZsSUJCAtevX6d06dIcOnSIuLg4LCwsmDp1Kk5O\nTs98PCHEy0vWjAshnln37t0xNTVFrVbj7u7OwoULKVGiBOnp6cTFxeHr66uJtbW1JSUlhfT0dEJD\nQ7l06RIKhYKbN2/i5+enibO3t9f8e+LEicydO5fevXtjZWXF4MGD8fX1JTk5GTs7O02cnZ2dVhex\nZMmSAJiamgLoTNLs7e1RKBTcunULd3f3ImtLTk7WmgzZ2dlpTeoL5/nP2+np6SxatIg1a9YABRPZ\nf66jz8vLY+bMmZo3KZmZmVSoUKHIXArnVPg49vb2WjnZ2tpq/m1qalrk0pgnPXeP8ncnuLBZs2Zp\nlp88KbeiNG/enNTUVGbMmMGlS5do3bp1kV/c/Hs8C9eVmpqqVceTJq6PWjOenp7OpEmTmDp1KlCw\nbOWtt94CYPPmzSxbtozMzEzy8/OLXPbzJKampppxSUtLIzExUeu8sLCwIDk5mV69emnekNy+fZuu\nXbvy1VdfYWJi8szHFEK8nGQyLoR4ZoW/wFmYk5MTH3zwATNnztTZNmzYMGrUqMHs2bMxNTXF39//\nkfsvU6YMwcHBBAcHc+jQIb766iu8vb0pU6YMKSkpuLm5AQVLAcqUKfPUeVtbW/PWW2+xc+dOevfu\nrbVtyZIlfPTRR5pj/O1ZjuHk5MRHH32kWSdclG3btvHzzz+zYsUKHB0diYyMZPPmzY/d77/J6Z/7\neN7n7nlyUygUmjcGqampWo/z9/fH39+fW7du8dVXX7FhwwbMzJ78K8nW1lazXh547iujODk50adP\nHxo1aqR1/61btwgKCmLt2rVUq1aNy5cv4+PjU+Q+FAqF1hu+f9ZY+FgVK1YkOjq6yO39+/enf//+\n/PXXX3z66afUqVNH00UXQrz6ZJmKEOKFqVevHnFxcZqP9X/77TcmTJgAFKyVrlatGqamphw+fJgr\n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\nsHug/QC2j3gv7GxtcbC3p06tahQN0FUs3u/cns/7Gp4x2b7zFzp2aPsi0nkqYTtOsGmHrupnYaHE\n3cVe/1hGZjZaLdjaFPz56vh6dd5pXY29f17gsxHrWDmrGypr3dfEN2v2c/hkFDOGt3/k800pTaPB\n5qFO3E6lIvWBji4+OYlOk0YzrWcfrCwt+W7nVtSZGSSl512k+cHUcaisrJn60aesGjSCzpMNL/4S\nxpem0aCy+ud96+Pqxnef9qXOsIGcjomiUcXKbPx6OGX7fsxHTZtTxNEZtx6dyczOYn7PPszu3pvP\nv19k7FQeK02jxuahAZudjQ2pDww645OS6DQmmGmf9tV9jn/+CXWGhqTUVL5s/y5FXFxxbduUzOxs\n5vcbxOwvvuLz2VOMnQpr165l3br1wP3vYQ93/WMZGRn3v4ftDJ5ja1tQf6PBzs4uX1+k66sMv8e3\nb99Bx475rwe4desWkZGR1K1b91/n9ax07Tc80Mp4KIdH9UO2dnYULV6cz/v316/v0bs3bZo1R61W\n53sfxPNx9+5dAgMD9ctubm7cuXMHBwcH7ty5g5ubm8FjMTExz/Q6ZlMZ79mzJ7Vr1wYgKCiIlStX\nEhwczKRJk2jXrh1FihRhyZIlaLVa/Pz8CA0NZc2aNcyZM+eJX8PDw4OVK1fSrl07Vq5cyZEjR4iL\ni2P16tWsWLGCRYsWodFoWLhwIV988QXLly9H+cCpwHPnzjF9+nSaNGnCqFGjmDVrFqtWrcLZ2Zkt\nW7bw888/Y21tzapVq5g3bx7jxo0DIDc3l4oVKxIWFsaxY8fw8/Njw4YNHD16lOTkZFatWsXQoUNZ\ntWoVb7zxBomJic/3zf0HRYsXN5iSkpqaSmpKCv5FAx6IKWYQo9VqiYuJoXiJEvp1B/bvp049033J\nPaxzp7f1F1V27PAmMTF5p32jY2Lx8HDH0dHwbirFiwcQE5sXl5KaSnJKKkWL+uHj40VKat68PqXS\nwvA0cVo6EafO8Eqdmi8wqyfToVVVVs3uzqrZ3WnXIojYm3mfp9ibibi72uNob2PwnGux8RyJiAJA\noVDQrH550tWZRF/X3SVm6foDnLpwnbmjO+LiZJ5f+udjoijtm3d618nOHldHRy7FxRrE7TwaTo2+\nvanyWQ82H9hPfHIyqWo1TYKqUbFocQAysjJZsnMrLe9XHoVpnb8eazAlxcnWDld7By7dzJuWVLds\n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\nTkSuQmCC\n", - "text/plain": [ - "" - ] - }, - "metadata": { - "tags": [] - } - } - ] - } - ] -} \ No newline at end of file From ce1630141e70b2ca599a21fd9494e98b88f25b2d Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:18:52 -0700 Subject: [PATCH 067/215] reduce diff??? --- flag.go | 111 +------------------------------------------------------- 1 file changed, 1 insertion(+), 110 deletions(-) diff --git a/flag.go b/flag.go index b4cf6da..de42938 100644 --- a/flag.go +++ b/flag.go @@ -115,22 +115,6 @@ type Generic interface { String() string } -// GenericFlag is the flag type for types implementing Generic -type GenericFlag struct { - Name string - Value Generic - Usage string - EnvVar string - Required bool -} - -// String returns the string representation of the generic flag to display the -// help text to the user (uses the String() method of the generic flag to show -// the value) -func (f GenericFlag) String() string { - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s%s \"%v\"\t%v", prefixFor(f.Name), f.Name, f.Value, f.Usage)) -} - // 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 @@ -178,20 +162,6 @@ func (f *StringSlice) Value() []string { return *f } -type StringSliceFlag struct { - Name string - Value *StringSlice - Usage string - EnvVar string - Required bool -} - -func (f StringSliceFlag) String() string { - firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") - pref := prefixFor(firstName) - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) -} - // Get returns the slice of strings set by this flag func (f *StringSlice) Get() interface{} { return *f @@ -257,20 +227,6 @@ func (f *IntSlice) Value() []int { return *f } -type IntSliceFlag struct { - Name string - Value *IntSlice - Usage string - EnvVar string - Required bool -} - -func (f IntSliceFlag) String() string { - firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") - pref := prefixFor(firstName) - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) -} - // Get returns the slice of ints set by this flag func (f *IntSlice) Get() interface{} { return *f @@ -331,17 +287,6 @@ func (f IntSliceFlag) IsRequired() bool { return f.Required } -type BoolFlag struct { - Name string - Usage string - EnvVar string - Required bool -} - -func (f BoolFlag) String() string { - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) -} - // Value returns the slice of ints set by this flag func (f *Int64Slice) Value() []int64 { return *f @@ -394,17 +339,6 @@ func (f BoolFlag) IsRequired() bool { return f.Required } -type BoolTFlag struct { - Name string - Usage string - EnvVar string - Required bool -} - -func (f BoolTFlag) String() string { - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) -} - // ApplyWithError populates the flag given the flag set and environment func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { val := false @@ -468,14 +402,6 @@ func (f BoolTFlag) IsRequired() bool { return f.Required } -type StringFlag struct { - Name string - Value string - Usage string - EnvVar string - Required bool -} - // Apply populates the flag given the flag set and environment // Ignores errors func (f StringFlag) Apply(set *flag.FlagSet) { @@ -536,18 +462,6 @@ func (f StringFlag) IsRequired() bool { return f.Required } -type IntFlag struct { - Name string - Value int - Usage string - EnvVar string - Required bool -} - -func (f IntFlag) String() string { - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) -} - // ApplyWithError populates the flag given the flag set and environment func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { @@ -602,18 +516,6 @@ func (f IntFlag) IsRequired() bool { return f.Required } -type DurationFlag struct { - Name string - Value time.Duration - Usage string - EnvVar string - Required bool -} - -func (f DurationFlag) String() string { - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) -} - // Apply populates the flag given the flag set and environment // Ignores errors func (f Uint64Flag) Apply(set *flag.FlagSet) { @@ -667,24 +569,13 @@ func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { set.Duration(name, f.Value, f.Usage) }) return nil + } func (f DurationFlag) IsRequired() bool { return f.Required } -type Float64Flag struct { - Name string - Value float64 - Usage string - EnvVar string - Required bool -} - -func (f Float64Flag) String() string { - return withHints(f.EnvVar, f.Required, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) -} - // Apply populates the flag given the flag set and environment // Ignores errors func (f Float64Flag) Apply(set *flag.FlagSet) { From fa8187f2ce6a7d8258899b46ccfe081c9c0ea6f7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:19:42 -0700 Subject: [PATCH 068/215] reduce diff --- flag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flag.go b/flag.go index de42938..6e911dc 100644 --- a/flag.go +++ b/flag.go @@ -568,8 +568,8 @@ func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { } set.Duration(name, f.Value, f.Usage) }) - return nil + return nil } func (f DurationFlag) IsRequired() bool { From e6842c0b7521b5e608da30a4e8a5ed06e6469cf7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:21:05 -0700 Subject: [PATCH 069/215] merge in test file --- flag_test.go | 57 ++++++++++++++++++++++++++++++++++++++- required_flags_test.go | 60 ------------------------------------------ 2 files changed, 56 insertions(+), 61 deletions(-) delete mode 100644 required_flags_test.go diff --git a/flag_test.go b/flag_test.go index da9fd73..45f3ac9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1,6 +1,7 @@ package cli import ( + "flag" "fmt" "io" "io/ioutil" @@ -1052,7 +1053,7 @@ func TestParseBoolShortOptionHandle(t *testing.T) { a := App{ Commands: []Command{ { - Name: "foobar", + Name: "foobar", UseShortOptionHandling: true, Action: func(ctx *Context) error { if ctx.Bool("serve") != true { @@ -1352,3 +1353,57 @@ func TestFlagFromFile(t *testing.T) { } } } + +func TestContext_CheckRequiredFlagsSuccess(t *testing.T) { + flags := []Flag{ + StringFlag{ + Name: "required", + Required: true, + }, + StringFlag{ + Name: "optional", + }, + } + + set := flag.NewFlagSet("test", 0) + for _, f := range flags { + f.Apply(set) + } + + e := set.Parse([]string{"--required", "foo"}) + if e != nil { + t.Errorf("Expected no error parsing but there was one: %s", e) + } + + err := checkRequiredFlags(flags, set) + if err != nil { + t.Error("Expected flag parsing to be successful") + } +} + +func TestContext_CheckRequiredFlagsFailure(t *testing.T) { + flags := []Flag{ + StringFlag{ + Name: "required", + Required: true, + }, + StringFlag{ + Name: "optional", + }, + } + + set := flag.NewFlagSet("test", 0) + for _, f := range flags { + f.Apply(set) + } + + e := set.Parse([]string{"--optional", "foo"}) + if e != nil { + t.Errorf("Expected no error parsing but there was one: %s", e) + } + + err := checkRequiredFlags(flags, set) + if err == nil { + t.Error("Expected flag parsing to be unsuccessful") + } +} diff --git a/required_flags_test.go b/required_flags_test.go deleted file mode 100644 index f9abe7a..0000000 --- a/required_flags_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package cli - -import ( - "flag" - "testing" -) - -func TestContext_CheckRequiredFlagsSuccess(t *testing.T) { - flags := []Flag{ - StringFlag{ - Name: "required", - Required: true, - }, - StringFlag{ - Name: "optional", - }, - } - - set := flag.NewFlagSet("test", 0) - for _, f := range flags { - f.Apply(set) - } - - e := set.Parse([]string{"--required", "foo"}) - if e != nil { - t.Errorf("Expected no error parsing but there was one: %s", e) - } - - err := checkRequiredFlags(flags, set) - if err != nil { - t.Error("Expected flag parsing to be successful") - } -} - -func TestContext_CheckRequiredFlagsFailure(t *testing.T) { - flags := []Flag{ - StringFlag{ - Name: "required", - Required: true, - }, - StringFlag{ - Name: "optional", - }, - } - - set := flag.NewFlagSet("test", 0) - for _, f := range flags { - f.Apply(set) - } - - e := set.Parse([]string{"--optional", "foo"}) - if e != nil { - t.Errorf("Expected no error parsing but there was one: %s", e) - } - - err := checkRequiredFlags(flags, set) - if err == nil { - t.Error("Expected flag parsing to be unsuccessful") - } -} From f7d5e2c21e4cca02de26a7f448d69f4dac531af7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:22:16 -0700 Subject: [PATCH 070/215] reduce diff --- flag_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flag_test.go b/flag_test.go index 45f3ac9..33c9eee 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1053,7 +1053,7 @@ func TestParseBoolShortOptionHandle(t *testing.T) { a := App{ Commands: []Command{ { - Name: "foobar", + Name: "foobar", UseShortOptionHandling: true, Action: func(ctx *Context) error { if ctx.Bool("serve") != true { From 30a71dc427bc2634f00d9fe315e5717022e0eb66 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:25:52 -0700 Subject: [PATCH 071/215] update Run command --- app.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/app.go b/app.go index 8723539..dcc1ee7 100644 --- a/app.go +++ b/app.go @@ -196,17 +196,8 @@ func (a *App) Run(arguments []string) (err error) { err = set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) cerr := checkRequiredFlags(a.Flags, set) - context := NewContext(a, set, nil) - // Define here so it closes over the above variables - showErrAndHelp := func(err error) { - fmt.Fprintln(a.Writer, err) - fmt.Fprintln(a.Writer) - ShowAppHelp(context) - fmt.Fprintln(a.Writer) - } - if nerr != nil { fmt.Fprintln(a.Writer, nerr) ShowAppHelp(context) @@ -219,7 +210,7 @@ func (a *App) Run(arguments []string) (err error) { } if cerr != nil { - showErrAndHelp(cerr) + ShowAppHelp(context) return cerr } @@ -345,7 +336,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } if cerr != nil { - showErrAndHelp(cerr) + ShowAppHelp(context) return cerr } From 9c299e7e8af265e017adf7abf431a0fe0c89dd95 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:28:29 -0700 Subject: [PATCH 072/215] reduce diff --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index dcc1ee7..fee45dc 100644 --- a/app.go +++ b/app.go @@ -332,7 +332,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } else { ShowCommandHelp(ctx, context.Args().First()) } - fmt.Fprintln(a.Writer, err) + return nerr } if cerr != nil { From 0608059cc709e86905bfd18886d6649275c9937e Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:32:15 -0700 Subject: [PATCH 073/215] reduce diff --- command.go | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/command.go b/command.go index dcce5bd..5bb2ada 100644 --- a/command.go +++ b/command.go @@ -262,37 +262,11 @@ func reorderArgs(args []string) []string { } } - // Define here so it closes over the above variables - showErrAndHelp := func(err error) { - fmt.Fprintln(ctx.App.Writer, err) - fmt.Fprintln(ctx.App.Writer) - ShowCommandHelp(ctx, c.Name) - fmt.Fprintln(ctx.App.Writer) - } - - if err != nil { - showErrAndHelp(fmt.Errorf("Incorrect Usage.")) - return err - } - - nerr := normalizeFlags(c.Flags, set) - if nerr != nil { - showErrAndHelp(nerr) - return nerr - } - - cerr := checkRequiredFlags(c.Flags, set) - if cerr != nil { - showErrAndHelp(cerr) - return cerr - } - - context := NewContext(ctx.App, set, ctx.globalSet) return append(flags, nonflags...) } func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { - allCharsFlags := func (s string) bool { + allCharsFlags := func(s string) bool { for i := range s { f := set.Lookup(string(s[i])) if f == nil { @@ -400,9 +374,6 @@ func (c Command) startApp(ctx *Context) error { app.Commands[index].commandNamePath = []string{c.Name, cc.Name} } - // set the writer to the original App's writer - app.Writer = ctx.App.Writer - return app.RunAsSubcommand(ctx) } From 3d2d6975b4fffee753c9422f3440d6b7c114ef40 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:32:42 -0700 Subject: [PATCH 074/215] reduce diff --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index 5bb2ada..3d44404 100644 --- a/command.go +++ b/command.go @@ -266,7 +266,7 @@ func reorderArgs(args []string) []string { } func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { - allCharsFlags := func(s string) bool { + allCharsFlags := func (s string) bool { for i := range s { f := set.Lookup(string(s[i])) if f == nil { From af627c73c3ddc2d4ff1e4c0847c3355bc0a47c0d Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:34:17 -0700 Subject: [PATCH 075/215] update func name --- context.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index 6fcf7c3..c9a32ff 100644 --- a/context.go +++ b/context.go @@ -290,7 +290,7 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error { func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { // If the help flag is included then none of the other flags are required. for _, f := range flags { - if f.getName() == "help" { + if f.GetName() == "help" { return nil } } @@ -302,9 +302,9 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { for _, f := range flags { if f.IsRequired() { - key := strings.Split(f.getName(), ",")[0] + key := strings.Split(f.GetName(), ",")[0] if !visited[key] { - return fmt.Errorf("Required flag %s not set", f.getName()) + return fmt.Errorf("Required flag %s not set", f.GetName()) } } } From 310bfeb1942571dfe0ac9f60f45e75df11189e4e Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:44:41 -0700 Subject: [PATCH 076/215] add required attr to generator --- generate-flag-types | 1 + 1 file changed, 1 insertion(+) diff --git a/generate-flag-types b/generate-flag-types index 1358857..6c5bbc8 100755 --- a/generate-flag-types +++ b/generate-flag-types @@ -143,6 +143,7 @@ def _write_cli_flag_types(outfile, types): Usage string EnvVar string FilePath string + Required bool Hidden bool """.format(**typedef)) From 62e99ad1c16714cda6c9f8b980dd9483372771e2 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:46:22 -0700 Subject: [PATCH 077/215] add IsRequired to generator --- generate-flag-types | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/generate-flag-types b/generate-flag-types index 6c5bbc8..e0b5aa1 100755 --- a/generate-flag-types +++ b/generate-flag-types @@ -171,6 +171,11 @@ def _write_cli_flag_types(outfile, types): return f.Name }} + // IsRequired returns the whether or not the flag is required + func (f {name}Flag) IsRequired() bool {{ + return f.Required + }} + // {name} looks up the value of a local {name}Flag, returns // {context_default} if not found func (c *Context) {name}(name string) {context_type} {{ From 8a58b7e039e37b0631d6ced0ab3279c319c4d8c8 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 20:47:47 -0700 Subject: [PATCH 078/215] remove manual isRequired funcs --- flag.go | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/flag.go b/flag.go index 6e911dc..0252ba2 100644 --- a/flag.go +++ b/flag.go @@ -139,10 +139,6 @@ func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { return nil } -func (f GenericFlag) IsRequired() bool { - return f.Required -} - // StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter type StringSlice []string @@ -200,10 +196,6 @@ func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { return nil } -func (f StringSliceFlag) IsRequired() bool { - return f.Required -} - // IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter type IntSlice []int @@ -283,10 +275,6 @@ func (f *Int64Slice) String() string { return fmt.Sprintf("%#v", *f) } -func (f IntSliceFlag) IsRequired() bool { - return f.Required -} - // Value returns the slice of ints set by this flag func (f *Int64Slice) Value() []int64 { return *f @@ -335,10 +323,6 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } -func (f BoolFlag) IsRequired() bool { - return f.Required -} - // ApplyWithError populates the flag given the flag set and environment func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { val := false @@ -398,10 +382,6 @@ func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { return nil } -func (f BoolTFlag) IsRequired() bool { - return f.Required -} - // Apply populates the flag given the flag set and environment // Ignores errors func (f StringFlag) Apply(set *flag.FlagSet) { @@ -458,10 +438,6 @@ func (f Int64Flag) Apply(set *flag.FlagSet) { f.ApplyWithError(set) } -func (f StringFlag) IsRequired() bool { - return f.Required -} - // ApplyWithError populates the flag given the flag set and environment func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { @@ -512,10 +488,6 @@ func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { return nil } -func (f IntFlag) IsRequired() bool { - return f.Required -} - // Apply populates the flag given the flag set and environment // Ignores errors func (f Uint64Flag) Apply(set *flag.FlagSet) { @@ -572,10 +544,6 @@ func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { return nil } -func (f DurationFlag) IsRequired() bool { - return f.Required -} - // Apply populates the flag given the flag set and environment // Ignores errors func (f Float64Flag) Apply(set *flag.FlagSet) { @@ -615,10 +583,6 @@ func visibleFlags(fl []Flag) []Flag { return visible } -func (f Float64Flag) IsRequired() bool { - return f.Required -} - func prefixFor(name string) (prefix string) { if len(name) == 1 { prefix = "-" From 922d2318916c3b59eafde03c36b12551a71f2d51 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 21:28:09 -0700 Subject: [PATCH 079/215] ./generate-flag-types cli -i flag-types.json -o flag_generated.go --- flag_generated.go | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/flag_generated.go b/flag_generated.go index 001576c..a3e4d6e 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -14,6 +14,7 @@ type BoolFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Destination *bool } @@ -29,6 +30,11 @@ func (f BoolFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f BoolFlag) IsRequired() bool { + return f.Required +} + // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { @@ -62,6 +68,7 @@ type BoolTFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Destination *bool } @@ -77,6 +84,11 @@ func (f BoolTFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f BoolTFlag) IsRequired() bool { + return f.Required +} + // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { @@ -110,6 +122,7 @@ type DurationFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value time.Duration Destination *time.Duration @@ -126,6 +139,11 @@ func (f DurationFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f DurationFlag) IsRequired() bool { + return f.Required +} + // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { @@ -159,6 +177,7 @@ type Float64Flag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value float64 Destination *float64 @@ -175,6 +194,11 @@ func (f Float64Flag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Float64Flag) IsRequired() bool { + return f.Required +} + // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { @@ -208,6 +232,7 @@ type GenericFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value Generic } @@ -223,6 +248,11 @@ func (f GenericFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f GenericFlag) IsRequired() bool { + return f.Required +} + // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { @@ -256,6 +286,7 @@ type Int64Flag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value int64 Destination *int64 @@ -272,6 +303,11 @@ func (f Int64Flag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Int64Flag) IsRequired() bool { + return f.Required +} + // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { @@ -305,6 +341,7 @@ type IntFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value int Destination *int @@ -321,6 +358,11 @@ func (f IntFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f IntFlag) IsRequired() bool { + return f.Required +} + // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { @@ -354,6 +396,7 @@ type IntSliceFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value *IntSlice } @@ -369,6 +412,11 @@ func (f IntSliceFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f IntSliceFlag) IsRequired() bool { + return f.Required +} + // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { @@ -402,6 +450,7 @@ type Int64SliceFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value *Int64Slice } @@ -417,6 +466,11 @@ func (f Int64SliceFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Int64SliceFlag) IsRequired() bool { + return f.Required +} + // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { @@ -450,6 +504,7 @@ type StringFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value string Destination *string @@ -466,6 +521,11 @@ func (f StringFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f StringFlag) IsRequired() bool { + return f.Required +} + // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { @@ -499,6 +559,7 @@ type StringSliceFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value *StringSlice } @@ -514,6 +575,11 @@ func (f StringSliceFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f StringSliceFlag) IsRequired() bool { + return f.Required +} + // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { @@ -547,6 +613,7 @@ type Uint64Flag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value uint64 Destination *uint64 @@ -563,6 +630,11 @@ func (f Uint64Flag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Uint64Flag) IsRequired() bool { + return f.Required +} + // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { @@ -596,6 +668,7 @@ type UintFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value uint Destination *uint @@ -612,6 +685,11 @@ func (f UintFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f UintFlag) IsRequired() bool { + return f.Required +} + // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { From 6a2ae783730e54eb7ea91cf6839ed46446134017 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 11 Jul 2019 21:53:10 -0700 Subject: [PATCH 080/215] backwards compatible RequiredFlag implementation --- context.go | 10 ++++++---- flag.go | 9 ++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/context.go b/context.go index c9a32ff..8746d68 100644 --- a/context.go +++ b/context.go @@ -301,10 +301,12 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { }) for _, f := range flags { - if f.IsRequired() { - key := strings.Split(f.GetName(), ",")[0] - if !visited[key] { - return fmt.Errorf("Required flag %s not set", f.GetName()) + if rf, ok := f.(RequiredFlag); ok { + if rf.IsRequired() { + key := strings.Split(f.GetName(), ",")[0] + if !visited[key] { + return fmt.Errorf("Required flag %s not set", f.GetName()) + } } } } diff --git a/flag.go b/flag.go index 0252ba2..71fd729 100644 --- a/flag.go +++ b/flag.go @@ -72,10 +72,17 @@ type Flag interface { fmt.Stringer // Apply Flag settings to the given flag set Apply(*flag.FlagSet) - IsRequired() bool GetName() string } +// RequiredFlag is an interface that allows us to return mark flags as required +// it allows flags defined in this library to be marked as required in a backwards compatible fashion +type RequiredFlag interface { + Flag + + IsRequired() bool +} + // 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 From f6777bf4bf44867abbcaa63a97a67db60469ea80 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 13 Jul 2019 01:03:46 -0700 Subject: [PATCH 081/215] quote the flag name --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 8746d68..bd6ac6c 100644 --- a/context.go +++ b/context.go @@ -305,7 +305,7 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { if rf.IsRequired() { key := strings.Split(f.GetName(), ",")[0] if !visited[key] { - return fmt.Errorf("Required flag %s not set", f.GetName()) + return fmt.Errorf("Required flag %q not set", f.GetName()) } } } From 550ed20ea429e19b7b132984a6e34c057acabc42 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 13 Jul 2019 01:26:47 -0700 Subject: [PATCH 082/215] update tests --- context_test.go | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ flag_test.go | 55 ------------------------- 2 files changed, 104 insertions(+), 55 deletions(-) diff --git a/context_test.go b/context_test.go index 7acca10..fac1d4b 100644 --- a/context_test.go +++ b/context_test.go @@ -401,3 +401,107 @@ func TestContext_GlobalSet(t *testing.T) { expect(t, c.GlobalInt("int"), 1) expect(t, c.GlobalIsSet("int"), true) } + +func TestCheckRequiredFlags(t *testing.T) { + tdata := []struct { + testCase string + parseInput []string + flags []Flag + expectedAnError bool + }{ + { + testCase: "empty", + }, + { + testCase: "optional", + flags: []Flag{ + StringFlag{Name: "optionalFlag"}, + }, + }, + { + testCase: "required", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + }, + expectedAnError: true, + }, + { + testCase: "required_and_present", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + }, + parseInput: []string{"--requiredFlag", "myinput"}, + }, + { + testCase: "required_and_optional", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "optionalFlag"}, + }, + expectedAnError: true, + }, + { + testCase: "required_and_optional_and_optional_present", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "optionalFlag"}, + }, + parseInput: []string{"--optionalFlag", "myinput"}, + expectedAnError: true, + }, + { + testCase: "required_and_optional_and_required_present", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "optionalFlag"}, + }, + parseInput: []string{"--requiredFlag", "myinput"}, + }, + { + testCase: "two_required", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "requiredFlagTwo", Required: true}, + }, + expectedAnError: true, + }, + { + testCase: "two_required_and_one_present", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "requiredFlagTwo", Required: true}, + }, + parseInput: []string{"--requiredFlag", "myinput"}, + expectedAnError: true, + }, + { + testCase: "two_required_and_both_present", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "requiredFlagTwo", Required: true}, + }, + parseInput: []string{"--requiredFlag", "myinput", "--requiredFlagTwo", "myinput"}, + }, + } + for _, test := range tdata { + t.Run(test.testCase, func(t *testing.T) { + // setup + set := flag.NewFlagSet("test", 0) + for _, flags := range test.flags { + flags.Apply(set) + } + set.Parse(test.parseInput) + + // logic under test + err := checkRequiredFlags(test.flags, set) + + // assertions + if test.expectedAnError && err == nil { + t.Errorf("expected an error, but there was none") + } + if !test.expectedAnError && err != nil { + t.Errorf("did not expected an error, but there was one: %s", err) + } + }) + } +} diff --git a/flag_test.go b/flag_test.go index 33c9eee..da9fd73 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1,7 +1,6 @@ package cli import ( - "flag" "fmt" "io" "io/ioutil" @@ -1353,57 +1352,3 @@ func TestFlagFromFile(t *testing.T) { } } } - -func TestContext_CheckRequiredFlagsSuccess(t *testing.T) { - flags := []Flag{ - StringFlag{ - Name: "required", - Required: true, - }, - StringFlag{ - Name: "optional", - }, - } - - set := flag.NewFlagSet("test", 0) - for _, f := range flags { - f.Apply(set) - } - - e := set.Parse([]string{"--required", "foo"}) - if e != nil { - t.Errorf("Expected no error parsing but there was one: %s", e) - } - - err := checkRequiredFlags(flags, set) - if err != nil { - t.Error("Expected flag parsing to be successful") - } -} - -func TestContext_CheckRequiredFlagsFailure(t *testing.T) { - flags := []Flag{ - StringFlag{ - Name: "required", - Required: true, - }, - StringFlag{ - Name: "optional", - }, - } - - set := flag.NewFlagSet("test", 0) - for _, f := range flags { - f.Apply(set) - } - - e := set.Parse([]string{"--optional", "foo"}) - if e != nil { - t.Errorf("Expected no error parsing but there was one: %s", e) - } - - err := checkRequiredFlags(flags, set) - if err == nil { - t.Error("Expected flag parsing to be unsuccessful") - } -} From 746866c10daf9425d41140f78ffc518ee4d9ae01 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 13 Jul 2019 03:44:39 -0700 Subject: [PATCH 083/215] add update integration with the help output --- app.go | 11 ++++++----- app_test.go | 44 ++++++++++++++++++++++++++++++++------------ context.go | 7 ------- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/app.go b/app.go index fee45dc..177ed3e 100644 --- a/app.go +++ b/app.go @@ -209,11 +209,6 @@ func (a *App) Run(arguments []string) (err error) { return nil } - if cerr != nil { - ShowAppHelp(context) - return cerr - } - if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) @@ -235,6 +230,12 @@ func (a *App) Run(arguments []string) (err error) { return nil } + if cerr != nil { + fmt.Fprintln(a.Writer, cerr) + ShowAppHelp(context) + return cerr + } + if a.After != nil { defer func() { if afterErr := a.After(context); afterErr != nil { diff --git a/app_test.go b/app_test.go index 629681e..04b515e 100644 --- a/app_test.go +++ b/app_test.go @@ -878,21 +878,41 @@ func TestAppNoHelpFlag(t *testing.T) { } func TestAppHelpPrinter(t *testing.T) { - oldPrinter := HelpPrinter - defer func() { - HelpPrinter = oldPrinter - }() - - var wasCalled = false - HelpPrinter = func(w io.Writer, template string, data interface{}) { - wasCalled = true + tdata := []struct { + testCase string + flags []Flag + }{ + { + testCase: "prints_help_and_does_not_error", + }, + { + testCase: "prints_help_and_does_not_error_when_required_flag_is_present", + flags: []Flag{StringFlag{Name: "flag", Required: true}}, + }, } + for _, test := range tdata { + t.Run(test.testCase, func(t *testing.T) { + oldPrinter := HelpPrinter + defer func() { + HelpPrinter = oldPrinter + }() - app := NewApp() - app.Run([]string{"-h"}) + var wasCalled = false + HelpPrinter = func(w io.Writer, template string, data interface{}) { + wasCalled = true + } - if wasCalled == false { - t.Errorf("Help printer expected to be called, but was not") + app := NewApp() + app.Flags = test.flags + err := app.Run([]string{"testCommand", "-h"}) + + if wasCalled == false { + t.Errorf("Help printer expected to be called, but was not") + } + if err != nil { + t.Errorf("did not expected an error, but there was one: %s", err) + } + }) } } diff --git a/context.go b/context.go index bd6ac6c..600307a 100644 --- a/context.go +++ b/context.go @@ -288,13 +288,6 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error { } func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { - // If the help flag is included then none of the other flags are required. - for _, f := range flags { - if f.GetName() == "help" { - return nil - } - } - visited := make(map[string]bool) set.Visit(func(f *flag.Flag) { visited[f.Name] = true From 80d7e91191cfb38c7e3fccbcf2b1320807d4b05d Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 13 Jul 2019 03:51:26 -0700 Subject: [PATCH 084/215] fill out test cases --- app_test.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/app_test.go b/app_test.go index 04b515e..360977c 100644 --- a/app_test.go +++ b/app_test.go @@ -879,15 +879,26 @@ func TestAppNoHelpFlag(t *testing.T) { func TestAppHelpPrinter(t *testing.T) { tdata := []struct { - testCase string - flags []Flag + testCase string + flags []Flag + appRunInput []string }{ { - testCase: "prints_help_and_does_not_error", + testCase: "prints_help_case_one", + appRunInput: []string{""}, }, { - testCase: "prints_help_and_does_not_error_when_required_flag_is_present", - flags: []Flag{StringFlag{Name: "flag", Required: true}}, + testCase: "prints_help_case_two", + appRunInput: []string{"-h"}, + }, + { + testCase: "prints_help_case_three", + appRunInput: []string{"testCommand", "-h"}, + }, + { + testCase: "prints_help_when_required_flag_is_present", + flags: []Flag{StringFlag{Name: "flag", Required: true}}, + appRunInput: []string{"testCommand", "-h"}, }, } for _, test := range tdata { @@ -904,7 +915,7 @@ func TestAppHelpPrinter(t *testing.T) { app := NewApp() app.Flags = test.flags - err := app.Run([]string{"testCommand", "-h"}) + err := app.Run(test.appRunInput) if wasCalled == false { t.Errorf("Help printer expected to be called, but was not") From cf824804c2353572e0f99b098829f2d7ffe2a0ec Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 13 Jul 2019 13:57:06 -0700 Subject: [PATCH 085/215] update tests --- app.go | 9 ++++- app_test.go | 111 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 22 deletions(-) diff --git a/app.go b/app.go index 177ed3e..52c2d85 100644 --- a/app.go +++ b/app.go @@ -10,6 +10,9 @@ import ( "time" ) +// printerFunc is the function signature for fmt.Fprintln +type printerFunc func(io.Writer, ...interface{}) (int, error) + var ( changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) @@ -20,6 +23,8 @@ var ( errInvalidActionType = NewExitError("ERROR invalid Action type. "+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("See %s", appActionDeprecationURL), 2) + + showFlagError printerFunc = fmt.Fprintln ) // App is the main structure of a cli application. It is recommended that @@ -195,7 +200,6 @@ func (a *App) Run(arguments []string) (err error) { set.SetOutput(ioutil.Discard) err = set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) - cerr := checkRequiredFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { @@ -230,8 +234,9 @@ func (a *App) Run(arguments []string) (err error) { return nil } + cerr := checkRequiredFlags(a.Flags, set) if cerr != nil { - fmt.Fprintln(a.Writer, cerr) + showFlagError(a.Writer, cerr) ShowAppHelp(context) return cerr } diff --git a/app_test.go b/app_test.go index 360977c..71d9fcc 100644 --- a/app_test.go +++ b/app_test.go @@ -877,56 +877,127 @@ func TestAppNoHelpFlag(t *testing.T) { } } -func TestAppHelpPrinter(t *testing.T) { +func TestRequiredFlagAppRunBehavior(t *testing.T) { tdata := []struct { - testCase string - flags []Flag - appRunInput []string + testCase string + flags []Flag + appRunInput []string + expectedHelpPrinter bool + expectedAnError bool + expectedFlagErrorPrinter bool }{ { - testCase: "prints_help_case_one", - appRunInput: []string{""}, + // expectations: + // - empty input shows the help message + // - empty input explicitly does not error + testCase: "empty_input", + appRunInput: []string{""}, + expectedHelpPrinter: true, + expectedAnError: false, // explicit false for readability (this is the default value) }, { - testCase: "prints_help_case_two", - appRunInput: []string{"-h"}, + // expectations: + // - empty input, when a required flag is present, shows the help message + // - empty input, when a required flag is present, errors + // - empty input, when a required flag is present, show the flag error message + testCase: "empty_input_with_required_flag", + appRunInput: []string{""}, + flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + expectedHelpPrinter: true, + expectedAnError: true, + expectedFlagErrorPrinter: true, }, { - testCase: "prints_help_case_three", - appRunInput: []string{"testCommand", "-h"}, + // expectations: + // - --help input, when a required flag is present, shows the help message + // - --help input, when a required flag is present, explicitly does not error + // - --help input, when a required flag is present, explicitly does not show the flag error message + testCase: "help_input_with_required_flag", + appRunInput: []string{"command", "--help"}, + flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + expectedHelpPrinter: true, + expectedAnError: false, // explicit false for readability (this is the default value) + expectedFlagErrorPrinter: false, // explicit false for readability (this is the default value) }, { - testCase: "prints_help_when_required_flag_is_present", - flags: []Flag{StringFlag{Name: "flag", Required: true}}, - appRunInput: []string{"testCommand", "-h"}, + // expectations: + // - optional input, when a required flag is present, shows the help message + // - optional input, when a required flag is present, errors + // - optional input, when a required flag is present, shows the flag error message + testCase: "optional_input_with_required_flag", + appRunInput: []string{"command", "--optional", "cats"}, + flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, + expectedHelpPrinter: true, + expectedAnError: true, + expectedFlagErrorPrinter: true, }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { + // setup - undo showFlagError mock when finished + oldShowError := showFlagError + defer func() { + showFlagError = oldShowError + }() + // setup - mock showFlagError + var showErrorWasCalled = false + showFlagError = func(writer io.Writer, err ...interface{}) (int, error) { + showErrorWasCalled = true + return 0, nil + } + // setup - undo HelpPrinter mock when finished oldPrinter := HelpPrinter defer func() { HelpPrinter = oldPrinter }() - - var wasCalled = false + // setup - mock HelpPrinter + var helpPrinterWasCalled = false HelpPrinter = func(w io.Writer, template string, data interface{}) { - wasCalled = true + helpPrinterWasCalled = true } - + // setup - app app := NewApp() app.Flags = test.flags + + // logic under test err := app.Run(test.appRunInput) - if wasCalled == false { - t.Errorf("Help printer expected to be called, but was not") + // assertions + if test.expectedHelpPrinter && helpPrinterWasCalled == false { + t.Errorf("HelpPrinter expected to be called, but was not") + } + if test.expectedFlagErrorPrinter && showErrorWasCalled == false { + t.Errorf("showFlagError expected to be called, but was not") + } + if test.expectedAnError && err == nil { + t.Errorf("expected an error, but there was none") } - if err != nil { + if !test.expectedAnError && err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } }) } } +func TestAppHelpPrinter(t *testing.T) { + oldPrinter := HelpPrinter + defer func() { + HelpPrinter = oldPrinter + }() + + var wasCalled = false + HelpPrinter = func(w io.Writer, template string, data interface{}) { + wasCalled = true + } + + app := NewApp() + app.Run([]string{"-h"}) + + if wasCalled == false { + t.Errorf("Help printer expected to be called, but was not") + } +} + func TestApp_VersionPrinter(t *testing.T) { oldPrinter := VersionPrinter defer func() { From 17108e1db49db34480170f575131e642b22bda2d Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 13 Jul 2019 13:59:29 -0700 Subject: [PATCH 086/215] tabs --- app_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app_test.go b/app_test.go index 71d9fcc..6b44654 100644 --- a/app_test.go +++ b/app_test.go @@ -888,7 +888,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }{ { // expectations: - // - empty input shows the help message + // - empty input shows the help message // - empty input explicitly does not error testCase: "empty_input", appRunInput: []string{""}, @@ -897,7 +897,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }, { // expectations: - // - empty input, when a required flag is present, shows the help message + // - empty input, when a required flag is present, shows the help message // - empty input, when a required flag is present, errors // - empty input, when a required flag is present, show the flag error message testCase: "empty_input_with_required_flag", @@ -909,7 +909,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }, { // expectations: - // - --help input, when a required flag is present, shows the help message + // - --help input, when a required flag is present, shows the help message // - --help input, when a required flag is present, explicitly does not error // - --help input, when a required flag is present, explicitly does not show the flag error message testCase: "help_input_with_required_flag", @@ -921,7 +921,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }, { // expectations: - // - optional input, when a required flag is present, shows the help message + // - optional input, when a required flag is present, shows the help message // - optional input, when a required flag is present, errors // - optional input, when a required flag is present, shows the flag error message testCase: "optional_input_with_required_flag", From f00f35ce8c1a6ebd7a3900901dd8b05049fbefc7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 13 Jul 2019 14:02:45 -0700 Subject: [PATCH 087/215] docs --- app_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app_test.go b/app_test.go index 6b44654..e838d30 100644 --- a/app_test.go +++ b/app_test.go @@ -877,6 +877,10 @@ func TestAppNoHelpFlag(t *testing.T) { } } +// TestRequiredFlagAppRunBehavior tests the app-wide behavior of required flags +// and how they interact with the error response and help messages. +// A different test (`TestCheckRequiredFlags`) exists for the more fine grain +// behavior of required flags. func TestRequiredFlagAppRunBehavior(t *testing.T) { tdata := []struct { testCase string From 9293f5b3cc6f5a96a1976bf2f810c957044c5ee8 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sun, 14 Jul 2019 21:00:16 -0700 Subject: [PATCH 088/215] visually shorten logic --- context.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/context.go b/context.go index 600307a..8caf90d 100644 --- a/context.go +++ b/context.go @@ -294,12 +294,10 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { }) for _, f := range flags { - if rf, ok := f.(RequiredFlag); ok { - if rf.IsRequired() { - key := strings.Split(f.GetName(), ",")[0] - if !visited[key] { - return fmt.Errorf("Required flag %q not set", f.GetName()) - } + if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { + key := strings.Split(f.GetName(), ",")[0] + if !visited[key] { + return fmt.Errorf("Required flag %q not set", f.GetName()) } } } From cdc7af744e07ac8dbb34793f8b392af46ba443f7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 17 Jul 2019 00:16:40 -0700 Subject: [PATCH 089/215] add handling for multiple required flags --- context.go | 21 ++++++++++++++++++++- context_test.go | 23 ++++++++++++++++------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/context.go b/context.go index 8caf90d..383749d 100644 --- a/context.go +++ b/context.go @@ -293,13 +293,32 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { visited[f.Name] = true }) + var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { key := strings.Split(f.GetName(), ",")[0] if !visited[key] { - return fmt.Errorf("Required flag %q not set", f.GetName()) + missingFlags = append(missingFlags, f.GetName()) } } } + + numberOfMissingFlags := len(missingFlags) + if numberOfMissingFlags == 1 { + return fmt.Errorf("Required flag %q not set", missingFlags[0]) + } + if numberOfMissingFlags >= 2 { + var missingFlagsOutput string + for idx, f := range missingFlags { + // if not the last item, append with a ", " + if idx != numberOfMissingFlags-1 { + missingFlagsOutput = fmt.Sprintf("%s%s, ", missingFlagsOutput, f) + } else { + missingFlagsOutput = fmt.Sprintf("%s%s", missingFlagsOutput, f) + } + } + return fmt.Errorf("Required flags %q not set", missingFlagsOutput) + } + return nil } diff --git a/context_test.go b/context_test.go index fac1d4b..585ca82 100644 --- a/context_test.go +++ b/context_test.go @@ -3,6 +3,7 @@ package cli import ( "flag" "os" + "strings" "testing" "time" ) @@ -404,10 +405,11 @@ func TestContext_GlobalSet(t *testing.T) { func TestCheckRequiredFlags(t *testing.T) { tdata := []struct { - testCase string - parseInput []string - flags []Flag - expectedAnError bool + testCase string + parseInput []string + flags []Flag + expectedAnError bool + expectedErrorContents []string }{ { testCase: "empty", @@ -423,7 +425,8 @@ func TestCheckRequiredFlags(t *testing.T) { flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, }, - expectedAnError: true, + expectedAnError: true, + expectedErrorContents: []string{"requiredFlag"}, }, { testCase: "required_and_present", @@ -460,10 +463,11 @@ func TestCheckRequiredFlags(t *testing.T) { { testCase: "two_required", flags: []Flag{ - StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "requiredFlagOne", Required: true}, StringFlag{Name: "requiredFlagTwo", Required: true}, }, - expectedAnError: true, + expectedAnError: true, + expectedErrorContents: []string{"requiredFlagOne", "requiredFlagTwo"}, }, { testCase: "two_required_and_one_present", @@ -502,6 +506,11 @@ func TestCheckRequiredFlags(t *testing.T) { if !test.expectedAnError && err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } + for _, errString := range test.expectedErrorContents { + if !strings.Contains(err.Error(), errString) { + t.Errorf("expected error %q to contain %q, but it didn't!", err.Error(), errString) + } + } }) } } From 01d5cfab7066912c97eeaf94cbbda8f90fc490f7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 17 Jul 2019 00:20:44 -0700 Subject: [PATCH 090/215] use strings.Join --- context.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/context.go b/context.go index 383749d..91a9575 100644 --- a/context.go +++ b/context.go @@ -308,16 +308,8 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { return fmt.Errorf("Required flag %q not set", missingFlags[0]) } if numberOfMissingFlags >= 2 { - var missingFlagsOutput string - for idx, f := range missingFlags { - // if not the last item, append with a ", " - if idx != numberOfMissingFlags-1 { - missingFlagsOutput = fmt.Sprintf("%s%s, ", missingFlagsOutput, f) - } else { - missingFlagsOutput = fmt.Sprintf("%s%s", missingFlagsOutput, f) - } - } - return fmt.Errorf("Required flags %q not set", missingFlagsOutput) + joinedMissingFlags := strings.Join(missingFlags, ", ") + return fmt.Errorf("Required flags %q not set", joinedMissingFlags) } return nil From 32d84d8e870a7f475c228c3c58f2c879f6a4009e Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Wed, 17 Jul 2019 00:25:13 -0700 Subject: [PATCH 091/215] copy update --- flag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flag.go b/flag.go index 71fd729..afb0fed 100644 --- a/flag.go +++ b/flag.go @@ -75,8 +75,8 @@ type Flag interface { GetName() string } -// RequiredFlag is an interface that allows us to return mark flags as required -// it allows flags defined in this library to be marked as required in a backwards compatible fashion +// RequiredFlag is an interface that allows us to mark flags as required +// it allows flags required flags to be backwards compatible with the Flag interface type RequiredFlag interface { Flag From cc1cf8c459c947156bb429ef319f4cf762b1e468 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 18 Jul 2019 00:09:07 -0700 Subject: [PATCH 092/215] wording shift --- app_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app_test.go b/app_test.go index e838d30..5f32483 100644 --- a/app_test.go +++ b/app_test.go @@ -913,9 +913,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }, { // expectations: - // - --help input, when a required flag is present, shows the help message - // - --help input, when a required flag is present, explicitly does not error - // - --help input, when a required flag is present, explicitly does not show the flag error message + // - inputing --help, when a required flag is present, shows the help message + // - inputing --help, when a required flag is present, explicitly does not error + // - inputing --help, when a required flag is present, explicitly does not show the flag error message testCase: "help_input_with_required_flag", appRunInput: []string{"command", "--help"}, flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, @@ -925,9 +925,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }, { // expectations: - // - optional input, when a required flag is present, shows the help message - // - optional input, when a required flag is present, errors - // - optional input, when a required flag is present, shows the flag error message + // - giving optional input, when a required flag is present, shows the help message + // - giving optional input, when a required flag is present, errors + // - giving optional input, when a required flag is present, shows the flag error message testCase: "optional_input_with_required_flag", appRunInput: []string{"command", "--optional", "cats"}, flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, From 300288670fe7713da8ae6e4a449d12e6c911b713 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 18 Jul 2019 00:20:32 -0700 Subject: [PATCH 093/215] add subcommand --- app_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app_test.go b/app_test.go index 5f32483..28c4afc 100644 --- a/app_test.go +++ b/app_test.go @@ -962,6 +962,18 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // setup - app app := NewApp() app.Flags = test.flags + app.Commands = []Command{ + Command{ + Name: "command", + Flags: test.flags, + Subcommands: []Command{ + Command{ + Name: "subcommand", + Flags: test.flags, + }, + }, + }, + } // logic under test err := app.Run(test.appRunInput) From 2299852c3c3512dafac738a10847da3bb3699b62 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 18 Jul 2019 00:47:18 -0700 Subject: [PATCH 094/215] cleanup subcommand and specs --- app.go | 4 ++-- app_test.go | 17 +---------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/app.go b/app.go index 52c2d85..485c92b 100644 --- a/app.go +++ b/app.go @@ -327,7 +327,6 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { set.SetOutput(ioutil.Discard) err = set.Parse(ctx.Args().Tail()) nerr := normalizeFlags(a.Flags, set) - cerr := checkRequiredFlags(a.Flags, set) context := NewContext(a, set, ctx) if nerr != nil { @@ -341,8 +340,9 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { return nerr } + cerr := checkRequiredFlags(a.Flags, set) if cerr != nil { - ShowAppHelp(context) + ShowSubcommandHelp(context) return cerr } diff --git a/app_test.go b/app_test.go index 28c4afc..660d238 100644 --- a/app_test.go +++ b/app_test.go @@ -890,22 +890,13 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { expectedAnError bool expectedFlagErrorPrinter bool }{ - { - // expectations: - // - empty input shows the help message - // - empty input explicitly does not error - testCase: "empty_input", - appRunInput: []string{""}, - expectedHelpPrinter: true, - expectedAnError: false, // explicit false for readability (this is the default value) - }, { // expectations: // - empty input, when a required flag is present, shows the help message // - empty input, when a required flag is present, errors // - empty input, when a required flag is present, show the flag error message testCase: "empty_input_with_required_flag", - appRunInput: []string{""}, + appRunInput: []string{"command"}, flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, expectedHelpPrinter: true, expectedAnError: true, @@ -966,12 +957,6 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { Command{ Name: "command", Flags: test.flags, - Subcommands: []Command{ - Command{ - Name: "subcommand", - Flags: test.flags, - }, - }, }, } From 19140e1fb52f458727a3c718f82fb93861d5849c Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 18 Jul 2019 00:48:09 -0700 Subject: [PATCH 095/215] show errors --- app.go | 1 + 1 file changed, 1 insertion(+) diff --git a/app.go b/app.go index 485c92b..44d6645 100644 --- a/app.go +++ b/app.go @@ -342,6 +342,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { cerr := checkRequiredFlags(a.Flags, set) if cerr != nil { + showFlagError(a.Writer, cerr) ShowSubcommandHelp(context) return cerr } From d8985dc6d56ac75b35f0422d8efbc04814bf17f3 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 18 Jul 2019 00:51:16 -0700 Subject: [PATCH 096/215] reduce diff --- app.go | 1 - 1 file changed, 1 deletion(-) diff --git a/app.go b/app.go index 44d6645..6fc0a42 100644 --- a/app.go +++ b/app.go @@ -201,7 +201,6 @@ func (a *App) Run(arguments []string) (err error) { err = set.Parse(arguments[1:]) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) - if nerr != nil { fmt.Fprintln(a.Writer, nerr) ShowAppHelp(context) From 7ce0af189ed431005f47e583a63648ea9a0a99ea Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 18 Jul 2019 00:52:24 -0700 Subject: [PATCH 097/215] remove unused code --- flag.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/flag.go b/flag.go index afb0fed..d98c808 100644 --- a/flag.go +++ b/flag.go @@ -649,18 +649,6 @@ func withEnvHint(envVar, str string) string { return str + envText } -func withRequiredHint(isRequired bool, str string) string { - if isRequired { - return str + " (required)" - } - - return str -} - -func withHints(envVar string, isRequired bool, str string) string { - return withRequiredHint(isRequired, withEnvHint(envVar, str)) -} - func withFileHint(filePath, str string) string { fileText := "" if filePath != "" { From d79d2a04242b21441061e00475287f4b826614f8 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Wed, 24 Jul 2019 16:08:47 +0200 Subject: [PATCH 098/215] Fix issue with source command completion Avoid competion for bash builtin `source` and fallback to default implementation as it throws below error ``` -bash: source: --: invalid option source: usage: source filename [arguments] ``` --- autocomplete/bash_autocomplete | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index 303d126..f5a8d1f 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -3,6 +3,7 @@ : ${PROG:=$(basename ${BASH_SOURCE})} _cli_bash_autocomplete() { + if [[ "${COMP_WORDS[@]:0:$COMP_CWORD}" != "source" ]]; then local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" @@ -13,8 +14,8 @@ _cli_bash_autocomplete() { fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 + fi } -complete -F _cli_bash_autocomplete $PROG - +complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG unset PROG From 23f09ac1e82395dc1a70c36d649ab03929e32d79 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sun, 28 Jul 2019 22:19:35 -0700 Subject: [PATCH 099/215] cleanup tests, check required flags in more places --- app.go | 14 +++++------ app_test.go | 70 ++++++++++++++++++----------------------------------- command.go | 7 ++++++ 3 files changed, 38 insertions(+), 53 deletions(-) diff --git a/app.go b/app.go index 6fc0a42..c286327 100644 --- a/app.go +++ b/app.go @@ -339,13 +339,6 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { return nerr } - cerr := checkRequiredFlags(a.Flags, set) - if cerr != nil { - showFlagError(a.Writer, cerr) - ShowSubcommandHelp(context) - return cerr - } - if checkCompletions(context) { return nil } @@ -371,6 +364,13 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } } + cerr := checkRequiredFlags(a.Flags, set) + if cerr != nil { + showFlagError(a.Writer, cerr) + ShowSubcommandHelp(context) + return cerr + } + if a.After != nil { defer func() { afterErr := a.After(context) diff --git a/app_test.go b/app_test.go index 660d238..cd4b1ef 100644 --- a/app_test.go +++ b/app_test.go @@ -877,54 +877,38 @@ func TestAppNoHelpFlag(t *testing.T) { } } -// TestRequiredFlagAppRunBehavior tests the app-wide behavior of required flags -// and how they interact with the error response and help messages. -// A different test (`TestCheckRequiredFlags`) exists for the more fine grain -// behavior of required flags. func TestRequiredFlagAppRunBehavior(t *testing.T) { tdata := []struct { - testCase string - flags []Flag - appRunInput []string - expectedHelpPrinter bool - expectedAnError bool - expectedFlagErrorPrinter bool + testCase string + flags []Flag + appRunInput []string + expectedAnError bool }{ { // expectations: // - empty input, when a required flag is present, shows the help message - // - empty input, when a required flag is present, errors - // - empty input, when a required flag is present, show the flag error message - testCase: "empty_input_with_required_flag", - appRunInput: []string{"command"}, - flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, - expectedHelpPrinter: true, - expectedAnError: true, - expectedFlagErrorPrinter: true, + // - empty input, when a required flag is present, errors and shows the flag error message + testCase: "empty_input_with_required_flag", + appRunInput: []string{"command"}, + flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + expectedAnError: true, }, { // expectations: // - inputing --help, when a required flag is present, shows the help message - // - inputing --help, when a required flag is present, explicitly does not error - // - inputing --help, when a required flag is present, explicitly does not show the flag error message - testCase: "help_input_with_required_flag", - appRunInput: []string{"command", "--help"}, - flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, - expectedHelpPrinter: true, - expectedAnError: false, // explicit false for readability (this is the default value) - expectedFlagErrorPrinter: false, // explicit false for readability (this is the default value) + // - inputing --help, when a required flag is present, does not error + testCase: "help_input_with_required_flag", + appRunInput: []string{"command", "--help"}, + flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, { // expectations: // - giving optional input, when a required flag is present, shows the help message - // - giving optional input, when a required flag is present, errors - // - giving optional input, when a required flag is present, shows the flag error message - testCase: "optional_input_with_required_flag", - appRunInput: []string{"command", "--optional", "cats"}, - flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, - expectedHelpPrinter: true, - expectedAnError: true, - expectedFlagErrorPrinter: true, + // - giving optional input, when a required flag is present, errors and shows the flag error message + testCase: "optional_input_with_required_flag", + appRunInput: []string{"command", "--optional", "cats"}, + flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, + expectedAnError: true, }, } for _, test := range tdata { @@ -935,9 +919,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { showFlagError = oldShowError }() // setup - mock showFlagError - var showErrorWasCalled = false + var showFlagErrorWasCalled = false showFlagError = func(writer io.Writer, err ...interface{}) (int, error) { - showErrorWasCalled = true + showFlagErrorWasCalled = true return 0, nil } // setup - undo HelpPrinter mock when finished @@ -953,26 +937,20 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // setup - app app := NewApp() app.Flags = test.flags - app.Commands = []Command{ - Command{ - Name: "command", - Flags: test.flags, - }, - } // logic under test err := app.Run(test.appRunInput) // assertions - if test.expectedHelpPrinter && helpPrinterWasCalled == false { + if helpPrinterWasCalled == false { t.Errorf("HelpPrinter expected to be called, but was not") } - if test.expectedFlagErrorPrinter && showErrorWasCalled == false { - t.Errorf("showFlagError expected to be called, but was not") - } if test.expectedAnError && err == nil { t.Errorf("expected an error, but there was none") } + if test.expectedAnError && showFlagErrorWasCalled == false { + t.Errorf("showFlagError expected to be called, but was not") + } if !test.expectedAnError && err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } diff --git a/command.go b/command.go index 3d44404..a6b19b5 100644 --- a/command.go +++ b/command.go @@ -135,6 +135,13 @@ func (c Command) Run(ctx *Context) (err error) { return nil } + cerr := checkRequiredFlags(c.Flags, set) + if cerr != nil { + showFlagError(context.App.Writer, cerr) + ShowCommandHelp(context, c.Name) + return cerr + } + if c.After != nil { defer func() { afterErr := c.After(context) From 9ec594d5290b846de59b0bc350849b848cabfbd7 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sun, 28 Jul 2019 22:34:07 -0700 Subject: [PATCH 100/215] reset generated flags changes --- flag_generated.go | 78 --------------------------------------------- generate-flag-types | 6 ---- 2 files changed, 84 deletions(-) diff --git a/flag_generated.go b/flag_generated.go index a3e4d6e..001576c 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -14,7 +14,6 @@ type BoolFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Destination *bool } @@ -30,11 +29,6 @@ func (f BoolFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f BoolFlag) IsRequired() bool { - return f.Required -} - // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { @@ -68,7 +62,6 @@ type BoolTFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Destination *bool } @@ -84,11 +77,6 @@ func (f BoolTFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f BoolTFlag) IsRequired() bool { - return f.Required -} - // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { @@ -122,7 +110,6 @@ type DurationFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value time.Duration Destination *time.Duration @@ -139,11 +126,6 @@ func (f DurationFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f DurationFlag) IsRequired() bool { - return f.Required -} - // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { @@ -177,7 +159,6 @@ type Float64Flag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value float64 Destination *float64 @@ -194,11 +175,6 @@ func (f Float64Flag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f Float64Flag) IsRequired() bool { - return f.Required -} - // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { @@ -232,7 +208,6 @@ type GenericFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value Generic } @@ -248,11 +223,6 @@ func (f GenericFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f GenericFlag) IsRequired() bool { - return f.Required -} - // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { @@ -286,7 +256,6 @@ type Int64Flag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value int64 Destination *int64 @@ -303,11 +272,6 @@ func (f Int64Flag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f Int64Flag) IsRequired() bool { - return f.Required -} - // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { @@ -341,7 +305,6 @@ type IntFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value int Destination *int @@ -358,11 +321,6 @@ func (f IntFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f IntFlag) IsRequired() bool { - return f.Required -} - // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { @@ -396,7 +354,6 @@ type IntSliceFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value *IntSlice } @@ -412,11 +369,6 @@ func (f IntSliceFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f IntSliceFlag) IsRequired() bool { - return f.Required -} - // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { @@ -450,7 +402,6 @@ type Int64SliceFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value *Int64Slice } @@ -466,11 +417,6 @@ func (f Int64SliceFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f Int64SliceFlag) IsRequired() bool { - return f.Required -} - // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { @@ -504,7 +450,6 @@ type StringFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value string Destination *string @@ -521,11 +466,6 @@ func (f StringFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f StringFlag) IsRequired() bool { - return f.Required -} - // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { @@ -559,7 +499,6 @@ type StringSliceFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value *StringSlice } @@ -575,11 +514,6 @@ func (f StringSliceFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f StringSliceFlag) IsRequired() bool { - return f.Required -} - // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { @@ -613,7 +547,6 @@ type Uint64Flag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value uint64 Destination *uint64 @@ -630,11 +563,6 @@ func (f Uint64Flag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f Uint64Flag) IsRequired() bool { - return f.Required -} - // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { @@ -668,7 +596,6 @@ type UintFlag struct { Usage string EnvVar string FilePath string - Required bool Hidden bool Value uint Destination *uint @@ -685,11 +612,6 @@ func (f UintFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required -func (f UintFlag) IsRequired() bool { - return f.Required -} - // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { diff --git a/generate-flag-types b/generate-flag-types index e0b5aa1..1358857 100755 --- a/generate-flag-types +++ b/generate-flag-types @@ -143,7 +143,6 @@ def _write_cli_flag_types(outfile, types): Usage string EnvVar string FilePath string - Required bool Hidden bool """.format(**typedef)) @@ -171,11 +170,6 @@ def _write_cli_flag_types(outfile, types): return f.Name }} - // IsRequired returns the whether or not the flag is required - func (f {name}Flag) IsRequired() bool {{ - return f.Required - }} - // {name} looks up the value of a local {name}Flag, returns // {context_default} if not found func (c *Context) {name}(name string) {context_type} {{ From 386b379d1950e8939c8a3dbba0335cf79903f421 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sun, 28 Jul 2019 22:45:43 -0700 Subject: [PATCH 101/215] Revert "reset generated flags changes" This reverts commit 9ec594d5290b846de59b0bc350849b848cabfbd7. --- flag_generated.go | 78 +++++++++++++++++++++++++++++++++++++++++++++ generate-flag-types | 6 ++++ 2 files changed, 84 insertions(+) diff --git a/flag_generated.go b/flag_generated.go index 001576c..a3e4d6e 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -14,6 +14,7 @@ type BoolFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Destination *bool } @@ -29,6 +30,11 @@ func (f BoolFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f BoolFlag) IsRequired() bool { + return f.Required +} + // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { @@ -62,6 +68,7 @@ type BoolTFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Destination *bool } @@ -77,6 +84,11 @@ func (f BoolTFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f BoolTFlag) IsRequired() bool { + return f.Required +} + // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { @@ -110,6 +122,7 @@ type DurationFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value time.Duration Destination *time.Duration @@ -126,6 +139,11 @@ func (f DurationFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f DurationFlag) IsRequired() bool { + return f.Required +} + // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { @@ -159,6 +177,7 @@ type Float64Flag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value float64 Destination *float64 @@ -175,6 +194,11 @@ func (f Float64Flag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Float64Flag) IsRequired() bool { + return f.Required +} + // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { @@ -208,6 +232,7 @@ type GenericFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value Generic } @@ -223,6 +248,11 @@ func (f GenericFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f GenericFlag) IsRequired() bool { + return f.Required +} + // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { @@ -256,6 +286,7 @@ type Int64Flag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value int64 Destination *int64 @@ -272,6 +303,11 @@ func (f Int64Flag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Int64Flag) IsRequired() bool { + return f.Required +} + // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { @@ -305,6 +341,7 @@ type IntFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value int Destination *int @@ -321,6 +358,11 @@ func (f IntFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f IntFlag) IsRequired() bool { + return f.Required +} + // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { @@ -354,6 +396,7 @@ type IntSliceFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value *IntSlice } @@ -369,6 +412,11 @@ func (f IntSliceFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f IntSliceFlag) IsRequired() bool { + return f.Required +} + // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { @@ -402,6 +450,7 @@ type Int64SliceFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value *Int64Slice } @@ -417,6 +466,11 @@ func (f Int64SliceFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Int64SliceFlag) IsRequired() bool { + return f.Required +} + // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { @@ -450,6 +504,7 @@ type StringFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value string Destination *string @@ -466,6 +521,11 @@ func (f StringFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f StringFlag) IsRequired() bool { + return f.Required +} + // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { @@ -499,6 +559,7 @@ type StringSliceFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value *StringSlice } @@ -514,6 +575,11 @@ func (f StringSliceFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f StringSliceFlag) IsRequired() bool { + return f.Required +} + // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { @@ -547,6 +613,7 @@ type Uint64Flag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value uint64 Destination *uint64 @@ -563,6 +630,11 @@ func (f Uint64Flag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f Uint64Flag) IsRequired() bool { + return f.Required +} + // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { @@ -596,6 +668,7 @@ type UintFlag struct { Usage string EnvVar string FilePath string + Required bool Hidden bool Value uint Destination *uint @@ -612,6 +685,11 @@ func (f UintFlag) GetName() string { return f.Name } +// IsRequired returns the whether or not the flag is required +func (f UintFlag) IsRequired() bool { + return f.Required +} + // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { diff --git a/generate-flag-types b/generate-flag-types index 1358857..e0b5aa1 100755 --- a/generate-flag-types +++ b/generate-flag-types @@ -143,6 +143,7 @@ def _write_cli_flag_types(outfile, types): Usage string EnvVar string FilePath string + Required bool Hidden bool """.format(**typedef)) @@ -170,6 +171,11 @@ def _write_cli_flag_types(outfile, types): return f.Name }} + // IsRequired returns the whether or not the flag is required + func (f {name}Flag) IsRequired() bool {{ + return f.Required + }} + // {name} looks up the value of a local {name}Flag, returns // {context_default} if not found func (c *Context) {name}(name string) {context_type} {{ From 9438aba3b89e7053070ef277121a14e5fb95947e Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 19:54:57 -0700 Subject: [PATCH 102/215] remove showFlagError, we can use the help printer assertion to accomplish the same goal --- app.go | 6 ++---- app_test.go | 14 -------------- command.go | 4 ++-- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/app.go b/app.go index c286327..6557044 100644 --- a/app.go +++ b/app.go @@ -23,8 +23,6 @@ var ( errInvalidActionType = NewExitError("ERROR invalid Action type. "+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("See %s", appActionDeprecationURL), 2) - - showFlagError printerFunc = fmt.Fprintln ) // App is the main structure of a cli application. It is recommended that @@ -235,7 +233,7 @@ func (a *App) Run(arguments []string) (err error) { cerr := checkRequiredFlags(a.Flags, set) if cerr != nil { - showFlagError(a.Writer, cerr) + fmt.Fprintln(a.Writer, cerr) ShowAppHelp(context) return cerr } @@ -366,7 +364,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { cerr := checkRequiredFlags(a.Flags, set) if cerr != nil { - showFlagError(a.Writer, cerr) + fmt.Fprintln(a.Writer, cerr) ShowSubcommandHelp(context) return cerr } diff --git a/app_test.go b/app_test.go index cd4b1ef..36f8a72 100644 --- a/app_test.go +++ b/app_test.go @@ -913,17 +913,6 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { - // setup - undo showFlagError mock when finished - oldShowError := showFlagError - defer func() { - showFlagError = oldShowError - }() - // setup - mock showFlagError - var showFlagErrorWasCalled = false - showFlagError = func(writer io.Writer, err ...interface{}) (int, error) { - showFlagErrorWasCalled = true - return 0, nil - } // setup - undo HelpPrinter mock when finished oldPrinter := HelpPrinter defer func() { @@ -948,9 +937,6 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { if test.expectedAnError && err == nil { t.Errorf("expected an error, but there was none") } - if test.expectedAnError && showFlagErrorWasCalled == false { - t.Errorf("showFlagError expected to be called, but was not") - } if !test.expectedAnError && err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } diff --git a/command.go b/command.go index a6b19b5..fdec3c9 100644 --- a/command.go +++ b/command.go @@ -137,7 +137,7 @@ func (c Command) Run(ctx *Context) (err error) { cerr := checkRequiredFlags(c.Flags, set) if cerr != nil { - showFlagError(context.App.Writer, cerr) + fmt.Fprintln(context.App.Writer, cerr) ShowCommandHelp(context, c.Name) return cerr } @@ -273,7 +273,7 @@ func reorderArgs(args []string) []string { } func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { - allCharsFlags := func (s string) bool { + allCharsFlags := func(s string) bool { for i := range s { f := set.Lookup(string(s[i])) if f == nil { From 714a73f028fa5a3c5b6512bf5e55b94be388de8f Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 19:57:14 -0700 Subject: [PATCH 103/215] remove unused thing --- app.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/app.go b/app.go index 6557044..80989aa 100644 --- a/app.go +++ b/app.go @@ -10,9 +10,6 @@ import ( "time" ) -// printerFunc is the function signature for fmt.Fprintln -type printerFunc func(io.Writer, ...interface{}) (int, error) - var ( changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) From 95d3a8624d8aa6661831d0009550b602458fcb4d Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 20:27:51 -0700 Subject: [PATCH 104/215] update test to reflect app flag usage --- app_test.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app_test.go b/app_test.go index 36f8a72..bbd1426 100644 --- a/app_test.go +++ b/app_test.go @@ -880,7 +880,7 @@ func TestAppNoHelpFlag(t *testing.T) { func TestRequiredFlagAppRunBehavior(t *testing.T) { tdata := []struct { testCase string - flags []Flag + appFlags []Flag appRunInput []string expectedAnError bool }{ @@ -889,8 +889,8 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // - empty input, when a required flag is present, shows the help message // - empty input, when a required flag is present, errors and shows the flag error message testCase: "empty_input_with_required_flag", - appRunInput: []string{"command"}, - flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + appRunInput: []string{"myCLI"}, + appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, expectedAnError: true, }, { @@ -898,18 +898,23 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // - inputing --help, when a required flag is present, shows the help message // - inputing --help, when a required flag is present, does not error testCase: "help_input_with_required_flag", - appRunInput: []string{"command", "--help"}, - flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + appRunInput: []string{"myCLI", "--help"}, + appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, { // expectations: // - giving optional input, when a required flag is present, shows the help message // - giving optional input, when a required flag is present, errors and shows the flag error message testCase: "optional_input_with_required_flag", - appRunInput: []string{"command", "--optional", "cats"}, - flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, + appRunInput: []string{"myCLI", "--optional", "cats"}, + appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, expectedAnError: true, }, + { + testCase: "required_flag_input", + appRunInput: []string{"myCLI", "--requiredFlag", "cats"}, + appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { @@ -925,7 +930,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { } // setup - app app := NewApp() - app.Flags = test.flags + app.Flags = test.appFlags // logic under test err := app.Run(test.appRunInput) From 7b9e16b6b5255803ea279fe1ee0e41973f49a42e Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 20:30:43 -0700 Subject: [PATCH 105/215] update test names --- app_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app_test.go b/app_test.go index bbd1426..6f36ef9 100644 --- a/app_test.go +++ b/app_test.go @@ -882,13 +882,14 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { testCase string appFlags []Flag appRunInput []string + appCommands []Command expectedAnError bool }{ { // expectations: // - empty input, when a required flag is present, shows the help message // - empty input, when a required flag is present, errors and shows the flag error message - testCase: "empty_input_with_required_flag", + testCase: "error_case_empty_input_with_required_flag", appRunInput: []string{"myCLI"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, expectedAnError: true, @@ -897,7 +898,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // expectations: // - inputing --help, when a required flag is present, shows the help message // - inputing --help, when a required flag is present, does not error - testCase: "help_input_with_required_flag", + testCase: "valid_case_help_input_with_required_flag", appRunInput: []string{"myCLI", "--help"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, @@ -905,13 +906,13 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // expectations: // - giving optional input, when a required flag is present, shows the help message // - giving optional input, when a required flag is present, errors and shows the flag error message - testCase: "optional_input_with_required_flag", + testCase: "error_case_optional_input_with_required_flag", appRunInput: []string{"myCLI", "--optional", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, expectedAnError: true, }, { - testCase: "required_flag_input", + testCase: "valid_case_required_flag_input", appRunInput: []string{"myCLI", "--requiredFlag", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, @@ -931,6 +932,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // setup - app app := NewApp() app.Flags = test.appFlags + app.Commands = test.appCommands // logic under test err := app.Run(test.appRunInput) From 3d6eec825ac768894a385ca3c3156a7905c27ce3 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 20:35:23 -0700 Subject: [PATCH 106/215] add test cases --- app_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app_test.go b/app_test.go index 6f36ef9..63f0fc4 100644 --- a/app_test.go +++ b/app_test.go @@ -916,6 +916,25 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { appRunInput: []string{"myCLI", "--requiredFlag", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, + { + testCase: "valid_case_required_flag_input_command", + appRunInput: []string{"myCLI", "myCommand", "--requiredFlag", "cats"}, + appCommands: []Command{Command{ + Name: "myCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + }}, + }, + { + testCase: "valid_case_required_flag_input_subcommand", + appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--requiredFlag", "cats"}, + appCommands: []Command{Command{ + Name: "myCommand", + Subcommands: []Command{Command{ + Name: "mySubCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + }}, + }}, + }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { From 595382c50970039261f765043aee4c647aeccbd5 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 20:39:37 -0700 Subject: [PATCH 107/215] expand test cases --- app_test.go | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/app_test.go b/app_test.go index 63f0fc4..f785a44 100644 --- a/app_test.go +++ b/app_test.go @@ -885,32 +885,50 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { appCommands []Command expectedAnError bool }{ + // expectations: + // - empty input, when a required flag is present, errors and shows the flag error message { - // expectations: - // - empty input, when a required flag is present, shows the help message - // - empty input, when a required flag is present, errors and shows the flag error message testCase: "error_case_empty_input_with_required_flag", appRunInput: []string{"myCLI"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, expectedAnError: true, }, + // expectations: + // - inputing --help, when a required flag is present, does not error { - // expectations: - // - inputing --help, when a required flag is present, shows the help message - // - inputing --help, when a required flag is present, does not error testCase: "valid_case_help_input_with_required_flag", appRunInput: []string{"myCLI", "--help"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, { - // expectations: - // - giving optional input, when a required flag is present, shows the help message - // - giving optional input, when a required flag is present, errors and shows the flag error message + testCase: "valid_case_help_input_with_required_flag_command", + appRunInput: []string{"myCLI", "myCommand", "--help"}, + appCommands: []Command{Command{ + Name: "myCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + }}, + }, + { + testCase: "valid_case_help_input_with_required_flag_subcommand", + appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--help"}, + appCommands: []Command{Command{ + Name: "myCommand", + Subcommands: []Command{Command{ + Name: "mySubCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + }}, + }}, + }, + // expectations: + // - giving optional input, when a required flag is present, shows the help message + // - giving optional input, when a required flag is present, errors and shows the flag error message + { testCase: "error_case_optional_input_with_required_flag", appRunInput: []string{"myCLI", "--optional", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, expectedAnError: true, }, + // valid input cases { testCase: "valid_case_required_flag_input", appRunInput: []string{"myCLI", "--requiredFlag", "cats"}, From d4740d10d0cbde53a8e3132a0964464b2b50fc0b Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 20:58:08 -0700 Subject: [PATCH 108/215] more test cases --- app_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app_test.go b/app_test.go index f785a44..b469644 100644 --- a/app_test.go +++ b/app_test.go @@ -893,6 +893,27 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, expectedAnError: true, }, + { + testCase: "error_case_empty_input_with_required_flag", + appRunInput: []string{"myCLI", "myCommand"}, + appCommands: []Command{Command{ + Name: "myCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + }}, + expectedAnError: true, + }, + { + testCase: "error_case_empty_input_with_required_flag", + appRunInput: []string{"myCLI", "myCommand", "mySubCommand"}, + appCommands: []Command{Command{ + Name: "myCommand", + Subcommands: []Command{Command{ + Name: "mySubCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, + }}, + }}, + expectedAnError: true, + }, // expectations: // - inputing --help, when a required flag is present, does not error { From 78db152323afb7934f9f0dd207eeaf34147bb300 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 21:35:15 -0700 Subject: [PATCH 109/215] add typed error assertions --- app_test.go | 3 +++ context.go | 33 +++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app_test.go b/app_test.go index b469644..1e42f12 100644 --- a/app_test.go +++ b/app_test.go @@ -1002,6 +1002,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { if test.expectedAnError && err == nil { t.Errorf("expected an error, but there was none") } + if _, ok := err.(requiredFlagsErr); test.expectedAnError && !ok { + t.Errorf("expected a requiredFlagsErr, but got: %s", err) + } if !test.expectedAnError && err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } diff --git a/context.go b/context.go index 91a9575..498fd2a 100644 --- a/context.go +++ b/context.go @@ -287,7 +287,29 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error { return nil } -func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { +type requiredFlagsErr interface { + error + getMissingFlags() []string +} + +type errRequiredFlags struct { + missingFlags []string +} + +func (e *errRequiredFlags) Error() string { + numberOfMissingFlags := len(e.missingFlags) + if numberOfMissingFlags == 1 { + return fmt.Sprintf("Required flag %q not set", e.missingFlags[0]) + } + joinedMissingFlags := strings.Join(e.missingFlags, ", ") + return fmt.Sprintf("Required flags %q not set", joinedMissingFlags) +} + +func (e *errRequiredFlags) getMissingFlags() []string { + return e.missingFlags +} + +func checkRequiredFlags(flags []Flag, set *flag.FlagSet) requiredFlagsErr { visited := make(map[string]bool) set.Visit(func(f *flag.Flag) { visited[f.Name] = true @@ -303,13 +325,8 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) error { } } - numberOfMissingFlags := len(missingFlags) - if numberOfMissingFlags == 1 { - return fmt.Errorf("Required flag %q not set", missingFlags[0]) - } - if numberOfMissingFlags >= 2 { - joinedMissingFlags := strings.Join(missingFlags, ", ") - return fmt.Errorf("Required flags %q not set", joinedMissingFlags) + if len(missingFlags) != 0 { + return &errRequiredFlags{missingFlags: missingFlags} } return nil From 45f2b3d8e71e11822cf591f1c370f8587726c425 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 21:45:11 -0700 Subject: [PATCH 110/215] more test cases --- app_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app_test.go b/app_test.go index 1e42f12..465d3ce 100644 --- a/app_test.go +++ b/app_test.go @@ -949,6 +949,27 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, expectedAnError: true, }, + { + testCase: "error_case_optional_input_with_required_flag_command", + appRunInput: []string{"myCLI", "myCommand", "--optional", "cats"}, + appCommands: []Command{Command{ + Name: "myCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, + }}, + expectedAnError: true, + }, + { + testCase: "error_case_optional_input_with_required_flag_subcommand", + appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--optional", "cats"}, + appCommands: []Command{Command{ + Name: "myCommand", + Subcommands: []Command{Command{ + Name: "mySubCommand", + Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, + }}, + }}, + expectedAnError: true, + }, // valid input cases { testCase: "valid_case_required_flag_input", From ef9acb4a3b846728c98844f7f92964ae2a79f259 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 21:46:56 -0700 Subject: [PATCH 111/215] rename cases --- app_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app_test.go b/app_test.go index 465d3ce..854745f 100644 --- a/app_test.go +++ b/app_test.go @@ -888,13 +888,13 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // expectations: // - empty input, when a required flag is present, errors and shows the flag error message { - testCase: "error_case_empty_input_with_required_flag", + testCase: "error_case_empty_input_with_required_flag_on_app", appRunInput: []string{"myCLI"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, expectedAnError: true, }, { - testCase: "error_case_empty_input_with_required_flag", + testCase: "error_case_empty_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand"}, appCommands: []Command{Command{ Name: "myCommand", @@ -903,7 +903,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { expectedAnError: true, }, { - testCase: "error_case_empty_input_with_required_flag", + testCase: "error_case_empty_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand"}, appCommands: []Command{Command{ Name: "myCommand", @@ -917,12 +917,12 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // expectations: // - inputing --help, when a required flag is present, does not error { - testCase: "valid_case_help_input_with_required_flag", + testCase: "valid_case_help_input_with_required_flag_on_app", appRunInput: []string{"myCLI", "--help"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, { - testCase: "valid_case_help_input_with_required_flag_command", + testCase: "valid_case_help_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--help"}, appCommands: []Command{Command{ Name: "myCommand", @@ -930,7 +930,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }}, }, { - testCase: "valid_case_help_input_with_required_flag_subcommand", + testCase: "valid_case_help_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--help"}, appCommands: []Command{Command{ Name: "myCommand", @@ -944,13 +944,13 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { // - giving optional input, when a required flag is present, shows the help message // - giving optional input, when a required flag is present, errors and shows the flag error message { - testCase: "error_case_optional_input_with_required_flag", + testCase: "error_case_optional_input_with_required_flag_on_app", appRunInput: []string{"myCLI", "--optional", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, expectedAnError: true, }, { - testCase: "error_case_optional_input_with_required_flag_command", + testCase: "error_case_optional_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--optional", "cats"}, appCommands: []Command{Command{ Name: "myCommand", @@ -959,7 +959,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { expectedAnError: true, }, { - testCase: "error_case_optional_input_with_required_flag_subcommand", + testCase: "error_case_optional_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--optional", "cats"}, appCommands: []Command{Command{ Name: "myCommand", @@ -972,12 +972,12 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }, // valid input cases { - testCase: "valid_case_required_flag_input", + testCase: "valid_case_required_flag_input_on_app", appRunInput: []string{"myCLI", "--requiredFlag", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, { - testCase: "valid_case_required_flag_input_command", + testCase: "valid_case_required_flag_input_on_command", appRunInput: []string{"myCLI", "myCommand", "--requiredFlag", "cats"}, appCommands: []Command{Command{ Name: "myCommand", @@ -985,7 +985,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }}, }, { - testCase: "valid_case_required_flag_input_subcommand", + testCase: "valid_case_required_flag_input_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--requiredFlag", "cats"}, appCommands: []Command{Command{ Name: "myCommand", From fdd4d106912b363ccffa03eec51a56dd3a6a822b Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 21:48:52 -0700 Subject: [PATCH 112/215] update comments --- app_test.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app_test.go b/app_test.go index 854745f..658ca21 100644 --- a/app_test.go +++ b/app_test.go @@ -885,8 +885,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { appCommands []Command expectedAnError bool }{ - // expectations: - // - empty input, when a required flag is present, errors and shows the flag error message + // assertion: empty input, when a required flag is present, errors { testCase: "error_case_empty_input_with_required_flag_on_app", appRunInput: []string{"myCLI"}, @@ -914,8 +913,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }}, expectedAnError: true, }, - // expectations: - // - inputing --help, when a required flag is present, does not error + // assertion: inputing --help, when a required flag is present, does not error { testCase: "valid_case_help_input_with_required_flag_on_app", appRunInput: []string{"myCLI", "--help"}, @@ -940,9 +938,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }}, }}, }, - // expectations: - // - giving optional input, when a required flag is present, shows the help message - // - giving optional input, when a required flag is present, errors and shows the flag error message + // assertion: giving optional input, when a required flag is present, errors { testCase: "error_case_optional_input_with_required_flag_on_app", appRunInput: []string{"myCLI", "--optional", "cats"}, @@ -970,7 +966,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { }}, expectedAnError: true, }, - // valid input cases + // assertion: when a required flag is present, inputting that required flag does not error { testCase: "valid_case_required_flag_input_on_app", appRunInput: []string{"myCLI", "--requiredFlag", "cats"}, From f21b22dd904b638518d9ea321d718f219bd6593c Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 22:10:18 -0700 Subject: [PATCH 113/215] cleanup some issues with error display --- app.go | 2 -- command.go | 1 - context.go | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app.go b/app.go index 80989aa..051c801 100644 --- a/app.go +++ b/app.go @@ -230,7 +230,6 @@ func (a *App) Run(arguments []string) (err error) { cerr := checkRequiredFlags(a.Flags, set) if cerr != nil { - fmt.Fprintln(a.Writer, cerr) ShowAppHelp(context) return cerr } @@ -361,7 +360,6 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { cerr := checkRequiredFlags(a.Flags, set) if cerr != nil { - fmt.Fprintln(a.Writer, cerr) ShowSubcommandHelp(context) return cerr } diff --git a/command.go b/command.go index fdec3c9..cbf06bb 100644 --- a/command.go +++ b/command.go @@ -137,7 +137,6 @@ func (c Command) Run(ctx *Context) (err error) { cerr := checkRequiredFlags(c.Flags, set) if cerr != nil { - fmt.Fprintln(context.App.Writer, cerr) ShowCommandHelp(context, c.Name) return cerr } diff --git a/context.go b/context.go index 498fd2a..8af3264 100644 --- a/context.go +++ b/context.go @@ -320,7 +320,7 @@ func checkRequiredFlags(flags []Flag, set *flag.FlagSet) requiredFlagsErr { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { key := strings.Split(f.GetName(), ",")[0] if !visited[key] { - missingFlags = append(missingFlags, f.GetName()) + missingFlags = append(missingFlags, key) } } } From 38f9e1622d2d4a5e1a86afd8f8f9d6cbf0157816 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 22:52:21 -0700 Subject: [PATCH 114/215] add environment variable support :tada: --- app.go | 4 ++-- command.go | 2 +- context.go | 9 ++------- context_test.go | 4 +++- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app.go b/app.go index 051c801..9ed492f 100644 --- a/app.go +++ b/app.go @@ -228,7 +228,7 @@ func (a *App) Run(arguments []string) (err error) { return nil } - cerr := checkRequiredFlags(a.Flags, set) + cerr := checkRequiredFlags(a.Flags, context) if cerr != nil { ShowAppHelp(context) return cerr @@ -358,7 +358,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } } - cerr := checkRequiredFlags(a.Flags, set) + cerr := checkRequiredFlags(a.Flags, context) if cerr != nil { ShowSubcommandHelp(context) return cerr diff --git a/command.go b/command.go index cbf06bb..e3b57db 100644 --- a/command.go +++ b/command.go @@ -135,7 +135,7 @@ func (c Command) Run(ctx *Context) (err error) { return nil } - cerr := checkRequiredFlags(c.Flags, set) + cerr := checkRequiredFlags(c.Flags, context) if cerr != nil { ShowCommandHelp(context, c.Name) return cerr diff --git a/context.go b/context.go index 8af3264..3e516c8 100644 --- a/context.go +++ b/context.go @@ -309,17 +309,12 @@ func (e *errRequiredFlags) getMissingFlags() []string { return e.missingFlags } -func checkRequiredFlags(flags []Flag, set *flag.FlagSet) requiredFlagsErr { - visited := make(map[string]bool) - set.Visit(func(f *flag.Flag) { - visited[f.Name] = true - }) - +func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { key := strings.Split(f.GetName(), ",")[0] - if !visited[key] { + if !context.IsSet(key) { missingFlags = append(missingFlags, key) } } diff --git a/context_test.go b/context_test.go index 585ca82..f2fc250 100644 --- a/context_test.go +++ b/context_test.go @@ -495,9 +495,11 @@ func TestCheckRequiredFlags(t *testing.T) { flags.Apply(set) } set.Parse(test.parseInput) + ctx := &Context{} + context := NewContext(ctx.App, set, ctx) // logic under test - err := checkRequiredFlags(test.flags, set) + err := checkRequiredFlags(test.flags, context) // assertions if test.expectedAnError && err == nil { From f4128a02f3215e532dff0b96e21a8e2cb08389a1 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 22:54:15 -0700 Subject: [PATCH 115/215] Update command.go --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index e3b57db..f1ca02d 100644 --- a/command.go +++ b/command.go @@ -272,7 +272,7 @@ func reorderArgs(args []string) []string { } func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { - allCharsFlags := func(s string) bool { + allCharsFlags := func (s string) bool { for i := range s { f := set.Lookup(string(s[i])) if f == nil { From d7ec4e801357fa5ccfab53669a42f78fc1a69d39 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 23:26:43 -0700 Subject: [PATCH 116/215] add env var tests --- context_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/context_test.go b/context_test.go index f2fc250..47313e1 100644 --- a/context_test.go +++ b/context_test.go @@ -407,6 +407,7 @@ func TestCheckRequiredFlags(t *testing.T) { tdata := []struct { testCase string parseInput []string + envVarInput [2]string flags []Flag expectedAnError bool expectedErrorContents []string @@ -435,6 +436,13 @@ func TestCheckRequiredFlags(t *testing.T) { }, parseInput: []string{"--requiredFlag", "myinput"}, }, + { + testCase: "required_and_present_via_env_var", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true, EnvVar: "REQUIRED_FLAG"}, + }, + envVarInput: [2]string{"REQUIRED_FLAG", "true"}, + }, { testCase: "required_and_optional", flags: []Flag{ @@ -452,6 +460,15 @@ func TestCheckRequiredFlags(t *testing.T) { parseInput: []string{"--optionalFlag", "myinput"}, expectedAnError: true, }, + { + testCase: "required_and_optional_and_optional_present_via_env_var", + flags: []Flag{ + StringFlag{Name: "requiredFlag", Required: true}, + StringFlag{Name: "optionalFlag", EnvVar: "OPTIONAL_FLAG"}, + }, + envVarInput: [2]string{"OPTIONAL_FLAG", "true"}, + expectedAnError: true, + }, { testCase: "required_and_optional_and_required_present", flags: []Flag{ @@ -495,8 +512,13 @@ func TestCheckRequiredFlags(t *testing.T) { flags.Apply(set) } set.Parse(test.parseInput) + if test.envVarInput[0] != "" { + os.Clearenv() + os.Setenv(test.envVarInput[0], test.envVarInput[1]) + } ctx := &Context{} context := NewContext(ctx.App, set, ctx) + context.Command.Flags = test.flags // logic under test err := checkRequiredFlags(test.flags, context) From 60fb2972328d6a7487c6821a58a86d476167c2bd Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Thu, 1 Aug 2019 23:27:34 -0700 Subject: [PATCH 117/215] remove help assertion stuff --- app_test.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/app_test.go b/app_test.go index 658ca21..ad49464 100644 --- a/app_test.go +++ b/app_test.go @@ -994,17 +994,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { - // setup - undo HelpPrinter mock when finished - oldPrinter := HelpPrinter - defer func() { - HelpPrinter = oldPrinter - }() - // setup - mock HelpPrinter - var helpPrinterWasCalled = false - HelpPrinter = func(w io.Writer, template string, data interface{}) { - helpPrinterWasCalled = true - } - // setup - app + // setup app := NewApp() app.Flags = test.appFlags app.Commands = test.appCommands @@ -1013,9 +1003,6 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { err := app.Run(test.appRunInput) // assertions - if helpPrinterWasCalled == false { - t.Errorf("HelpPrinter expected to be called, but was not") - } if test.expectedAnError && err == nil { t.Errorf("expected an error, but there was none") } From c75a689f629137700e8a30651f95cc41cf12a6d1 Mon Sep 17 00:00:00 2001 From: Jordan Christiansen Date: Fri, 2 Aug 2019 14:28:57 -0500 Subject: [PATCH 118/215] Make exit code example more clear The purpose of this example is to show that you can exit with an error code if a flag is unspecified, but with the code as it is, the only way to cause a non-zero exit is by adding the flag `--ginger-crouton=false`, which is not explained in the example. In this new version of the example, running the command with no flag will exit with an error, and running it with the flag will exit normally. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6eb2996..0a9cd32 100644 --- a/README.md +++ b/README.md @@ -901,14 +901,14 @@ import ( func main() { app := cli.NewApp() app.Flags = []cli.Flag{ - cli.BoolTFlag{ + cli.BoolFlag{ Name: "ginger-crouton", - Usage: "is it in the soup?", + Usage: "Add ginger croutons to the soup", }, } app.Action = func(ctx *cli.Context) error { if !ctx.Bool("ginger-crouton") { - return cli.NewExitError("it is not in the soup", 86) + return cli.NewExitError("Ginger croutons are not in the soup", 86) } return nil } From fa51d00dc6fb57edca14295fde308401d7720e61 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Fri, 2 Aug 2019 18:06:15 -0700 Subject: [PATCH 119/215] Update maintainers for current reality --- MAINTAINERS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index f6bdd99..5b7a6ea 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1 +1,4 @@ - @meatballhat +- @lynncyrin +- @AudriusButkevicius +- @asahasrabuddhe From e8eac43d9d73e96f6b034f89770ab2fdbec2cb5b Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Fri, 2 Aug 2019 18:26:41 -0700 Subject: [PATCH 120/215] Update CHANGELOG.md --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401eae5..f7c4de0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ ## [Unreleased] +## 1.21.0 - 2019-08-02 + +### Fixed + +* + +### Changed + +* + +### Added + +* + ## 1.20.0 - 2017-08-10 ### Fixed @@ -407,7 +421,10 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD +[Unreleased]: https://github.com/urfave/cli/compare/v1.21.0...HEAD +[1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 +[1.20.0]: https://github.com/urfave/cli/compare/v1.19.0...v1.20.0 +[1.19.0]: https://github.com/urfave/cli/compare/v1.18.0...v1.19.0 [1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 [1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 From 8a7f65e05215a76a2246b7b42cb9c082b5eae483 Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Fri, 2 Aug 2019 19:30:41 -0700 Subject: [PATCH 121/215] Update CHANGELOG.md --- CHANGELOG.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c4de0..1e49d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,21 +4,28 @@ ## [Unreleased] -## 1.21.0 - 2019-08-02 +## [1.21.0] - 2019-08-02 ### Fixed -* +* Fix using "slice" flag types with `EnvVar` in [urfave/cli/pull/687](https://github.com/urfave/cli/pull/687) via [@joshuarubin](https://github.com/joshuarubin) +* Fix regression of `SkipFlagParsing` behavior in [urfave/cli/pull/697](https://github.com/urfave/cli/pull/697) via [@jszwedko](https://github.com/jszwedko) +* Fix handling `ShortOptions` and `SkipArgReorder` in [urfave/cli/pull/686](https://github.com/urfave/cli/pull/686) via [@baude](https://github.com/baude) +* Fix args reordering when bool flags are present in [urfave/cli/pull/712](https://github.com/urfave/cli/pull/712) via [@windler](https://github.com/windler) +* Fix parsing of short options in [urfave/cli/pull/758](https://github.com/urfave/cli/pull/758) via [@vrothberg](https://github.com/vrothberg) -### Changed - -* - -### Added +### Added / Changed -* +* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/) +* Cleaned up help output in [urfave/cli/pull/664](https://github.com/urfave/cli/pull/664) via [@maguro](https://github.com/maguro) +* Case is now considered when sorting strings in [urfave/cli/pull/676](https://github.com/urfave/cli/pull/676) via [@rliebz](https://github.com/rliebz) +* Backport JSON `InputSource` to v1 in [urfave/cli/pull/598](https://github.com/urfave/cli/pull/598) via [@jszwedko](https://github.com/jszwedko) +* Allow more customization of flag help strings in [urfave/cli/pull/661](https://github.com/urfave/cli/pull/661) via [@rliebz](https://github.com/rliebz) +* Allow custom `ExitError` handler function in [urfave/cli/pull/628](https://github.com/urfave/cli/pull/628) via [@phinnaeus](https://github.com/phinnaeus) +* Allow loading a variable from a file in [urfave/cli/pull/675](https://github.com/urfave/cli/pull/675) via [@jmccann](https://github.com/jmccann) +* Allow combining short bool names in [urfave/cli/pull/684](https://github.com/urfave/cli/pull/684) via [@baude](https://github.com/baude) -## 1.20.0 - 2017-08-10 +## [1.20.0] - 2017-08-10 ### Fixed @@ -423,7 +430,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. [Unreleased]: https://github.com/urfave/cli/compare/v1.21.0...HEAD [1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 -[1.20.0]: https://github.com/urfave/cli/compare/v1.19.0...v1.20.0 +[1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0 +[1.19.1]: https://github.com/urfave/cli/compare/v1.19.0...v1.19.1 [1.19.0]: https://github.com/urfave/cli/compare/v1.18.0...v1.19.0 [1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 From 4627bbe109fb902221de0a86f20048ad5679ea0c Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Sat, 3 Aug 2019 12:55:06 +0200 Subject: [PATCH 122/215] Add go module support This adds a go.{mod,sum} file to official support go modules. Signed-off-by: Sascha Grunert --- altsrc/flag.go | 2 +- altsrc/flag_generated.go | 2 +- altsrc/flag_test.go | 2 +- altsrc/input_source_context.go | 2 +- altsrc/json_command_test.go | 2 +- altsrc/json_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 +- go.mod | 8 ++++++++ go.sum | 6 ++++++ 13 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/altsrc/flag.go b/altsrc/flag.go index 84ef009..376733a 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -6,7 +6,7 @@ import ( "strings" "syscall" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // FlagInputSourceExtension is an extension interface of cli.Flag that diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go index 0aeb0b0..08170ca 100644 --- a/altsrc/flag_generated.go +++ b/altsrc/flag_generated.go @@ -3,7 +3,7 @@ package altsrc import ( "flag" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // WARNING: This file is generated! diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index cd18294..5f293bb 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) type testApplyInputSource struct { diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index 276dcda..8ea7e92 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -3,7 +3,7 @@ package altsrc import ( "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // InputSourceContext is an interface used to allow diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go index 1f9af36..f24cae6 100644 --- a/altsrc/json_command_test.go +++ b/altsrc/json_command_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) const ( diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 47ce82c..a5b2b0d 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // NewJSONSourceFromFlagFunc returns a func that takes a cli.Context diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index a111eee..e8cb060 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // MapInputSource implements InputSourceContext to return diff --git a/altsrc/toml_command_test.go b/altsrc/toml_command_test.go index a5053d4..1d91d56 100644 --- a/altsrc/toml_command_test.go +++ b/altsrc/toml_command_test.go @@ -11,7 +11,7 @@ import ( "os" "testing" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) func TestCommandTomFileTest(t *testing.T) { diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index 87bb9d9..127693a 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -10,7 +10,7 @@ import ( "reflect" "github.com/BurntSushi/toml" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) type tomlMap struct { diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index 9d3f431..31f78ce 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -11,7 +11,7 @@ import ( "os" "testing" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) func TestCommandYamlFileTest(t *testing.T) { diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index 202469f..2177c75 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -14,7 +14,7 @@ import ( "runtime" "strings" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" "gopkg.in/yaml.v2" ) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d5b55c4 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/urfave/cli + +go 1.12 + +require ( + github.com/BurntSushi/toml v0.3.1 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a2ea702 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From e01e3c540c7aea9e4e9740d002bd212491c2fe00 Mon Sep 17 00:00:00 2001 From: mingrammer Date: Wed, 6 Mar 2019 23:51:22 +0900 Subject: [PATCH 123/215] Fix the unaligned indents for the commands that have no categories --- app_test.go | 6 +++--- help.go | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app_test.go b/app_test.go index ad49464..69d1418 100644 --- a/app_test.go +++ b/app_test.go @@ -136,8 +136,8 @@ func ExampleApp_Run_appHelp() { // Oliver Allen // // COMMANDS: - // describeit, d use it to see a description - // help, h Shows a list of commands or help for one command + // 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") @@ -190,7 +190,7 @@ func ExampleApp_Run_noAction() { // [global options] command [command options] [arguments...] // // COMMANDS: - // help, h Shows a list of commands or help for one command + // help, h Shows a list of commands or help for one command // // GLOBAL OPTIONS: // --help, -h show help diff --git a/help.go b/help.go index 65874fa..126cc7c 100644 --- a/help.go +++ b/help.go @@ -30,8 +30,9 @@ AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: COMMANDS:{{range .VisibleCategories}}{{if .Name}} - {{.Name}}:{{end}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range $index, $option := .VisibleFlags}}{{if $index}} @@ -71,9 +72,11 @@ USAGE: {{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}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} -{{end}}{{if .VisibleFlags}} + + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} From 9766be8d3e11e71a2a4bb5c235fc6961278df983 Mon Sep 17 00:00:00 2001 From: mingrammer Date: Thu, 7 Mar 2019 00:04:18 +0900 Subject: [PATCH 124/215] get latest changes from master --- .gitignore | 1 + README.md | 2 +- altsrc/flag.go | 2 +- altsrc/flag_generated.go | 2 +- altsrc/flag_test.go | 2 +- altsrc/input_source_context.go | 2 +- altsrc/json_command_test.go | 2 +- altsrc/json_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 +- flaggen/main.go | 28 ++++++++++++++++++++++++++++ go.mod | 8 ++++++++ go.sum | 6 ++++++ help.go | 6 ++---- 17 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 flaggen/main.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index faf70c4..7a7e2d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.coverprofile node_modules/ +vendor \ No newline at end of file diff --git a/README.md b/README.md index 6eb2996..9904334 100644 --- a/README.md +++ b/README.md @@ -874,7 +874,7 @@ Will include: ``` COMMANDS: - noop + noop Template actions: add diff --git a/altsrc/flag.go b/altsrc/flag.go index 84ef009..376733a 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -6,7 +6,7 @@ import ( "strings" "syscall" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // FlagInputSourceExtension is an extension interface of cli.Flag that diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go index 0aeb0b0..08170ca 100644 --- a/altsrc/flag_generated.go +++ b/altsrc/flag_generated.go @@ -3,7 +3,7 @@ package altsrc import ( "flag" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // WARNING: This file is generated! diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index cd18294..5f293bb 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) type testApplyInputSource struct { diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index 276dcda..8ea7e92 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -3,7 +3,7 @@ package altsrc import ( "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // InputSourceContext is an interface used to allow diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go index 1f9af36..f24cae6 100644 --- a/altsrc/json_command_test.go +++ b/altsrc/json_command_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) const ( diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 47ce82c..a5b2b0d 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // NewJSONSourceFromFlagFunc returns a func that takes a cli.Context diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index a111eee..e8cb060 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) // MapInputSource implements InputSourceContext to return diff --git a/altsrc/toml_command_test.go b/altsrc/toml_command_test.go index a5053d4..1d91d56 100644 --- a/altsrc/toml_command_test.go +++ b/altsrc/toml_command_test.go @@ -11,7 +11,7 @@ import ( "os" "testing" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) func TestCommandTomFileTest(t *testing.T) { diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index 87bb9d9..127693a 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -10,7 +10,7 @@ import ( "reflect" "github.com/BurntSushi/toml" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) type tomlMap struct { diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index 9d3f431..31f78ce 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -11,7 +11,7 @@ import ( "os" "testing" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" ) func TestCommandYamlFileTest(t *testing.T) { diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index 202469f..2177c75 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -14,7 +14,7 @@ import ( "runtime" "strings" - "gopkg.in/urfave/cli.v1" + "github.com/urfave/cli" "gopkg.in/yaml.v2" ) diff --git a/flaggen/main.go b/flaggen/main.go new file mode 100644 index 0000000..02af0d8 --- /dev/null +++ b/flaggen/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "github.com/urfave/cli" + "log" + "os" +) + +func main() { + app := cli.NewApp() + + app.Name = "flaggen" + app.Usage = "Generate flag type code!" + + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "package, p", + Value: "cli", + Usage: "`Name of the package` for which the flag types will be generated", + }, + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d5b55c4 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/urfave/cli + +go 1.12 + +require ( + github.com/BurntSushi/toml v0.3.1 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a2ea702 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/help.go b/help.go index 126cc7c..d611971 100644 --- a/help.go +++ b/help.go @@ -240,10 +240,8 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc m funcMap := template.FuncMap{ "join": strings.Join, } - if customFunc != nil { - for key, value := range customFunc { - funcMap[key] = value - } + for key, value := range customFunc { + funcMap[key] = value } w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) From 32ddef5ca7f20a9aa0e7b80484e59d1653e856e4 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sat, 3 Aug 2019 21:48:48 +0530 Subject: [PATCH 125/215] finish generation of flag types for cli package --- flaggen/flag_generated.gotpl | 0 flaggen/main.go | 147 +++++++++++++++++++++++++++++++++-- 2 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 flaggen/flag_generated.gotpl diff --git a/flaggen/flag_generated.gotpl b/flaggen/flag_generated.gotpl new file mode 100644 index 0000000..e69de29 diff --git a/flaggen/main.go b/flaggen/main.go index 02af0d8..cbaba90 100644 --- a/flaggen/main.go +++ b/flaggen/main.go @@ -1,28 +1,165 @@ package main import ( + "encoding/json" "github.com/urfave/cli" "log" "os" + "text/template" ) +type CliFlagInfo struct { + PackageName string + Flags []FlagType +} + +type FlagType struct { + Name string `json:"name"` + Type string `json:"type"` + Value bool `json:"value"` + Destination bool `json:"dest"` + Doctail string `json:"doctail"` + ContextDefault string `json:"context_default"` + ContextType string `json:"context_type"` + Parser string `json:"parser"` + ParserCast string `json:"parser_cast"` +} + +var cliFlagTemplate = `// Code generated by fg; DO NOT EDIT. + +package {{ .PackageName }} + +import ( + "flag" + "strconv" + "time" +) +{{ range $i, $flag := .Flags }} +// {{ $flag.Name }}Flag is a flag with type {{ $flag.Type }}{{ $flag.Doctail }} +type {{ $flag.Name }}Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + {{- if eq $flag.Value true }} + Value {{ $flag.Type }} + {{- end }} + {{- if eq $flag.Destination true }} + Destination *{{ $flag.Type }} + {{- end }} +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f {{ $flag.Name }}Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f {{ $flag.Name }}Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f {{ $flag.Name }}Flag) IsRequired() bool { + return f.Required +} + +// {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns +// {{ $flag.ContextDefault }} if not found +func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { + return lookup{{ $flag.Name }}(name, c.flagSet) +} + +// Global{{ $flag.Name }} looks up the value of a global {{ $flag.Name }}Flag, returns +// {{ $flag.ContextDefault }} if not found +func (c *Context) Global{{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookup{{ $flag.Name }}(name, fs) + } + return {{ $flag.ContextDefault }} +} + +func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { + f := set.Lookup(name) + if f != nil { + {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} + if err != nil { + return {{ $flag.ContextDefault }} + } + {{ if ne .ParserCast "" }}return {{ $flag.ParserCast }}{{ else }}return parsed{{ end }} + } + return {{ $flag.ContextDefault }} +} +{{- end }}` + func main() { + var packageName, inputPath, outputPath string + app := cli.NewApp() - app.Name = "flaggen" + app.Name = "fg" app.Usage = "Generate flag type code!" + app.Version = "v0.1.0" app.Flags = []cli.Flag{ cli.StringFlag{ - Name: "package, p", - Value: "cli", - Usage: "`Name of the package` for which the flag types will be generated", + Name: "package, p", + Value: "cli", + Usage: "`PACKAGE` for which the flag types will be generated", + Destination: &packageName, + }, + cli.StringFlag{ + Name: "input, i", + Usage: "path to the `INPUT JSON FILE` which defines each type to be generated", + Destination: &inputPath, + }, + cli.StringFlag{ + Name: "output, o", + Usage: "path to the `OUTPUT GO FILE` which will contain the flag types", + Destination: &outputPath, }, } + app.Action = func(c *cli.Context) error { + var info CliFlagInfo + info.PackageName = packageName + + inFile, err := os.Open(inputPath) + if err != nil { + log.Fatal(err) + } + + defer inFile.Close() + + decoder := json.NewDecoder(inFile) + + err = decoder.Decode(&info.Flags) + if err != nil { + log.Fatal(err) + } + + tpl := template.Must(template.New("").Parse(cliFlagTemplate)) + + outFile, err := os.Create(outputPath) + if err != nil { + log.Fatal(err) + } + + defer outFile.Close() + + err = tpl.Execute(outFile, info) + if err != nil { + log.Fatal(err) + } + + return nil + } + err := app.Run(os.Args) if err != nil { log.Fatal(err) } - } From 16c7a60528bc5f7d98030e09630a38f3d8fc9ddc Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sat, 3 Aug 2019 22:14:58 +0530 Subject: [PATCH 126/215] finish generation of flag types for altsrc package rename package to fg (flag generator) --- {flaggen => fg}/main.go | 45 ++++++++++++++++++++++++++++++++---- flaggen/flag_generated.gotpl | 0 2 files changed, 41 insertions(+), 4 deletions(-) rename {flaggen => fg}/main.go (76%) delete mode 100644 flaggen/flag_generated.gotpl diff --git a/flaggen/main.go b/fg/main.go similarity index 76% rename from flaggen/main.go rename to fg/main.go index cbaba90..ce5e389 100644 --- a/flaggen/main.go +++ b/fg/main.go @@ -25,10 +25,10 @@ type FlagType struct { ParserCast string `json:"parser_cast"` } -var cliFlagTemplate = `// Code generated by fg; DO NOT EDIT. +var flagTemplate = `// Code generated by fg; DO NOT EDIT. package {{ .PackageName }} - +{{ if eq .PackageName "cli" }} import ( "flag" "strconv" @@ -93,7 +93,42 @@ func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextTyp } return {{ $flag.ContextDefault }} } -{{- end }}` +{{- end }} +{{ else }} +import ( + "flag" + "github.com/urfave/cli" +) + +{{ range $i, $flag := .Flags }} +// {{ $flag.Name }}Flag is the flag type that wraps cli.{{ $flag.Name }}Flag to allow +// for other values to be specified +type {{ $flag.Name }}Flag struct { + cli.{{ $flag.Name }}Flag + set *flag.FlagSet +} + +// New{{ $flag.Name }}Flag creates a new {{ $flag.Name }}Flag +func New{{ $flag.Name }}Flag(fl cli.{{ $flag.Name }}Flag) *{{ $flag.Name }}Flag { + return &{{ $flag.Name }}Flag{ {{ $flag.Name }}Flag: fl, set: nil } +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped {{ $flag.Name }}Flag.Apply +func (f *{{ $flag.Name }}Flag) Apply(set *flag.FlagSet) { + f.set = set + f.{{ $flag.Name }}Flag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped {{ $flag.Name }}Flag.ApplyWithError +func (f *{{ $flag.Name }}Flag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.{{ $flag.Name }}Flag.ApplyWithError(set) +} +{{- end }} +{{- end }} +` func main() { var packageName, inputPath, outputPath string @@ -125,6 +160,8 @@ func main() { app.Action = func(c *cli.Context) error { var info CliFlagInfo + var tpl *template.Template + info.PackageName = packageName inFile, err := os.Open(inputPath) @@ -141,7 +178,7 @@ func main() { log.Fatal(err) } - tpl := template.Must(template.New("").Parse(cliFlagTemplate)) + tpl = template.Must(template.New("").Parse(flagTemplate)) outFile, err := os.Create(outputPath) if err != nil { diff --git a/flaggen/flag_generated.gotpl b/flaggen/flag_generated.gotpl deleted file mode 100644 index e69de29..0000000 From c133a5aeb16e0978e3c29f8446ddf02922b7e150 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sat, 3 Aug 2019 22:15:43 +0530 Subject: [PATCH 127/215] add explicit true/false choices for value and dest keys due to go default false for bool types --- flag-types.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/flag-types.json b/flag-types.json index 1223107..3306025 100644 --- a/flag-types.json +++ b/flag-types.json @@ -3,6 +3,7 @@ "name": "Bool", "type": "bool", "value": false, + "dest": true, "context_default": "false", "parser": "strconv.ParseBool(f.Value.String())" }, @@ -10,6 +11,7 @@ "name": "BoolT", "type": "bool", "value": false, + "dest": true, "doctail": " that is true by default", "context_default": "false", "parser": "strconv.ParseBool(f.Value.String())" @@ -17,6 +19,8 @@ { "name": "Duration", "type": "time.Duration", + "value": true, + "dest": true, "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", "context_default": "0", "parser": "time.ParseDuration(f.Value.String())" @@ -24,12 +28,15 @@ { "name": "Float64", "type": "float64", + "value": true, + "dest": true, "context_default": "0", "parser": "strconv.ParseFloat(f.Value.String(), 64)" }, { "name": "Generic", "type": "Generic", + "value": true, "dest": false, "context_default": "nil", "context_type": "interface{}" @@ -37,12 +44,16 @@ { "name": "Int64", "type": "int64", + "value": true, + "dest": true, "context_default": "0", "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" }, { "name": "Int", "type": "int", + "value": true, + "dest": true, "context_default": "0", "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", "parser_cast": "int(parsed)" @@ -50,6 +61,7 @@ { "name": "IntSlice", "type": "*IntSlice", + "value": true, "dest": false, "context_default": "nil", "context_type": "[]int", @@ -58,6 +70,7 @@ { "name": "Int64Slice", "type": "*Int64Slice", + "value": true, "dest": false, "context_default": "nil", "context_type": "[]int64", @@ -66,12 +79,15 @@ { "name": "String", "type": "string", + "value": true, + "dest": true, "context_default": "\"\"", "parser": "f.Value.String(), error(nil)" }, { "name": "StringSlice", "type": "*StringSlice", + "value": true, "dest": false, "context_default": "nil", "context_type": "[]string", @@ -80,12 +96,16 @@ { "name": "Uint64", "type": "uint64", + "value": true, + "dest": true, "context_default": "0", "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" }, { "name": "Uint", "type": "uint", + "value": true, + "dest": true, "context_default": "0", "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", "parser_cast": "uint(parsed)" From add69c7d4fbef52ac8541c2f7dfa465fdd9be2c3 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sat, 3 Aug 2019 22:16:25 +0530 Subject: [PATCH 128/215] updated flag types generated courtesy fg cli --- altsrc/flag_generated.go | 109 +++++++++++++++++++-------------------- flag_generated.go | 30 +++++------ 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go index 08170ca..0c45914 100644 --- a/altsrc/flag_generated.go +++ b/altsrc/flag_generated.go @@ -1,13 +1,12 @@ +// Code generated by fg; DO NOT EDIT. + package altsrc import ( "flag" - "github.com/urfave/cli" ) -// WARNING: This file is generated! - // BoolFlag is the flag type that wraps cli.BoolFlag to allow // for other values to be specified type BoolFlag struct { @@ -20,15 +19,15 @@ func NewBoolFlag(fl cli.BoolFlag) *BoolFlag { return &BoolFlag{BoolFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped BoolFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped BoolFlag.Apply func (f *BoolFlag) Apply(set *flag.FlagSet) { f.set = set f.BoolFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped BoolFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped BoolFlag.ApplyWithError func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.BoolFlag.ApplyWithError(set) @@ -46,15 +45,15 @@ func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag { return &BoolTFlag{BoolTFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped BoolTFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped BoolTFlag.Apply func (f *BoolTFlag) Apply(set *flag.FlagSet) { f.set = set f.BoolTFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped BoolTFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped BoolTFlag.ApplyWithError func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.BoolTFlag.ApplyWithError(set) @@ -72,15 +71,15 @@ func NewDurationFlag(fl cli.DurationFlag) *DurationFlag { return &DurationFlag{DurationFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped DurationFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped DurationFlag.Apply func (f *DurationFlag) Apply(set *flag.FlagSet) { f.set = set f.DurationFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped DurationFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped DurationFlag.ApplyWithError func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.DurationFlag.ApplyWithError(set) @@ -98,15 +97,15 @@ func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag { return &Float64Flag{Float64Flag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped Float64Flag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Float64Flag.Apply func (f *Float64Flag) Apply(set *flag.FlagSet) { f.set = set f.Float64Flag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped Float64Flag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Float64Flag.ApplyWithError func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Float64Flag.ApplyWithError(set) @@ -124,15 +123,15 @@ func NewGenericFlag(fl cli.GenericFlag) *GenericFlag { return &GenericFlag{GenericFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped GenericFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped GenericFlag.Apply func (f *GenericFlag) Apply(set *flag.FlagSet) { f.set = set f.GenericFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped GenericFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped GenericFlag.ApplyWithError func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.GenericFlag.ApplyWithError(set) @@ -150,15 +149,15 @@ func NewInt64Flag(fl cli.Int64Flag) *Int64Flag { return &Int64Flag{Int64Flag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped Int64Flag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Int64Flag.Apply func (f *Int64Flag) Apply(set *flag.FlagSet) { f.set = set f.Int64Flag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped Int64Flag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Int64Flag.ApplyWithError func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Int64Flag.ApplyWithError(set) @@ -176,15 +175,15 @@ func NewIntFlag(fl cli.IntFlag) *IntFlag { return &IntFlag{IntFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped IntFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped IntFlag.Apply func (f *IntFlag) Apply(set *flag.FlagSet) { f.set = set f.IntFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped IntFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped IntFlag.ApplyWithError func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.IntFlag.ApplyWithError(set) @@ -202,15 +201,15 @@ func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag { return &IntSliceFlag{IntSliceFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped IntSliceFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped IntSliceFlag.Apply func (f *IntSliceFlag) Apply(set *flag.FlagSet) { f.set = set f.IntSliceFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped IntSliceFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped IntSliceFlag.ApplyWithError func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.IntSliceFlag.ApplyWithError(set) @@ -228,15 +227,15 @@ func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag { return &Int64SliceFlag{Int64SliceFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped Int64SliceFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Int64SliceFlag.Apply func (f *Int64SliceFlag) Apply(set *flag.FlagSet) { f.set = set f.Int64SliceFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped Int64SliceFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Int64SliceFlag.ApplyWithError func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Int64SliceFlag.ApplyWithError(set) @@ -254,15 +253,15 @@ func NewStringFlag(fl cli.StringFlag) *StringFlag { return &StringFlag{StringFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped StringFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped StringFlag.Apply func (f *StringFlag) Apply(set *flag.FlagSet) { f.set = set f.StringFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped StringFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped StringFlag.ApplyWithError func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.StringFlag.ApplyWithError(set) @@ -280,15 +279,15 @@ func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag { return &StringSliceFlag{StringSliceFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped StringSliceFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped StringSliceFlag.Apply func (f *StringSliceFlag) Apply(set *flag.FlagSet) { f.set = set f.StringSliceFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped StringSliceFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped StringSliceFlag.ApplyWithError func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.StringSliceFlag.ApplyWithError(set) @@ -306,15 +305,15 @@ func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag { return &Uint64Flag{Uint64Flag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped Uint64Flag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Uint64Flag.Apply func (f *Uint64Flag) Apply(set *flag.FlagSet) { f.set = set f.Uint64Flag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped Uint64Flag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Uint64Flag.ApplyWithError func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Uint64Flag.ApplyWithError(set) @@ -332,15 +331,15 @@ func NewUintFlag(fl cli.UintFlag) *UintFlag { return &UintFlag{UintFlag: fl, set: nil} } -// Apply saves the flagSet for later usage calls, then calls the -// wrapped UintFlag.Apply +// Apply saves the flagSet for later usage calls, then calls +// the wrapped UintFlag.Apply func (f *UintFlag) Apply(set *flag.FlagSet) { f.set = set f.UintFlag.Apply(set) } -// ApplyWithError saves the flagSet for later usage calls, then calls the -// wrapped UintFlag.ApplyWithError +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped UintFlag.ApplyWithError func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.UintFlag.ApplyWithError(set) diff --git a/flag_generated.go b/flag_generated.go index a3e4d6e..f106d23 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -1,3 +1,5 @@ +// Code generated by fg; DO NOT EDIT. + package cli import ( @@ -6,8 +8,6 @@ import ( "time" ) -// WARNING: This file is generated! - // BoolFlag is a flag with type bool type BoolFlag struct { Name string @@ -30,7 +30,7 @@ func (f BoolFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f BoolFlag) IsRequired() bool { return f.Required } @@ -84,7 +84,7 @@ func (f BoolTFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f BoolTFlag) IsRequired() bool { return f.Required } @@ -139,7 +139,7 @@ func (f DurationFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f DurationFlag) IsRequired() bool { return f.Required } @@ -194,7 +194,7 @@ func (f Float64Flag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f Float64Flag) IsRequired() bool { return f.Required } @@ -248,7 +248,7 @@ func (f GenericFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f GenericFlag) IsRequired() bool { return f.Required } @@ -303,7 +303,7 @@ func (f Int64Flag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f Int64Flag) IsRequired() bool { return f.Required } @@ -358,7 +358,7 @@ func (f IntFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f IntFlag) IsRequired() bool { return f.Required } @@ -412,7 +412,7 @@ func (f IntSliceFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f IntSliceFlag) IsRequired() bool { return f.Required } @@ -466,7 +466,7 @@ func (f Int64SliceFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f Int64SliceFlag) IsRequired() bool { return f.Required } @@ -521,7 +521,7 @@ func (f StringFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f StringFlag) IsRequired() bool { return f.Required } @@ -575,7 +575,7 @@ func (f StringSliceFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f StringSliceFlag) IsRequired() bool { return f.Required } @@ -630,7 +630,7 @@ func (f Uint64Flag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f Uint64Flag) IsRequired() bool { return f.Required } @@ -685,7 +685,7 @@ func (f UintFlag) GetName() string { return f.Name } -// IsRequired returns the whether or not the flag is required +// IsRequired returns whether or not the flag is required func (f UintFlag) IsRequired() bool { return f.Required } From d209be324522a802f8056094f8bb89b4562ca9a3 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sat, 3 Aug 2019 22:32:36 +0530 Subject: [PATCH 129/215] update go generate command test file generation --- altsrc/altsrc.go | 2 +- cli.go | 2 +- generate-flag-types | 262 -------------------------------------------- 3 files changed, 2 insertions(+), 264 deletions(-) delete mode 100755 generate-flag-types diff --git a/altsrc/altsrc.go b/altsrc/altsrc.go index ac34bf6..b81c99a 100644 --- a/altsrc/altsrc.go +++ b/altsrc/altsrc.go @@ -1,3 +1,3 @@ package altsrc -//go:generate python ../generate-flag-types altsrc -i ../flag-types.json -o flag_generated.go +//go:generate fg -p altsrc -i ../flag-types.json -o flag_generated.go diff --git a/cli.go b/cli.go index 90c07eb..e71e142 100644 --- a/cli.go +++ b/cli.go @@ -19,4 +19,4 @@ // } package cli -//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go +//go:generate fg -p cli -i flag-types.json -o flag_generated.go diff --git a/generate-flag-types b/generate-flag-types deleted file mode 100755 index e0b5aa1..0000000 --- a/generate-flag-types +++ /dev/null @@ -1,262 +0,0 @@ -#!/usr/bin/env python -""" -The flag types that ship with the cli library have many things in common, and -so we can take advantage of the `go generate` command to create much of the -source code from a list of definitions. These definitions attempt to cover -the parts that vary between flag types, and should evolve as needed. - -An example of the minimum definition needed is: - - { - "name": "SomeType", - "type": "sometype", - "context_default": "nil" - } - -In this example, the code generated for the `cli` package will include a type -named `SomeTypeFlag` that is expected to wrap a value of type `sometype`. -Fetching values by name via `*cli.Context` will default to a value of `nil`. - -A more complete, albeit somewhat redundant, example showing all available -definition keys is: - - { - "name": "VeryMuchType", - "type": "*VeryMuchType", - "value": true, - "dest": false, - "doctail": " which really only wraps a []float64, oh well!", - "context_type": "[]float64", - "context_default": "nil", - "parser": "parseVeryMuchType(f.Value.String())", - "parser_cast": "[]float64(parsed)" - } - -The meaning of each field is as follows: - - name (string) - The type "name", which will be suffixed with - `Flag` when generating the type definition - for `cli` and the wrapper type for `altsrc` - type (string) - The type that the generated `Flag` type for `cli` - is expected to "contain" as its `.Value` member - value (bool) - Should the generated `cli` type have a `Value` - member? - dest (bool) - Should the generated `cli` type support a - destination pointer? - doctail (string) - Additional docs for the `cli` flag type comment - context_type (string) - The literal type used in the `*cli.Context` - reader func signature - context_default (string) - The literal value used as the default by the - `*cli.Context` reader funcs when no value is - present - parser (string) - Literal code used to parse the flag `f`, - expected to have a return signature of - (value, error) - parser_cast (string) - Literal code used to cast the `parsed` value - returned from the `parser` code -""" - -from __future__ import print_function, unicode_literals - -import argparse -import json -import os -import subprocess -import sys -import tempfile -import textwrap - - -class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter, - argparse.RawDescriptionHelpFormatter): - pass - - -def main(sysargs=sys.argv[:]): - parser = argparse.ArgumentParser( - description='Generate flag type code!', - formatter_class=_FancyFormatter) - parser.add_argument( - 'package', - type=str, default='cli', choices=_WRITEFUNCS.keys(), - help='Package for which flag types will be generated' - ) - parser.add_argument( - '-i', '--in-json', - type=argparse.FileType('r'), - default=sys.stdin, - help='Input JSON file which defines each type to be generated' - ) - parser.add_argument( - '-o', '--out-go', - type=argparse.FileType('w'), - default=sys.stdout, - help='Output file/stream to which generated source will be written' - ) - parser.epilog = __doc__ - - args = parser.parse_args(sysargs[1:]) - _generate_flag_types(_WRITEFUNCS[args.package], args.out_go, args.in_json) - return 0 - - -def _generate_flag_types(writefunc, output_go, input_json): - types = json.load(input_json) - - tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False) - writefunc(tmp, types) - tmp.close() - - new_content = subprocess.check_output( - ['goimports', tmp.name] - ).decode('utf-8') - - print(new_content, file=output_go, end='') - output_go.flush() - os.remove(tmp.name) - - -def _set_typedef_defaults(typedef): - typedef.setdefault('doctail', '') - typedef.setdefault('context_type', typedef['type']) - typedef.setdefault('dest', True) - typedef.setdefault('value', True) - typedef.setdefault('parser', 'f.Value, error(nil)') - typedef.setdefault('parser_cast', 'parsed') - - -def _write_cli_flag_types(outfile, types): - _fwrite(outfile, """\ - package cli - - // WARNING: This file is generated! - - """) - - for typedef in types: - _set_typedef_defaults(typedef) - - _fwrite(outfile, """\ - // {name}Flag is a flag with type {type}{doctail} - type {name}Flag struct {{ - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - """.format(**typedef)) - - if typedef['value']: - _fwrite(outfile, """\ - Value {type} - """.format(**typedef)) - - if typedef['dest']: - _fwrite(outfile, """\ - Destination *{type} - """.format(**typedef)) - - _fwrite(outfile, "\n}\n\n") - - _fwrite(outfile, """\ - // String returns a readable representation of this value - // (for usage defaults) - func (f {name}Flag) String() string {{ - return FlagStringer(f) - }} - - // GetName returns the name of the flag - func (f {name}Flag) GetName() string {{ - return f.Name - }} - - // IsRequired returns the whether or not the flag is required - func (f {name}Flag) IsRequired() bool {{ - return f.Required - }} - - // {name} looks up the value of a local {name}Flag, returns - // {context_default} if not found - func (c *Context) {name}(name string) {context_type} {{ - return lookup{name}(name, c.flagSet) - }} - - // Global{name} looks up the value of a global {name}Flag, returns - // {context_default} if not found - func (c *Context) Global{name}(name string) {context_type} {{ - if fs := lookupGlobalFlagSet(name, c); fs != nil {{ - return lookup{name}(name, fs) - }} - return {context_default} - }} - - func lookup{name}(name string, set *flag.FlagSet) {context_type} {{ - f := set.Lookup(name) - if f != nil {{ - parsed, err := {parser} - if err != nil {{ - return {context_default} - }} - return {parser_cast} - }} - return {context_default} - }} - """.format(**typedef)) - - -def _write_altsrc_flag_types(outfile, types): - _fwrite(outfile, """\ - package altsrc - - import ( - "gopkg.in/urfave/cli.v1" - ) - - // WARNING: This file is generated! - - """) - - for typedef in types: - _set_typedef_defaults(typedef) - - _fwrite(outfile, """\ - // {name}Flag is the flag type that wraps cli.{name}Flag to allow - // for other values to be specified - type {name}Flag struct {{ - cli.{name}Flag - set *flag.FlagSet - }} - - // New{name}Flag creates a new {name}Flag - func New{name}Flag(fl cli.{name}Flag) *{name}Flag {{ - return &{name}Flag{{{name}Flag: fl, set: nil}} - }} - - // Apply saves the flagSet for later usage calls, then calls the - // wrapped {name}Flag.Apply - func (f *{name}Flag) Apply(set *flag.FlagSet) {{ - 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)) - - -def _fwrite(outfile, text): - print(textwrap.dedent(text), end='', file=outfile) - - -_WRITEFUNCS = { - 'cli': _write_cli_flag_types, - 'altsrc': _write_altsrc_flag_types -} - -if __name__ == '__main__': - sys.exit(main()) From 97dbddb32db290fdc6392e6a669a92acbadef9ff Mon Sep 17 00:00:00 2001 From: "[[ BOT ]] Lynn Cyrin" Date: Sat, 3 Aug 2019 10:23:29 -0700 Subject: [PATCH 130/215] use codeowners instead of maintainers --- .github/CODEOWNERS | 4 ++++ MAINTAINERS.md | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 .github/CODEOWNERS delete mode 100644 MAINTAINERS.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..4f138e8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# See https://help.github.com/articles/about-codeowners/ +# for more info about CODEOWNERS file + +* @urfave/cli diff --git a/MAINTAINERS.md b/MAINTAINERS.md deleted file mode 100644 index 5b7a6ea..0000000 --- a/MAINTAINERS.md +++ /dev/null @@ -1,4 +0,0 @@ -- @meatballhat -- @lynncyrin -- @AudriusButkevicius -- @asahasrabuddhe From 9938dec695d6a0ba5a4d84b703766333cd7d10e8 Mon Sep 17 00:00:00 2001 From: "[[ BOT ]] Lynn Cyrin" Date: Sat, 3 Aug 2019 10:26:07 -0700 Subject: [PATCH 131/215] update contributing docs --- CONTRIBUTING.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 329195e..9a4640a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,10 @@ ## Contributing -**NOTE**: the primary maintainer(s) may be found in -[./MAINTAINERS.md](./MAINTAINERS.md). +Use @urfave/cli to ping the maintainers. -Feel free to put up a pull request to fix a bug or maybe add a feature. I will +Feel free to put up a pull request to fix a bug or maybe add a feature. We will give it a code review and make sure that it does not break backwards -compatibility. If I or any other collaborators agree that it is in line with +compatibility. If collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. @@ -15,5 +14,5 @@ to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you feel like you have contributed to the project but have not yet been added -as a collaborator, we probably forgot to add you :sweat_smile:. Please open an +as a collaborator, we probably forgot to add you :sweat_smile:. Please open an issue! From d1ded77768d33ce64657686558884e69cbb5bce4 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 08:45:29 +0530 Subject: [PATCH 132/215] rename utility from fg > flag-generator --- {fg => flag-generator}/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {fg => flag-generator}/main.go (100%) diff --git a/fg/main.go b/flag-generator/main.go similarity index 100% rename from fg/main.go rename to flag-generator/main.go From 065fe9e9af992d82126929c157edb16b1a1f06ab Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 10:05:44 +0530 Subject: [PATCH 133/215] change structure to embed source json and template files restructure code to have defaults in place of choices --- flag-generator/.gitignore | 1 + flag-generator/fs_vfsdata.go | 214 +++++++++++++++++ flag-generator/go.mod | 9 + flag-generator/go.sum | 9 + flag-generator/main.go | 215 +++++++----------- .../source/flag-types.json | 0 .../templates/altsrc_flags_generated.gotpl | 36 +++ .../templates/cli_flags_generated.gotpl | 69 ++++++ 8 files changed, 426 insertions(+), 127 deletions(-) create mode 100644 flag-generator/.gitignore create mode 100644 flag-generator/fs_vfsdata.go create mode 100644 flag-generator/go.mod create mode 100644 flag-generator/go.sum rename flag-types.json => flag-generator/source/flag-types.json (100%) create mode 100644 flag-generator/templates/altsrc_flags_generated.gotpl create mode 100644 flag-generator/templates/cli_flags_generated.gotpl diff --git a/flag-generator/.gitignore b/flag-generator/.gitignore new file mode 100644 index 0000000..5657f6e --- /dev/null +++ b/flag-generator/.gitignore @@ -0,0 +1 @@ +vendor \ No newline at end of file diff --git a/flag-generator/fs_vfsdata.go b/flag-generator/fs_vfsdata.go new file mode 100644 index 0000000..2122fcf --- /dev/null +++ b/flag-generator/fs_vfsdata.go @@ -0,0 +1,214 @@ +// Code generated by vfsgen; DO NOT EDIT. + +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + pathpkg "path" + "time" +) + +// fs statically implements the virtual filesystem provided to vfsgen. +var fs = func() http.FileSystem { + fs := vfsgenÛ°FS{ + "/": &vfsgenÛ°DirInfo{ + name: "/", + modTime: time.Time{}, + }, + "/source": &vfsgenÛ°DirInfo{ + name: "source", + modTime: time.Time{}, + }, + "/source/flag-types.json": &vfsgenÛ°CompressedFileInfo{ + name: "flag-types.json", + modTime: time.Time{}, + uncompressedSize: 2559, + + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x94\xc1\x6e\x9c\x30\x10\x86\xef\x3c\xc5\xc8\xbd\x40\xb5\x82\x1c\x56\x7b\xd8\x63\x55\xb5\xda\x5b\xa5\x34\xbd\x24\x51\xe4\x80\x21\x56\x1d\x1b\xd9\x43\xd4\x28\xca\xbb\x57\xf6\x2e\xbb\x60\x0c\x62\x5b\x2d\xb9\xfe\x23\x98\xef\x9b\xb1\xe6\x36\x02\x78\x8b\x00\x00\x88\xa4\xcf\x8c\x6c\x81\x7c\x51\x4a\x90\xd5\x3e\xc3\xd7\xda\x65\x8f\x9d\xec\x85\x8a\xc6\x86\x25\x15\x86\x1d\xb2\x82\x19\x24\x5b\x40\xdd\xb4\x49\xae\x24\xb2\x3f\xf8\x50\xb0\x92\x36\xc2\x16\x89\xfb\xa0\xfd\x4b\x4d\xb5\x61\xda\xc6\x06\x75\xae\xe4\x4b\xfa\xc3\x26\xb6\x79\x5c\xa6\xbf\x6c\x8f\xf4\x1a\x35\x97\x55\x9c\x24\x24\x02\x78\x5f\x85\x51\x7f\xfe\x3f\x6b\xa1\x72\xa4\x5c\xd8\x8f\x01\x9f\x28\x02\x37\xae\x0c\x8f\xaf\xd0\xf2\x2f\xa9\xf5\xb5\xd1\x14\xb9\x92\xbe\x19\xf2\x67\x96\xfa\xc5\x56\xb1\xeb\x33\x69\x18\x1b\xc6\xe0\x09\xb1\x36\xdb\x2c\xab\x94\xa0\xb2\x4a\x95\xae\xb2\xfa\x77\x95\xd9\x0e\xd9\x27\x87\xdc\xf6\x49\x26\xd4\xaf\x86\xda\x8e\xb1\xf7\x83\xf9\xde\xdf\x84\xa2\xb8\x59\xfb\xda\x65\x3f\x9e\x25\x3c\x0f\xb6\xb7\x23\xd7\x7d\x00\xbb\x82\xcd\x7a\x14\xf8\x3b\x93\x4c\xf3\xdc\x07\xf6\xe2\x71\xe0\xee\xb3\x0c\x10\x4b\x2e\xfc\xe1\xb7\x2d\xb8\x44\xa6\x4b\x9a\xb3\xb7\xf7\x31\xb8\x9d\x0c\xcc\x92\xcb\x05\x26\xb9\x93\xa1\x39\x5e\x4d\x8e\x72\x27\x31\xc0\xfa\xa1\xa4\xbd\x4f\x1f\x72\xea\xfa\x59\xaa\xd8\x45\xc5\x94\xcb\xb5\xe0\x39\xf3\x85\x3e\xfb\x85\x4b\xbc\x8c\xdb\xfb\xce\xdc\x4e\xda\x47\xcd\xf8\x48\x91\x24\xfb\xc8\x2a\x33\xad\x95\x8e\x25\x17\x53\x52\x9b\xf5\xa8\x96\x57\xba\x98\xd8\xe9\xf1\x8e\xa9\x1d\x48\xce\x93\xdb\xef\xde\x17\x33\xbd\xf4\x5f\xdf\xe0\x1d\xb9\x23\x43\xe8\xe1\xab\x9b\x4b\x19\xde\x41\xa0\x76\x99\x25\xf4\x87\x12\xdc\x42\x87\xe5\xbc\x35\xdc\xf0\xd0\xcd\x6a\x16\x39\x5a\xb6\xf7\xd9\x57\xeb\x86\x0f\xcf\x56\xb3\xc0\xdd\x9a\x86\x0d\x1f\xae\xc6\xbf\x5c\xd1\x7d\xf4\x37\x00\x00\xff\xff\x66\x52\x85\x44\xff\x09\x00\x00"), + }, + "/templates": &vfsgenÛ°DirInfo{ + name: "templates", + modTime: time.Time{}, + }, + "/templates/altsrc_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ + name: "altsrc_flags_generated.gotpl", + modTime: time.Time{}, + uncompressedSize: 1064, + + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x41\x6b\xdc\x30\x10\x85\xcf\xd1\xaf\x78\x2c\xa1\xac\xc3\xd6\xbe\x6f\xc9\xa1\x34\x29\xf4\xb2\x29\x24\xd0\xb3\xd6\x1e\xd9\xa2\x5a\xdb\x48\xe3\x5d\x16\xe1\xff\x5e\x46\x0e\xe9\x96\x6a\x4b\x2f\xb9\xc9\x1a\xbd\xf7\xe6\x9b\x71\x55\xe1\xcb\xd0\x10\x5a\xea\xc9\x6b\xa6\x06\xfb\x33\x4c\xfb\x09\x0f\x4f\xd8\x3d\xbd\xe0\xf1\xe1\xdb\x4b\xa9\xd4\xa8\xeb\x9f\xba\x25\xc4\x88\xf2\xfb\x72\xde\xe9\x03\x61\x9e\x95\xb2\x87\x71\xf0\x8c\xb5\xba\x59\x19\xa7\xdb\x95\x02\x80\x55\x6b\xb9\x9b\xf6\x65\x3d\x1c\xaa\xc9\x1b\x7d\xa4\xaa\x76\x76\xa5\x0a\xa5\x62\x84\xd7\x7d\x4b\xb8\xb5\x1b\xdc\x8a\x04\xdb\x7b\x94\x5f\x9d\x6e\x83\x18\x56\x95\xc4\xa4\x42\xf9\x1a\x22\x35\xd8\x00\xee\x08\x49\xc0\xe7\x91\xc0\x9d\x66\x9c\xbc\x1e\x03\x6a\x67\xcb\xac\x88\x07\x68\xe7\x86\x93\xb8\x9a\xc1\x63\xe0\x8e\x3c\x8e\xda\x4d\x14\xa4\xb8\x27\x84\x91\x6a\x6b\x2c\x35\x2a\xb9\x66\x6d\x02\xfb\xa9\x66\xc4\xc4\x76\x2d\x2c\x15\x03\x31\xee\x52\x45\xae\x9e\x89\xd5\xac\x24\x7c\x47\xa7\xac\x73\xed\x49\x33\x05\x68\xf4\x74\xca\x86\x2b\x33\xf5\xf5\x35\xfd\xda\xb8\xab\xfd\x14\xb8\xcb\x46\x2e\x14\x9e\x78\xf2\x3d\x3e\xe4\x9e\xc4\x6c\x23\x5b\x18\xb7\x11\xc0\x2d\x7a\xeb\x30\xbf\x92\x7d\x1e\x47\x77\x46\xd0\x47\xfa\xbd\xa0\x67\xe2\x34\x6e\xa7\x99\x3c\xa6\x20\xff\x4e\xad\x9d\x0b\x1b\x79\xd2\x2f\x67\x11\x8b\x40\x56\x38\x52\x93\xcd\x2c\x93\xfb\x32\x82\xb5\xc9\x03\x15\x4b\x0b\xeb\xbf\x46\x5f\x20\xaa\x1b\x53\xca\xfd\xbd\xf4\x2d\x1f\xd7\x43\x44\x5f\x5c\x32\xfd\xb0\xdc\x3d\x7a\x3f\xf8\xf7\x85\x7b\x8b\xf9\x2f\xca\xb7\xd7\x39\x5c\x4a\xdd\x2e\xfb\xbd\xe4\xbe\xd8\xf7\xbf\x26\xf0\x87\xb7\x8c\x22\xc6\x8f\xa0\xbe\xc1\x3c\xff\x0a\x00\x00\xff\xff\x3c\x32\xe5\x42\x28\x04\x00\x00"), + }, + "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ + name: "cli_flags_generated.gotpl", + modTime: time.Time{}, + uncompressedSize: 2234, + + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x41\x4f\xeb\x38\x10\x3e\x37\xbf\x62\xb6\xe2\x90\xa0\x6e\x7b\x5f\xc4\x89\xc2\x2e\xd2\x0a\xd0\x83\xc7\xdd\x4d\xc6\xa9\x85\xb1\x8b\xed\xc0\x43\x55\xfe\xfb\xd3\x8c\x9d\xb4\x4d\x0b\xf4\xf2\xe8\xc9\x9e\xf9\xf2\xcd\x37\xdf\x8c\xea\xd9\x0c\x2e\x6c\x85\x50\xa3\x41\x27\x02\x56\xb0\x78\x07\x59\x9f\xc1\xfc\x16\x6e\x6e\x1f\xe0\x72\x7e\xfd\x30\xcd\xb2\x95\x28\x9f\x44\x8d\xb0\x5e\xc3\xf4\x2e\x9e\x6f\xc4\x33\x42\xdb\x66\x99\x7a\x5e\x59\x17\x20\xcf\x46\x63\xa9\x45\x3d\xce\x46\x63\x1f\x5c\x69\xcd\x2b\x1d\x83\x7a\xc6\x71\x56\x64\xeb\x35\x38\x61\x6a\x84\x13\x35\x81\x13\x02\xc2\x3f\xe7\x30\xbd\xd2\xa2\xf6\x44\x33\x9b\x11\x39\x27\xa6\x89\x9a\x72\xa0\x3c\x08\x60\xf8\x9b\x0a\x4b\x08\xef\x2b\xdc\x00\x1f\xe8\xd6\xb6\xfd\x7d\x6e\xcb\x20\x94\x26\xbe\x5d\xe0\x36\xa3\x0f\xae\x29\x03\xac\x33\x00\x00\x4e\xa4\x9f\x0f\x4e\x99\x9a\xc3\x3f\x3d\x75\xbb\x17\xbe\x34\xaf\x8f\xc2\xed\x85\xaf\x94\xc6\x3b\x11\x96\x83\xf0\x0f\x7c\x69\x94\xc3\x8a\xce\x0b\x6b\x35\x07\xff\x53\x55\x85\x26\x52\xf4\xc1\xf5\xfa\x6f\x50\x12\xf0\x25\xc9\x7d\x14\xba\x41\x08\xae\x61\x87\x47\x7c\x1d\x8d\x86\x6d\x67\x23\xfa\x0e\x4d\xd5\x9f\xb7\x39\xe6\xe8\x83\x32\x22\x28\x6b\x36\x4c\x5b\xc1\xd1\xe9\xa7\x7c\x6d\x46\x23\xb9\xe7\x6e\xc0\x61\x68\x9c\xa1\x49\x38\x14\x95\x58\x68\x04\x87\x2b\x87\x1e\x4d\x88\x15\xac\x84\xb0\x54\x1e\x5e\x49\x2a\x7d\x99\x4b\xeb\xa0\x61\x1b\x2b\x94\xa2\xd1\xc1\x17\x99\x6c\x4c\x09\xb9\x3c\x38\x97\x22\x15\xcb\x8b\xe4\x61\x9a\x50\xac\x0d\x04\x89\x00\x74\xb9\x2c\x92\xbe\x7f\x31\x30\x45\x27\x30\x2c\x11\x0c\x05\x58\x0f\xf2\xe2\x7c\x51\x35\x51\x7c\x50\x56\x32\x38\x55\xbb\xf6\xfd\x48\xbb\x82\x6f\x4b\x0c\x4b\x74\x60\x1d\x18\x1b\xfa\x9a\xb4\xb6\x2e\x61\xbf\xa8\xbf\x21\xcd\x0b\xde\x88\xa1\x80\x2e\x9d\x44\x0c\x59\x40\x5b\xfb\xe4\xa1\x59\x71\x71\xf6\x9f\xba\x17\xa0\x6d\x29\xf4\xc1\xa2\x93\x4e\xfe\x0e\xdf\x85\x35\x01\x7f\x85\x79\x9c\x16\x31\x2b\xc9\x4d\x49\xdb\x98\xae\x8b\x12\x4e\x13\xae\xd8\xa3\xce\xd9\xf9\xe8\x22\x67\xe9\x7b\x84\x8e\x98\xb7\x6c\x3c\x26\xe2\x61\xcd\xb4\x80\x14\x47\xed\x71\x07\xb2\xc9\x75\xbb\xb9\x6b\x10\xb5\xdf\xac\x0e\x6a\x99\x40\x39\xa5\xe0\x3d\x86\x7e\x61\xb4\x5d\x08\x7d\xb4\x87\x35\xc3\xff\xa4\x89\x87\x05\x7d\xb3\x95\x4a\x82\xf4\xf4\x7f\x1c\xcd\x8c\x9a\xae\xa2\x73\x9d\x93\xc5\x19\x61\xfe\x3a\x07\xa3\xba\x15\x3d\x76\x0a\xd2\x17\x8c\x6f\xb7\xe7\xf6\xb1\x65\x34\x2a\x36\xea\x13\xd2\xe4\xcd\x04\x3c\x06\x38\xe5\x74\xd2\xfb\x0d\x76\x49\xb2\xca\x63\x98\xfe\xcf\x02\x59\x50\xd1\x1b\xb9\xef\xd1\x46\xd0\x9d\x70\x1e\x5d\xd4\xb2\xa2\x73\x35\x01\x74\x8e\xf8\xfa\xb2\x09\xc3\xcf\x5a\xd2\x34\x80\xca\xf8\x40\xf0\xdd\xba\xdc\x28\x5d\x10\x36\xfe\x71\x77\x45\xe9\x2d\x70\x6e\x5f\xcc\x71\x23\xe8\x90\xed\x87\x4d\x5c\x08\x1f\x62\x23\x43\xb6\xad\xfc\x76\x13\x09\x16\x7b\xd9\xd5\x7b\xfc\x62\x6c\x66\xf1\x3b\x00\x00\xff\xff\x59\x27\xb0\x6d\xba\x08\x00\x00"), + }, + } + fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ + fs["/source"].(os.FileInfo), + fs["/templates"].(os.FileInfo), + } + fs["/source"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ + fs["/source/flag-types.json"].(os.FileInfo), + } + fs["/templates"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ + fs["/templates/altsrc_flags_generated.gotpl"].(os.FileInfo), + fs["/templates/cli_flags_generated.gotpl"].(os.FileInfo), + } + + return fs +}() + +type vfsgenÛ°FS map[string]interface{} + +func (fs vfsgenÛ°FS) Open(path string) (http.File, error) { + path = pathpkg.Clean("/" + path) + f, ok := fs[path] + if !ok { + return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist} + } + + switch f := f.(type) { + case *vfsgenÛ°CompressedFileInfo: + gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent)) + if err != nil { + // This should never happen because we generate the gzip bytes such that they are always valid. + panic("unexpected error reading own gzip compressed bytes: " + err.Error()) + } + return &vfsgenÛ°CompressedFile{ + vfsgenÛ°CompressedFileInfo: f, + gr: gr, + }, nil + case *vfsgenÛ°DirInfo: + return &vfsgenÛ°Dir{ + vfsgenÛ°DirInfo: f, + }, nil + default: + // This should never happen because we generate only the above types. + panic(fmt.Sprintf("unexpected type %T", f)) + } +} + +// vfsgenÛ°CompressedFileInfo is a static definition of a gzip compressed file. +type vfsgenÛ°CompressedFileInfo struct { + name string + modTime time.Time + compressedContent []byte + uncompressedSize int64 +} + +func (f *vfsgenÛ°CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) { + return nil, fmt.Errorf("cannot Readdir from file %s", f.name) +} +func (f *vfsgenÛ°CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil } + +func (f *vfsgenÛ°CompressedFileInfo) GzipBytes() []byte { + return f.compressedContent +} + +func (f *vfsgenÛ°CompressedFileInfo) Name() string { return f.name } +func (f *vfsgenÛ°CompressedFileInfo) Size() int64 { return f.uncompressedSize } +func (f *vfsgenÛ°CompressedFileInfo) Mode() os.FileMode { return 0444 } +func (f *vfsgenÛ°CompressedFileInfo) ModTime() time.Time { return f.modTime } +func (f *vfsgenÛ°CompressedFileInfo) IsDir() bool { return false } +func (f *vfsgenÛ°CompressedFileInfo) Sys() interface{} { return nil } + +// vfsgenÛ°CompressedFile is an opened compressedFile instance. +type vfsgenÛ°CompressedFile struct { + *vfsgenÛ°CompressedFileInfo + gr *gzip.Reader + grPos int64 // Actual gr uncompressed position. + seekPos int64 // Seek uncompressed position. +} + +func (f *vfsgenÛ°CompressedFile) Read(p []byte) (n int, err error) { + if f.grPos > f.seekPos { + // Rewind to beginning. + err = f.gr.Reset(bytes.NewReader(f.compressedContent)) + if err != nil { + return 0, err + } + f.grPos = 0 + } + if f.grPos < f.seekPos { + // Fast-forward. + _, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos) + if err != nil { + return 0, err + } + f.grPos = f.seekPos + } + n, err = f.gr.Read(p) + f.grPos += int64(n) + f.seekPos = f.grPos + return n, err +} +func (f *vfsgenÛ°CompressedFile) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + f.seekPos = 0 + offset + case io.SeekCurrent: + f.seekPos += offset + case io.SeekEnd: + f.seekPos = f.uncompressedSize + offset + default: + panic(fmt.Errorf("invalid whence value: %v", whence)) + } + return f.seekPos, nil +} +func (f *vfsgenÛ°CompressedFile) Close() error { + return f.gr.Close() +} + +// vfsgenÛ°DirInfo is a static definition of a directory. +type vfsgenÛ°DirInfo struct { + name string + modTime time.Time + entries []os.FileInfo +} + +func (d *vfsgenÛ°DirInfo) Read([]byte) (int, error) { + return 0, fmt.Errorf("cannot Read from directory %s", d.name) +} +func (d *vfsgenÛ°DirInfo) Close() error { return nil } +func (d *vfsgenÛ°DirInfo) Stat() (os.FileInfo, error) { return d, nil } + +func (d *vfsgenÛ°DirInfo) Name() string { return d.name } +func (d *vfsgenÛ°DirInfo) Size() int64 { return 0 } +func (d *vfsgenÛ°DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir } +func (d *vfsgenÛ°DirInfo) ModTime() time.Time { return d.modTime } +func (d *vfsgenÛ°DirInfo) IsDir() bool { return true } +func (d *vfsgenÛ°DirInfo) Sys() interface{} { return nil } + +// vfsgenÛ°Dir is an opened dir instance. +type vfsgenÛ°Dir struct { + *vfsgenÛ°DirInfo + pos int // Position within entries for Seek and Readdir. +} + +func (d *vfsgenÛ°Dir) Seek(offset int64, whence int) (int64, error) { + if offset == 0 && whence == io.SeekStart { + d.pos = 0 + return 0, nil + } + return 0, fmt.Errorf("unsupported Seek in directory %s", d.name) +} + +func (d *vfsgenÛ°Dir) Readdir(count int) ([]os.FileInfo, error) { + if d.pos >= len(d.entries) && count > 0 { + return nil, io.EOF + } + if count <= 0 || count > len(d.entries)-d.pos { + count = len(d.entries) - d.pos + } + e := d.entries[d.pos : d.pos+count] + d.pos += count + return e, nil +} diff --git a/flag-generator/go.mod b/flag-generator/go.mod new file mode 100644 index 0000000..c93d180 --- /dev/null +++ b/flag-generator/go.mod @@ -0,0 +1,9 @@ +module github.com/urfave/cli/flag-generator + +go 1.12 + +require ( + github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 + github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd + github.com/urfave/cli v1.21.0 +) diff --git a/flag-generator/go.sum b/flag-generator/go.sum new file mode 100644 index 0000000..e68bf88 --- /dev/null +++ b/flag-generator/go.sum @@ -0,0 +1,9 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE= +github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/flag-generator/main.go b/flag-generator/main.go index ce5e389..1f6d5b6 100644 --- a/flag-generator/main.go +++ b/flag-generator/main.go @@ -2,10 +2,16 @@ package main import ( "encoding/json" + "fmt" + "github.com/shurcooL/httpfs/union" + "github.com/shurcooL/vfsgen" "github.com/urfave/cli" + "io/ioutil" "log" + "net/http" "os" "text/template" + "time" ) type CliFlagInfo struct { @@ -25,178 +31,133 @@ type FlagType struct { ParserCast string `json:"parser_cast"` } -var flagTemplate = `// Code generated by fg; DO NOT EDIT. - -package {{ .PackageName }} -{{ if eq .PackageName "cli" }} -import ( - "flag" - "strconv" - "time" -) -{{ range $i, $flag := .Flags }} -// {{ $flag.Name }}Flag is a flag with type {{ $flag.Type }}{{ $flag.Doctail }} -type {{ $flag.Name }}Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - {{- if eq $flag.Value true }} - Value {{ $flag.Type }} - {{- end }} - {{- if eq $flag.Destination true }} - Destination *{{ $flag.Type }} - {{- end }} +// zeroModTimeFileSystem is an http.FileSystem wrapper. +// It exposes a filesystem exactly like Source, except +// all file modification times are changed to zero. +// See https://github.com/shurcooL/vfsgen/pull/40#issuecomment-355416103 +type zeroModTimeFileSystem struct { + Source http.FileSystem } -// String returns a readable representation of this value -// (for usage defaults) -func (f {{ $flag.Name }}Flag) String() string { - return FlagStringer(f) +func (fs zeroModTimeFileSystem) Open(name string) (http.File, error) { + f, err := fs.Source.Open(name) + return file{f}, err } -// GetName returns the name of the flag -func (f {{ $flag.Name }}Flag) GetName() string { - return f.Name +type file struct { + http.File } -// IsRequired returns whether or not the flag is required -func (f {{ $flag.Name }}Flag) IsRequired() bool { - return f.Required +func (f file) Stat() (os.FileInfo, error) { + fi, err := f.File.Stat() + return fileInfo{fi}, err } -// {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns -// {{ $flag.ContextDefault }} if not found -func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { - return lookup{{ $flag.Name }}(name, c.flagSet) +type fileInfo struct { + os.FileInfo } -// Global{{ $flag.Name }} looks up the value of a global {{ $flag.Name }}Flag, returns -// {{ $flag.ContextDefault }} if not found -func (c *Context) Global{{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookup{{ $flag.Name }}(name, fs) - } - return {{ $flag.ContextDefault }} -} +func (fi fileInfo) ModTime() time.Time { return time.Time{} } -func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { - f := set.Lookup(name) - if f != nil { - {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} - if err != nil { - return {{ $flag.ContextDefault }} - } - {{ if ne .ParserCast "" }}return {{ $flag.ParserCast }}{{ else }}return parsed{{ end }} - } - return {{ $flag.ContextDefault }} -} -{{- end }} -{{ else }} -import ( - "flag" - "github.com/urfave/cli" -) +func main() { + app := cli.NewApp() -{{ range $i, $flag := .Flags }} -// {{ $flag.Name }}Flag is the flag type that wraps cli.{{ $flag.Name }}Flag to allow -// for other values to be specified -type {{ $flag.Name }}Flag struct { - cli.{{ $flag.Name }}Flag - set *flag.FlagSet -} + app.Name = "fg" + app.Usage = "Generate flag type code!" + app.Version = "v0.1.0" -// New{{ $flag.Name }}Flag creates a new {{ $flag.Name }}Flag -func New{{ $flag.Name }}Flag(fl cli.{{ $flag.Name }}Flag) *{{ $flag.Name }}Flag { - return &{{ $flag.Name }}Flag{ {{ $flag.Name }}Flag: fl, set: nil } -} + app.Action = ActionFunc -// Apply saves the flagSet for later usage calls, then calls -// the wrapped {{ $flag.Name }}Flag.Apply -func (f *{{ $flag.Name }}Flag) Apply(set *flag.FlagSet) { - f.set = set - f.{{ $flag.Name }}Flag.Apply(set) -} + err := GenerateAssets() + if err != nil { + log.Fatal(err) + } -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped {{ $flag.Name }}Flag.ApplyWithError -func (f *{{ $flag.Name }}Flag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.{{ $flag.Name }}Flag.ApplyWithError(set) + err = app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } -{{- end }} -{{- end }} -` -func main() { - var packageName, inputPath, outputPath string +func GenerateAssets() error { + fs := zeroModTimeFileSystem{ + Source: union.New(map[string]http.FileSystem{ + "/templates": http.Dir("templates"), + "/source": http.Dir("source"), + }), + } - app := cli.NewApp() + return vfsgen.Generate(fs, vfsgen.Options{ + PackageName: "main", + VariableName: "fs", + }) +} - app.Name = "fg" - app.Usage = "Generate flag type code!" - app.Version = "v0.1.0" +func ActionFunc(_ *cli.Context) error { + var info CliFlagInfo + var tpl *template.Template - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "package, p", - Value: "cli", - Usage: "`PACKAGE` for which the flag types will be generated", - Destination: &packageName, - }, - cli.StringFlag{ - Name: "input, i", - Usage: "path to the `INPUT JSON FILE` which defines each type to be generated", - Destination: &inputPath, - }, - cli.StringFlag{ - Name: "output, o", - Usage: "path to the `OUTPUT GO FILE` which will contain the flag types", - Destination: &outputPath, - }, + inFile, err := fs.Open("/source/flag-types.json") + if err != nil { + log.Fatal(err) } - app.Action = func(c *cli.Context) error { - var info CliFlagInfo - var tpl *template.Template + decoder := json.NewDecoder(inFile) + err = decoder.Decode(&info.Flags) + if err != nil { + log.Fatal(err) + } + err = inFile.Close() + if err != nil { + log.Fatal(err) + } + + for _, packageName := range []string{"cli", "altsrc"} { info.PackageName = packageName - inFile, err := os.Open(inputPath) + bytes, err := ReadTemplate(packageName) if err != nil { log.Fatal(err) } - defer inFile.Close() + tpl = template.Must(template.New("").Parse(string(bytes))) - decoder := json.NewDecoder(inFile) + var outFile *os.File - err = decoder.Decode(&info.Flags) + if packageName == "cli" { + outFile, err = os.Create("flag_generated.go") + } else { + outFile, err = os.Create("altsrc/flag_generated.go") + } if err != nil { log.Fatal(err) } - tpl = template.Must(template.New("").Parse(flagTemplate)) - - outFile, err := os.Create(outputPath) + err = tpl.Execute(outFile, info) if err != nil { log.Fatal(err) } - defer outFile.Close() - - err = tpl.Execute(outFile, info) + err = outFile.Close() if err != nil { log.Fatal(err) } + } + + return nil +} - return nil +func ReadTemplate(packageName string) ([]byte, error) { + templateFile, err := fs.Open(fmt.Sprintf("/templates/%s_flags_generated.gotpl", packageName)) + if err != nil { + return nil, err } - err := app.Run(os.Args) + templateFileBytes, err := ioutil.ReadAll(templateFile) if err != nil { - log.Fatal(err) + return nil, err } + + return templateFileBytes, nil } diff --git a/flag-types.json b/flag-generator/source/flag-types.json similarity index 100% rename from flag-types.json rename to flag-generator/source/flag-types.json diff --git a/flag-generator/templates/altsrc_flags_generated.gotpl b/flag-generator/templates/altsrc_flags_generated.gotpl new file mode 100644 index 0000000..54feb84 --- /dev/null +++ b/flag-generator/templates/altsrc_flags_generated.gotpl @@ -0,0 +1,36 @@ +// Code generated by fg; DO NOT EDIT. + +package {{ .PackageName }} + +import ( + "flag" + "github.com/urfave/cli" +) + +{{ range $i, $flag := .Flags }} +// {{ $flag.Name }}Flag is the flag type that wraps cli.{{ $flag.Name }}Flag to allow +// for other values to be specified +type {{ $flag.Name }}Flag struct { + cli.{{ $flag.Name }}Flag + set *flag.FlagSet +} + +// New{{ $flag.Name }}Flag creates a new {{ $flag.Name }}Flag +func New{{ $flag.Name }}Flag(fl cli.{{ $flag.Name }}Flag) *{{ $flag.Name }}Flag { + return &{{ $flag.Name }}Flag{ {{ $flag.Name }}Flag: fl, set: nil } +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped {{ $flag.Name }}Flag.Apply +func (f *{{ $flag.Name }}Flag) Apply(set *flag.FlagSet) { + f.set = set + f.{{ $flag.Name }}Flag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped {{ $flag.Name }}Flag.ApplyWithError +func (f *{{ $flag.Name }}Flag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.{{ $flag.Name }}Flag.ApplyWithError(set) +} +{{- end }} \ No newline at end of file diff --git a/flag-generator/templates/cli_flags_generated.gotpl b/flag-generator/templates/cli_flags_generated.gotpl new file mode 100644 index 0000000..4f148c6 --- /dev/null +++ b/flag-generator/templates/cli_flags_generated.gotpl @@ -0,0 +1,69 @@ +// Code generated by fg; DO NOT EDIT. + +package {{ .PackageName }} + +import ( + "flag" + "strconv" + "time" +) +{{ range $i, $flag := .Flags }} +// {{ $flag.Name }}Flag is a flag with type {{ $flag.Type }}{{ $flag.Doctail }} +type {{ $flag.Name }}Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + {{- if eq $flag.Value true }} + Value {{ $flag.Type }} + {{- end }} + {{- if eq $flag.Destination true }} + Destination *{{ $flag.Type }} + {{- end }} +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f {{ $flag.Name }}Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f {{ $flag.Name }}Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f {{ $flag.Name }}Flag) IsRequired() bool { + return f.Required +} + +// {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns +// {{ $flag.ContextDefault }} if not found +func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { + return lookup{{ $flag.Name }}(name, c.flagSet) +} + +// Global{{ $flag.Name }} looks up the value of a global {{ $flag.Name }}Flag, returns +// {{ $flag.ContextDefault }} if not found +func (c *Context) Global{{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookup{{ $flag.Name }}(name, fs) + } + return {{ $flag.ContextDefault }} +} + +func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { + f := set.Lookup(name) + if f != nil { + {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} + if err != nil { + return {{ $flag.ContextDefault }} + } + {{ if ne .ParserCast "" }}return {{ $flag.ParserCast }}{{ else }}return parsed{{ end }} + } + return {{ $flag.ContextDefault }} +} +{{- end }} \ No newline at end of file From 2a084945a47235959c023291f87127ead86fc168 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 10:28:08 +0530 Subject: [PATCH 134/215] move around code change package to flag-gen to avoid conflict with flag-generator binary test code generation --- {flag-generator => flag-gen}/.gitignore | 0 flag-gen/assets_generate.go | 53 +++++++++++++++++ .../assets_vfsdata.go | 4 +- {flag-generator => flag-gen}/go.mod | 0 {flag-generator => flag-gen}/go.sum | 0 {flag-generator => flag-gen}/main.go | 59 ++----------------- .../source/flag-types.json | 0 .../templates/altsrc_flags_generated.gotpl | 0 .../templates/cli_flags_generated.gotpl | 0 9 files changed, 60 insertions(+), 56 deletions(-) rename {flag-generator => flag-gen}/.gitignore (100%) create mode 100644 flag-gen/assets_generate.go rename flag-generator/fs_vfsdata.go => flag-gen/assets_vfsdata.go (99%) rename {flag-generator => flag-gen}/go.mod (100%) rename {flag-generator => flag-gen}/go.sum (100%) rename {flag-generator => flag-gen}/main.go (59%) rename {flag-generator => flag-gen}/source/flag-types.json (100%) rename {flag-generator => flag-gen}/templates/altsrc_flags_generated.gotpl (100%) rename {flag-generator => flag-gen}/templates/cli_flags_generated.gotpl (100%) diff --git a/flag-generator/.gitignore b/flag-gen/.gitignore similarity index 100% rename from flag-generator/.gitignore rename to flag-gen/.gitignore diff --git a/flag-gen/assets_generate.go b/flag-gen/assets_generate.go new file mode 100644 index 0000000..b587253 --- /dev/null +++ b/flag-gen/assets_generate.go @@ -0,0 +1,53 @@ +// +build ignore + +package main + +import ( + "github.com/shurcooL/httpfs/union" + "github.com/shurcooL/vfsgen" + "log" + "net/http" + "os" + "time" +) + +// zeroModTimeFileSystem is an http.FileSystem wrapper. +// It exposes a filesystem exactly like Source, except +// all file modification times are changed to zero. +// See https://github.com/shurcooL/vfsgen/pull/40#issuecomment-355416103 +type zeroModTimeFileSystem struct { + Source http.FileSystem +} + +func (fs zeroModTimeFileSystem) Open(name string) (http.File, error) { + f, err := fs.Source.Open(name) + return file{f}, err +} + +type file struct { + http.File +} + +func (f file) Stat() (os.FileInfo, error) { + fi, err := f.File.Stat() + return fileInfo{fi}, err +} + +type fileInfo struct { + os.FileInfo +} + +func (fi fileInfo) ModTime() time.Time { return time.Time{} } + +func main() { + err := vfsgen.Generate(zeroModTimeFileSystem{ + Source: union.New(map[string]http.FileSystem{ + "/templates": http.Dir("templates"), + "/source": http.Dir("source"), + }), + }, vfsgen.Options{}) + + if err != nil { + log.Fatal(err) + } +} diff --git a/flag-generator/fs_vfsdata.go b/flag-gen/assets_vfsdata.go similarity index 99% rename from flag-generator/fs_vfsdata.go rename to flag-gen/assets_vfsdata.go index 2122fcf..d039e9f 100644 --- a/flag-generator/fs_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -14,8 +14,8 @@ import ( "time" ) -// fs statically implements the virtual filesystem provided to vfsgen. -var fs = func() http.FileSystem { +// assets statically implements the virtual filesystem provided to vfsgen. +var assets = func() http.FileSystem { fs := vfsgenÛ°FS{ "/": &vfsgenÛ°DirInfo{ name: "/", diff --git a/flag-generator/go.mod b/flag-gen/go.mod similarity index 100% rename from flag-generator/go.mod rename to flag-gen/go.mod diff --git a/flag-generator/go.sum b/flag-gen/go.sum similarity index 100% rename from flag-generator/go.sum rename to flag-gen/go.sum diff --git a/flag-generator/main.go b/flag-gen/main.go similarity index 59% rename from flag-generator/main.go rename to flag-gen/main.go index 1f6d5b6..3e463ff 100644 --- a/flag-generator/main.go +++ b/flag-gen/main.go @@ -1,17 +1,15 @@ +//go:generate go run assets_generate.go + package main import ( "encoding/json" "fmt" - "github.com/shurcooL/httpfs/union" - "github.com/shurcooL/vfsgen" "github.com/urfave/cli" "io/ioutil" "log" - "net/http" "os" "text/template" - "time" ) type CliFlagInfo struct { @@ -31,34 +29,6 @@ type FlagType struct { ParserCast string `json:"parser_cast"` } -// zeroModTimeFileSystem is an http.FileSystem wrapper. -// It exposes a filesystem exactly like Source, except -// all file modification times are changed to zero. -// See https://github.com/shurcooL/vfsgen/pull/40#issuecomment-355416103 -type zeroModTimeFileSystem struct { - Source http.FileSystem -} - -func (fs zeroModTimeFileSystem) Open(name string) (http.File, error) { - f, err := fs.Source.Open(name) - return file{f}, err -} - -type file struct { - http.File -} - -func (f file) Stat() (os.FileInfo, error) { - fi, err := f.File.Stat() - return fileInfo{fi}, err -} - -type fileInfo struct { - os.FileInfo -} - -func (fi fileInfo) ModTime() time.Time { return time.Time{} } - func main() { app := cli.NewApp() @@ -68,36 +38,17 @@ func main() { app.Action = ActionFunc - err := GenerateAssets() - if err != nil { - log.Fatal(err) - } - - err = app.Run(os.Args) + err := app.Run(os.Args) if err != nil { log.Fatal(err) } } -func GenerateAssets() error { - fs := zeroModTimeFileSystem{ - Source: union.New(map[string]http.FileSystem{ - "/templates": http.Dir("templates"), - "/source": http.Dir("source"), - }), - } - - return vfsgen.Generate(fs, vfsgen.Options{ - PackageName: "main", - VariableName: "fs", - }) -} - func ActionFunc(_ *cli.Context) error { var info CliFlagInfo var tpl *template.Template - inFile, err := fs.Open("/source/flag-types.json") + inFile, err := assets.Open("/source/flag-types.json") if err != nil { log.Fatal(err) } @@ -149,7 +100,7 @@ func ActionFunc(_ *cli.Context) error { } func ReadTemplate(packageName string) ([]byte, error) { - templateFile, err := fs.Open(fmt.Sprintf("/templates/%s_flags_generated.gotpl", packageName)) + templateFile, err := assets.Open(fmt.Sprintf("/templates/%s_flags_generated.gotpl", packageName)) if err != nil { return nil, err } diff --git a/flag-generator/source/flag-types.json b/flag-gen/source/flag-types.json similarity index 100% rename from flag-generator/source/flag-types.json rename to flag-gen/source/flag-types.json diff --git a/flag-generator/templates/altsrc_flags_generated.gotpl b/flag-gen/templates/altsrc_flags_generated.gotpl similarity index 100% rename from flag-generator/templates/altsrc_flags_generated.gotpl rename to flag-gen/templates/altsrc_flags_generated.gotpl diff --git a/flag-generator/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl similarity index 100% rename from flag-generator/templates/cli_flags_generated.gotpl rename to flag-gen/templates/cli_flags_generated.gotpl From c676ed4caa76219414ad737584ec46b03e94cbf8 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 11:04:23 +0530 Subject: [PATCH 135/215] indentation fixes in template regeneration test --- altsrc/flag_generated.go | 1 - flag-gen/assets_vfsdata.go | 8 ++++---- flag-gen/templates/altsrc_flags_generated.gotpl | 3 +-- flag-gen/templates/cli_flags_generated.gotpl | 8 ++++---- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go index bf2c488..0c45914 100644 --- a/altsrc/flag_generated.go +++ b/altsrc/flag_generated.go @@ -4,7 +4,6 @@ package altsrc import ( "flag" - "github.com/urfave/cli" ) diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index d039e9f..4b89ce3 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -39,16 +39,16 @@ var assets = func() http.FileSystem { "/templates/altsrc_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "altsrc_flags_generated.gotpl", modTime: time.Time{}, - uncompressedSize: 1064, + uncompressedSize: 1063, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x41\x6b\xdc\x30\x10\x85\xcf\xd1\xaf\x78\x2c\xa1\xac\xc3\xd6\xbe\x6f\xc9\xa1\x34\x29\xf4\xb2\x29\x24\xd0\xb3\xd6\x1e\xd9\xa2\x5a\xdb\x48\xe3\x5d\x16\xe1\xff\x5e\x46\x0e\xe9\x96\x6a\x4b\x2f\xb9\xc9\x1a\xbd\xf7\xe6\x9b\x71\x55\xe1\xcb\xd0\x10\x5a\xea\xc9\x6b\xa6\x06\xfb\x33\x4c\xfb\x09\x0f\x4f\xd8\x3d\xbd\xe0\xf1\xe1\xdb\x4b\xa9\xd4\xa8\xeb\x9f\xba\x25\xc4\x88\xf2\xfb\x72\xde\xe9\x03\x61\x9e\x95\xb2\x87\x71\xf0\x8c\xb5\xba\x59\x19\xa7\xdb\x95\x02\x80\x55\x6b\xb9\x9b\xf6\x65\x3d\x1c\xaa\xc9\x1b\x7d\xa4\xaa\x76\x76\xa5\x0a\xa5\x62\x84\xd7\x7d\x4b\xb8\xb5\x1b\xdc\x8a\x04\xdb\x7b\x94\x5f\x9d\x6e\x83\x18\x56\x95\xc4\xa4\x42\xf9\x1a\x22\x35\xd8\x00\xee\x08\x49\xc0\xe7\x91\xc0\x9d\x66\x9c\xbc\x1e\x03\x6a\x67\xcb\xac\x88\x07\x68\xe7\x86\x93\xb8\x9a\xc1\x63\xe0\x8e\x3c\x8e\xda\x4d\x14\xa4\xb8\x27\x84\x91\x6a\x6b\x2c\x35\x2a\xb9\x66\x6d\x02\xfb\xa9\x66\xc4\xc4\x76\x2d\x2c\x15\x03\x31\xee\x52\x45\xae\x9e\x89\xd5\xac\x24\x7c\x47\xa7\xac\x73\xed\x49\x33\x05\x68\xf4\x74\xca\x86\x2b\x33\xf5\xf5\x35\xfd\xda\xb8\xab\xfd\x14\xb8\xcb\x46\x2e\x14\x9e\x78\xf2\x3d\x3e\xe4\x9e\xc4\x6c\x23\x5b\x18\xb7\x11\xc0\x2d\x7a\xeb\x30\xbf\x92\x7d\x1e\x47\x77\x46\xd0\x47\xfa\xbd\xa0\x67\xe2\x34\x6e\xa7\x99\x3c\xa6\x20\xff\x4e\xad\x9d\x0b\x1b\x79\xd2\x2f\x67\x11\x8b\x40\x56\x38\x52\x93\xcd\x2c\x93\xfb\x32\x82\xb5\xc9\x03\x15\x4b\x0b\xeb\xbf\x46\x5f\x20\xaa\x1b\x53\xca\xfd\xbd\xf4\x2d\x1f\xd7\x43\x44\x5f\x5c\x32\xfd\xb0\xdc\x3d\x7a\x3f\xf8\xf7\x85\x7b\x8b\xf9\x2f\xca\xb7\xd7\x39\x5c\x4a\xdd\x2e\xfb\xbd\xe4\xbe\xd8\xf7\xbf\x26\xf0\x87\xb7\x8c\x22\xc6\x8f\xa0\xbe\xc1\x3c\xff\x0a\x00\x00\xff\xff\x3c\x32\xe5\x42\x28\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x41\x8b\xdb\x30\x10\x85\xcf\xab\x5f\xf1\x08\x4b\x89\x97\x60\xdf\x53\xf6\x50\xba\x5b\xe8\x25\x5b\xd8\x85\x9e\x15\x7b\x64\x8b\x2a\xb6\x91\xc6\x09\x41\xf8\xbf\x97\x91\x97\x34\xa5\x4a\xe9\xa5\x37\x59\xa3\xf7\xde\x7c\x33\xae\x2a\x7c\x1e\x1a\x42\x4b\x3d\x79\xcd\xd4\x60\x7f\x86\x69\x3f\xe2\xe9\x05\xbb\x97\x37\x3c\x3f\x7d\x7d\x2b\x95\x1a\x75\xfd\x43\xb7\x84\x18\x51\x7e\x5b\xce\x3b\x7d\x20\xcc\xb3\x52\xf6\x30\x0e\x9e\xb1\x56\x77\x2b\xe3\x74\xbb\x52\x00\xb0\x6a\x2d\x77\xd3\xbe\xac\x87\x43\x35\x79\xa3\x8f\x54\xd5\xce\xae\x54\xa1\x54\x8c\xf0\xba\x6f\x09\xf7\x76\x83\x7b\x91\x60\xfb\x88\xf2\x8b\xd3\x6d\x10\xc3\xaa\x92\x98\x54\x28\xdf\x43\xa4\x06\x1b\xc0\x1d\x21\x09\xf8\x3c\x12\xb8\xd3\x8c\x93\xd7\x63\x40\xed\x6c\x99\x15\xf1\x00\xed\xdc\x70\x12\x57\x33\x78\x0c\xdc\x91\xc7\x51\xbb\x89\x82\x14\xf7\x84\x30\x52\x6d\x8d\xa5\x46\x25\xd7\xac\x4d\x60\x3f\xd5\x8c\x98\xd8\x6e\x85\xa5\x62\x20\xc6\x43\xaa\xc8\xd5\x2b\xb1\x9a\x95\x84\xef\xe8\x94\x75\xae\x3d\x69\xa6\x00\x8d\x9e\x4e\xd9\x70\x65\xa6\xbe\xbe\xa5\x5f\x1b\x77\xb3\x9f\x02\x0f\xd9\xc8\x85\xc2\x13\x4f\xbe\xc7\x87\xdc\x93\x98\x6d\x64\x0b\xe3\x36\x02\xb8\x45\x6f\x1d\xe6\x77\xb2\x4f\xe3\xe8\xce\x08\xfa\x48\xbf\x16\xf4\x4a\x9c\xc6\xed\x34\x93\xc7\x14\xe4\xdf\xa9\xb5\x73\x61\x23\x4f\xfa\xe5\x2c\x62\x11\xc8\x0a\x47\x6a\xb2\x99\x65\x72\x5f\x46\xb0\x36\x79\xa0\x62\x69\x61\xfd\xc7\xe8\x0b\x44\x75\x67\x4a\xb9\x7f\x94\xbe\xe5\xe3\x76\x88\xe8\x8b\x6b\xa6\xef\x96\xbb\x67\xef\x07\xff\x7f\xe1\x2e\x31\xff\x44\x79\x79\x9d\xc3\xa5\xd4\xed\xb2\xdf\x6b\xee\xab\x7d\xff\x6d\x02\xbf\x79\xcb\x28\x62\x04\xf5\x0d\xe6\xf9\x67\x00\x00\x00\xff\xff\x1d\x0d\x69\x06\x27\x04\x00\x00"), }, "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", modTime: time.Time{}, - uncompressedSize: 2234, + uncompressedSize: 2230, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x41\x4f\xeb\x38\x10\x3e\x37\xbf\x62\xb6\xe2\x90\xa0\x6e\x7b\x5f\xc4\x89\xc2\x2e\xd2\x0a\xd0\x83\xc7\xdd\x4d\xc6\xa9\x85\xb1\x8b\xed\xc0\x43\x55\xfe\xfb\xd3\x8c\x9d\xb4\x4d\x0b\xf4\xf2\xe8\xc9\x9e\xf9\xf2\xcd\x37\xdf\x8c\xea\xd9\x0c\x2e\x6c\x85\x50\xa3\x41\x27\x02\x56\xb0\x78\x07\x59\x9f\xc1\xfc\x16\x6e\x6e\x1f\xe0\x72\x7e\xfd\x30\xcd\xb2\x95\x28\x9f\x44\x8d\xb0\x5e\xc3\xf4\x2e\x9e\x6f\xc4\x33\x42\xdb\x66\x99\x7a\x5e\x59\x17\x20\xcf\x46\x63\xa9\x45\x3d\xce\x46\x63\x1f\x5c\x69\xcd\x2b\x1d\x83\x7a\xc6\x71\x56\x64\xeb\x35\x38\x61\x6a\x84\x13\x35\x81\x13\x02\xc2\x3f\xe7\x30\xbd\xd2\xa2\xf6\x44\x33\x9b\x11\x39\x27\xa6\x89\x9a\x72\xa0\x3c\x08\x60\xf8\x9b\x0a\x4b\x08\xef\x2b\xdc\x00\x1f\xe8\xd6\xb6\xfd\x7d\x6e\xcb\x20\x94\x26\xbe\x5d\xe0\x36\xa3\x0f\xae\x29\x03\xac\x33\x00\x00\x4e\xa4\x9f\x0f\x4e\x99\x9a\xc3\x3f\x3d\x75\xbb\x17\xbe\x34\xaf\x8f\xc2\xed\x85\xaf\x94\xc6\x3b\x11\x96\x83\xf0\x0f\x7c\x69\x94\xc3\x8a\xce\x0b\x6b\x35\x07\xff\x53\x55\x85\x26\x52\xf4\xc1\xf5\xfa\x6f\x50\x12\xf0\x25\xc9\x7d\x14\xba\x41\x08\xae\x61\x87\x47\x7c\x1d\x8d\x86\x6d\x67\x23\xfa\x0e\x4d\xd5\x9f\xb7\x39\xe6\xe8\x83\x32\x22\x28\x6b\x36\x4c\x5b\xc1\xd1\xe9\xa7\x7c\x6d\x46\x23\xb9\xe7\x6e\xc0\x61\x68\x9c\xa1\x49\x38\x14\x95\x58\x68\x04\x87\x2b\x87\x1e\x4d\x88\x15\xac\x84\xb0\x54\x1e\x5e\x49\x2a\x7d\x99\x4b\xeb\xa0\x61\x1b\x2b\x94\xa2\xd1\xc1\x17\x99\x6c\x4c\x09\xb9\x3c\x38\x97\x22\x15\xcb\x8b\xe4\x61\x9a\x50\xac\x0d\x04\x89\x00\x74\xb9\x2c\x92\xbe\x7f\x31\x30\x45\x27\x30\x2c\x11\x0c\x05\x58\x0f\xf2\xe2\x7c\x51\x35\x51\x7c\x50\x56\x32\x38\x55\xbb\xf6\xfd\x48\xbb\x82\x6f\x4b\x0c\x4b\x74\x60\x1d\x18\x1b\xfa\x9a\xb4\xb6\x2e\x61\xbf\xa8\xbf\x21\xcd\x0b\xde\x88\xa1\x80\x2e\x9d\x44\x0c\x59\x40\x5b\xfb\xe4\xa1\x59\x71\x71\xf6\x9f\xba\x17\xa0\x6d\x29\xf4\xc1\xa2\x93\x4e\xfe\x0e\xdf\x85\x35\x01\x7f\x85\x79\x9c\x16\x31\x2b\xc9\x4d\x49\xdb\x98\xae\x8b\x12\x4e\x13\xae\xd8\xa3\xce\xd9\xf9\xe8\x22\x67\xe9\x7b\x84\x8e\x98\xb7\x6c\x3c\x26\xe2\x61\xcd\xb4\x80\x14\x47\xed\x71\x07\xb2\xc9\x75\xbb\xb9\x6b\x10\xb5\xdf\xac\x0e\x6a\x99\x40\x39\xa5\xe0\x3d\x86\x7e\x61\xb4\x5d\x08\x7d\xb4\x87\x35\xc3\xff\xa4\x89\x87\x05\x7d\xb3\x95\x4a\x82\xf4\xf4\x7f\x1c\xcd\x8c\x9a\xae\xa2\x73\x9d\x93\xc5\x19\x61\xfe\x3a\x07\xa3\xba\x15\x3d\x76\x0a\xd2\x17\x8c\x6f\xb7\xe7\xf6\xb1\x65\x34\x2a\x36\xea\x13\xd2\xe4\xcd\x04\x3c\x06\x38\xe5\x74\xd2\xfb\x0d\x76\x49\xb2\xca\x63\x98\xfe\xcf\x02\x59\x50\xd1\x1b\xb9\xef\xd1\x46\xd0\x9d\x70\x1e\x5d\xd4\xb2\xa2\x73\x35\x01\x74\x8e\xf8\xfa\xb2\x09\xc3\xcf\x5a\xd2\x34\x80\xca\xf8\x40\xf0\xdd\xba\xdc\x28\x5d\x10\x36\xfe\x71\x77\x45\xe9\x2d\x70\x6e\x5f\xcc\x71\x23\xe8\x90\xed\x87\x4d\x5c\x08\x1f\x62\x23\x43\xb6\xad\xfc\x76\x13\x09\x16\x7b\xd9\xd5\x7b\xfc\x62\x6c\x66\xf1\x3b\x00\x00\xff\xff\x59\x27\xb0\x6d\xba\x08\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x41\x4f\xeb\x38\x10\x3e\x37\xbf\x62\xb6\xe2\x90\xa0\x6e\x7b\x5f\xc4\x89\xc2\x2e\xd2\x0a\xd0\x83\xc7\xdd\x4d\xc6\xa9\x85\xb1\x8b\xed\xc0\x43\x51\xff\xfb\xd3\x8c\x9d\xb4\x4d\x0b\xf4\xc2\xeb\xc9\x9e\xf9\xfc\xcd\x37\xdf\x8c\x9a\xd9\x0c\x2e\x6c\x85\x50\xa3\x41\x27\x02\x56\xb0\x78\x07\x59\x9f\xc1\xfc\x16\x6e\x6e\x1f\xe0\x72\x7e\xfd\x30\xcd\xb2\x95\x28\x9f\x44\x8d\xd0\xb6\x30\xbd\x8b\xe7\x1b\xf1\x8c\xb0\x5e\x67\x99\x7a\x5e\x59\x17\x20\xcf\x46\x63\xa9\x45\x3d\xce\x46\x63\x1f\x5c\x69\xcd\x2b\x1d\x83\x7a\xc6\x71\x56\x64\x6d\x0b\x4e\x98\x1a\xe1\x44\x4d\xe0\x84\x80\xf0\xcf\x39\x4c\xaf\xb4\xa8\x3d\xd1\xcc\x66\x44\xce\x89\x69\xa2\xa6\x1c\x28\x0f\x02\x18\xfe\xa6\xc2\x12\xc2\xfb\x0a\x37\xc0\x07\xba\xad\xd7\xfd\x7d\x6e\xcb\x20\x94\x26\xbe\x5d\xe0\x36\xa3\x0f\xae\x29\x03\xb4\x19\x00\x00\x27\xd2\xcf\x07\xa7\x4c\xcd\xe1\x9f\x9e\xba\xdd\x0b\x5f\x9a\xd7\x47\xe1\xf6\xc2\x57\x4a\xe3\x9d\x08\xcb\x41\xf8\x07\xbe\x34\xca\x61\x45\xe7\x85\xb5\x9a\x83\xff\xa9\xaa\x42\x13\x29\xfa\x60\xdb\xfe\x0d\x4a\x02\xbe\x24\xb9\x8f\x42\x37\x08\xc1\x35\xec\xf0\x88\xaf\xa3\xd1\xb0\xed\x6c\x44\xef\xd0\x54\xfd\x79\x9b\x63\x8e\x3e\x28\x23\x82\xb2\x66\xc3\xb4\x15\x1c\x9d\x7e\xca\xb7\xce\x68\x24\xf7\xdc\x0d\x38\x0c\x8d\x33\x34\x09\x87\xa2\x12\x0b\x8d\xe0\x70\xe5\xd0\xa3\x09\xb1\x82\x95\x10\x96\xca\xc3\x2b\x49\xa5\x97\xb9\xb4\x0e\x1a\xb6\xb1\x42\x29\x1a\x1d\x7c\x91\xc9\xc6\x94\x90\xcb\x83\x73\x29\x52\xb1\xbc\x48\x1e\xa6\x09\xc5\xda\x40\x90\x08\x40\x97\xcb\x22\xe9\xfb\x17\x03\x53\x74\x02\xc3\x12\xc1\x50\x80\xf5\x20\x2f\xce\x17\x55\x13\xc5\x07\x65\x25\x83\x53\xb5\x6b\xdf\x8f\xb4\x2b\xf8\xb6\xc4\xb0\x44\x07\xd6\x81\xb1\xa1\xaf\x49\x6b\xeb\x12\xf6\x8b\xfa\x1b\xd2\xbc\xe0\x8d\x18\x0a\xe8\xd2\x49\xc4\x90\x05\xb4\xb5\x4f\x1e\x9a\x15\x17\x67\xff\xa9\x7b\x01\xda\x96\x42\x1f\x2c\x3a\xe9\xe4\xef\xf0\x5d\x58\x13\xf0\x57\x98\xc7\x69\x11\xb3\x92\xdc\x94\xb4\x8d\xe9\xba\x28\xe1\x34\xe1\x8a\x3d\xea\x9c\x9d\x8f\x2e\x16\x6d\xcb\xcf\x11\x3a\x5e\x5e\xb2\xf1\x98\x78\x87\x25\xd3\xfe\x51\x1c\xb5\xc7\x1d\xc8\x26\xd7\xad\xe6\xae\x3f\xd4\x7d\xb3\x3a\x28\x65\x02\xe5\x94\x82\xf7\x18\xfa\x7d\xd1\x76\x21\xf4\xd1\x16\xd6\x0c\xff\x4e\x0f\x0f\x0b\xfa\xb3\x4e\x2a\x09\xd2\xd3\xbf\x71\xf4\x32\x4a\xba\x8a\xc6\x75\x46\x16\x67\x84\xf9\xeb\x1c\x8c\xea\x16\xf4\xd8\x21\x48\x5f\x30\x7e\xbd\x3d\xb6\x8f\x1d\xa3\x49\xb1\x4f\x9f\x90\x26\x6b\x26\xe0\x31\xc0\x29\xa7\x93\xde\xef\x77\x4b\x92\x53\x1e\xc3\xf4\x7f\xd6\xc7\x7a\x8a\xde\xc7\x7d\x8b\x36\x82\xee\x84\xf3\xe8\xa2\x96\x15\x9d\xab\x09\xa0\x73\xc4\xd7\x97\x4d\x18\xfe\xa6\x25\x4d\x03\xa8\x8c\x5f\x07\xbe\x5b\x97\x1b\xa5\xa9\xe7\xf4\xaf\xdd\x15\xa5\x0f\x81\x73\xfb\x62\x8e\x9b\x40\x87\x5c\x7f\xd8\xc4\x85\xf0\x21\x36\x32\x64\xdb\xca\x6f\x37\x91\x60\xb1\x97\x5d\xbd\xc7\xef\x45\xff\xee\x77\x00\x00\x00\xff\xff\x4d\xaf\x90\xd0\xb6\x08\x00\x00"), }, } fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ diff --git a/flag-gen/templates/altsrc_flags_generated.gotpl b/flag-gen/templates/altsrc_flags_generated.gotpl index 54feb84..e89eb8e 100644 --- a/flag-gen/templates/altsrc_flags_generated.gotpl +++ b/flag-gen/templates/altsrc_flags_generated.gotpl @@ -6,7 +6,6 @@ import ( "flag" "github.com/urfave/cli" ) - {{ range $i, $flag := .Flags }} // {{ $flag.Name }}Flag is the flag type that wraps cli.{{ $flag.Name }}Flag to allow // for other values to be specified @@ -33,4 +32,4 @@ func (f *{{ $flag.Name }}Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.{{ $flag.Name }}Flag.ApplyWithError(set) } -{{- end }} \ No newline at end of file +{{ end }} \ No newline at end of file diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index 4f148c6..3a6ca7c 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -42,20 +42,20 @@ func (f {{ $flag.Name }}Flag) IsRequired() bool { // {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns // {{ $flag.ContextDefault }} if not found -func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { +func (c *Context) {{ $flag.Name }}(name string){{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { return lookup{{ $flag.Name }}(name, c.flagSet) } // Global{{ $flag.Name }} looks up the value of a global {{ $flag.Name }}Flag, returns // {{ $flag.ContextDefault }} if not found -func (c *Context) Global{{ $flag.Name }}(name string) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { +func (c *Context) Global{{ $flag.Name }}(name string){{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookup{{ $flag.Name }}(name, fs) } return {{ $flag.ContextDefault }} } -func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { +func lookup{{ $flag.Name }}(name string, set *flag.FlagSet){{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { f := set.Lookup(name) if f != nil { {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} @@ -66,4 +66,4 @@ func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextTyp } return {{ $flag.ContextDefault }} } -{{- end }} \ No newline at end of file +{{ end }} \ No newline at end of file From 86e10211dea0c2b369610aabea1c987888bbb01a Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 11:05:50 +0530 Subject: [PATCH 136/215] remove redundant go generate from altsrc update go generate in cli package to generate both files regeneration test --- altsrc/altsrc.go | 3 - altsrc/flag_generated.go | 346 ------------------- cli.go | 2 +- flag_generated.go | 718 --------------------------------------- 4 files changed, 1 insertion(+), 1068 deletions(-) delete mode 100644 altsrc/altsrc.go delete mode 100644 altsrc/flag_generated.go delete mode 100644 flag_generated.go diff --git a/altsrc/altsrc.go b/altsrc/altsrc.go deleted file mode 100644 index b81c99a..0000000 --- a/altsrc/altsrc.go +++ /dev/null @@ -1,3 +0,0 @@ -package altsrc - -//go:generate fg -p altsrc -i ../flag-types.json -o flag_generated.go diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go deleted file mode 100644 index 0c45914..0000000 --- a/altsrc/flag_generated.go +++ /dev/null @@ -1,346 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package altsrc - -import ( - "flag" - "github.com/urfave/cli" -) - -// BoolFlag is the flag type that wraps cli.BoolFlag to allow -// for other values to be specified -type BoolFlag struct { - cli.BoolFlag - set *flag.FlagSet -} - -// NewBoolFlag creates a new BoolFlag -func NewBoolFlag(fl cli.BoolFlag) *BoolFlag { - return &BoolFlag{BoolFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped BoolFlag.Apply -func (f *BoolFlag) Apply(set *flag.FlagSet) { - f.set = set - f.BoolFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped BoolFlag.ApplyWithError -func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.BoolFlag.ApplyWithError(set) -} - -// BoolTFlag is the flag type that wraps cli.BoolTFlag to allow -// for other values to be specified -type BoolTFlag struct { - cli.BoolTFlag - set *flag.FlagSet -} - -// NewBoolTFlag creates a new BoolTFlag -func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag { - return &BoolTFlag{BoolTFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped BoolTFlag.Apply -func (f *BoolTFlag) Apply(set *flag.FlagSet) { - f.set = set - f.BoolTFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped BoolTFlag.ApplyWithError -func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.BoolTFlag.ApplyWithError(set) -} - -// DurationFlag is the flag type that wraps cli.DurationFlag to allow -// for other values to be specified -type DurationFlag struct { - cli.DurationFlag - set *flag.FlagSet -} - -// NewDurationFlag creates a new DurationFlag -func NewDurationFlag(fl cli.DurationFlag) *DurationFlag { - return &DurationFlag{DurationFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped DurationFlag.Apply -func (f *DurationFlag) Apply(set *flag.FlagSet) { - f.set = set - f.DurationFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped DurationFlag.ApplyWithError -func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.DurationFlag.ApplyWithError(set) -} - -// Float64Flag is the flag type that wraps cli.Float64Flag to allow -// for other values to be specified -type Float64Flag struct { - cli.Float64Flag - set *flag.FlagSet -} - -// NewFloat64Flag creates a new Float64Flag -func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag { - return &Float64Flag{Float64Flag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped Float64Flag.Apply -func (f *Float64Flag) Apply(set *flag.FlagSet) { - f.set = set - f.Float64Flag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped Float64Flag.ApplyWithError -func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.Float64Flag.ApplyWithError(set) -} - -// GenericFlag is the flag type that wraps cli.GenericFlag to allow -// for other values to be specified -type GenericFlag struct { - cli.GenericFlag - set *flag.FlagSet -} - -// NewGenericFlag creates a new GenericFlag -func NewGenericFlag(fl cli.GenericFlag) *GenericFlag { - return &GenericFlag{GenericFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped GenericFlag.Apply -func (f *GenericFlag) Apply(set *flag.FlagSet) { - f.set = set - f.GenericFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped GenericFlag.ApplyWithError -func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.GenericFlag.ApplyWithError(set) -} - -// Int64Flag is the flag type that wraps cli.Int64Flag to allow -// for other values to be specified -type Int64Flag struct { - cli.Int64Flag - set *flag.FlagSet -} - -// NewInt64Flag creates a new Int64Flag -func NewInt64Flag(fl cli.Int64Flag) *Int64Flag { - return &Int64Flag{Int64Flag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped Int64Flag.Apply -func (f *Int64Flag) Apply(set *flag.FlagSet) { - f.set = set - f.Int64Flag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped Int64Flag.ApplyWithError -func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.Int64Flag.ApplyWithError(set) -} - -// IntFlag is the flag type that wraps cli.IntFlag to allow -// for other values to be specified -type IntFlag struct { - cli.IntFlag - set *flag.FlagSet -} - -// NewIntFlag creates a new IntFlag -func NewIntFlag(fl cli.IntFlag) *IntFlag { - return &IntFlag{IntFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped IntFlag.Apply -func (f *IntFlag) Apply(set *flag.FlagSet) { - f.set = set - f.IntFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped IntFlag.ApplyWithError -func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.IntFlag.ApplyWithError(set) -} - -// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow -// for other values to be specified -type IntSliceFlag struct { - cli.IntSliceFlag - set *flag.FlagSet -} - -// NewIntSliceFlag creates a new IntSliceFlag -func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag { - return &IntSliceFlag{IntSliceFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped IntSliceFlag.Apply -func (f *IntSliceFlag) Apply(set *flag.FlagSet) { - f.set = set - f.IntSliceFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped IntSliceFlag.ApplyWithError -func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.IntSliceFlag.ApplyWithError(set) -} - -// Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow -// for other values to be specified -type Int64SliceFlag struct { - cli.Int64SliceFlag - set *flag.FlagSet -} - -// NewInt64SliceFlag creates a new Int64SliceFlag -func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag { - return &Int64SliceFlag{Int64SliceFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped Int64SliceFlag.Apply -func (f *Int64SliceFlag) Apply(set *flag.FlagSet) { - f.set = set - f.Int64SliceFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped Int64SliceFlag.ApplyWithError -func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.Int64SliceFlag.ApplyWithError(set) -} - -// StringFlag is the flag type that wraps cli.StringFlag to allow -// for other values to be specified -type StringFlag struct { - cli.StringFlag - set *flag.FlagSet -} - -// NewStringFlag creates a new StringFlag -func NewStringFlag(fl cli.StringFlag) *StringFlag { - return &StringFlag{StringFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped StringFlag.Apply -func (f *StringFlag) Apply(set *flag.FlagSet) { - f.set = set - f.StringFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped StringFlag.ApplyWithError -func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.StringFlag.ApplyWithError(set) -} - -// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow -// for other values to be specified -type StringSliceFlag struct { - cli.StringSliceFlag - set *flag.FlagSet -} - -// NewStringSliceFlag creates a new StringSliceFlag -func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag { - return &StringSliceFlag{StringSliceFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped StringSliceFlag.Apply -func (f *StringSliceFlag) Apply(set *flag.FlagSet) { - f.set = set - f.StringSliceFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped StringSliceFlag.ApplyWithError -func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.StringSliceFlag.ApplyWithError(set) -} - -// Uint64Flag is the flag type that wraps cli.Uint64Flag to allow -// for other values to be specified -type Uint64Flag struct { - cli.Uint64Flag - set *flag.FlagSet -} - -// NewUint64Flag creates a new Uint64Flag -func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag { - return &Uint64Flag{Uint64Flag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped Uint64Flag.Apply -func (f *Uint64Flag) Apply(set *flag.FlagSet) { - f.set = set - f.Uint64Flag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped Uint64Flag.ApplyWithError -func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.Uint64Flag.ApplyWithError(set) -} - -// UintFlag is the flag type that wraps cli.UintFlag to allow -// for other values to be specified -type UintFlag struct { - cli.UintFlag - set *flag.FlagSet -} - -// NewUintFlag creates a new UintFlag -func NewUintFlag(fl cli.UintFlag) *UintFlag { - return &UintFlag{UintFlag: fl, set: nil} -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped UintFlag.Apply -func (f *UintFlag) Apply(set *flag.FlagSet) { - f.set = set - f.UintFlag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped UintFlag.ApplyWithError -func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.UintFlag.ApplyWithError(set) -} diff --git a/cli.go b/cli.go index e71e142..80d4357 100644 --- a/cli.go +++ b/cli.go @@ -19,4 +19,4 @@ // } package cli -//go:generate fg -p cli -i flag-types.json -o flag_generated.go +//go:generate flag-generator diff --git a/flag_generated.go b/flag_generated.go deleted file mode 100644 index f106d23..0000000 --- a/flag_generated.go +++ /dev/null @@ -1,718 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package cli - -import ( - "flag" - "strconv" - "time" -) - -// BoolFlag is a flag with type bool -type BoolFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f BoolFlag) IsRequired() bool { - return f.Required -} - -// Bool looks up the value of a local BoolFlag, returns -// false if not found -func (c *Context) Bool(name string) bool { - return lookupBool(name, c.flagSet) -} - -// GlobalBool looks up the value of a global BoolFlag, returns -// false if not found -func (c *Context) GlobalBool(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBool(name, fs) - } - return false -} - -func lookupBool(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// BoolTFlag is a flag with type bool that is true by default -type BoolTFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolTFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolTFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f BoolTFlag) IsRequired() bool { - return f.Required -} - -// BoolT looks up the value of a local BoolTFlag, returns -// false if not found -func (c *Context) BoolT(name string) bool { - return lookupBoolT(name, c.flagSet) -} - -// GlobalBoolT looks up the value of a global BoolTFlag, returns -// false if not found -func (c *Context) GlobalBoolT(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBoolT(name, fs) - } - return false -} - -func lookupBoolT(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) -type DurationFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value time.Duration - Destination *time.Duration -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f DurationFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f DurationFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f DurationFlag) IsRequired() bool { - return f.Required -} - -// Duration looks up the value of a local DurationFlag, returns -// 0 if not found -func (c *Context) Duration(name string) time.Duration { - return lookupDuration(name, c.flagSet) -} - -// GlobalDuration looks up the value of a global DurationFlag, returns -// 0 if not found -func (c *Context) GlobalDuration(name string) time.Duration { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupDuration(name, fs) - } - return 0 -} - -func lookupDuration(name string, set *flag.FlagSet) time.Duration { - f := set.Lookup(name) - if f != nil { - parsed, err := time.ParseDuration(f.Value.String()) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// Float64Flag is a flag with type float64 -type Float64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value float64 - Destination *float64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Float64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Float64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Float64Flag) IsRequired() bool { - return f.Required -} - -// Float64 looks up the value of a local Float64Flag, returns -// 0 if not found -func (c *Context) Float64(name string) float64 { - return lookupFloat64(name, c.flagSet) -} - -// GlobalFloat64 looks up the value of a global Float64Flag, returns -// 0 if not found -func (c *Context) GlobalFloat64(name string) float64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupFloat64(name, fs) - } - return 0 -} - -func lookupFloat64(name string, set *flag.FlagSet) float64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseFloat(f.Value.String(), 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// GenericFlag is a flag with type Generic -type GenericFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value Generic -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f GenericFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f GenericFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f GenericFlag) IsRequired() bool { - return f.Required -} - -// Generic looks up the value of a local GenericFlag, returns -// nil if not found -func (c *Context) Generic(name string) interface{} { - return lookupGeneric(name, c.flagSet) -} - -// GlobalGeneric looks up the value of a global GenericFlag, returns -// nil if not found -func (c *Context) GlobalGeneric(name string) interface{} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupGeneric(name, fs) - } - return nil -} - -func lookupGeneric(name string, set *flag.FlagSet) interface{} { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value, error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64Flag is a flag with type int64 -type Int64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int64 - Destination *int64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Int64Flag) IsRequired() bool { - return f.Required -} - -// Int64 looks up the value of a local Int64Flag, returns -// 0 if not found -func (c *Context) Int64(name string) int64 { - return lookupInt64(name, c.flagSet) -} - -// GlobalInt64 looks up the value of a global Int64Flag, returns -// 0 if not found -func (c *Context) GlobalInt64(name string) int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64(name, fs) - } - return 0 -} - -func lookupInt64(name string, set *flag.FlagSet) int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// IntFlag is a flag with type int -type IntFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int - Destination *int -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f IntFlag) IsRequired() bool { - return f.Required -} - -// Int looks up the value of a local IntFlag, returns -// 0 if not found -func (c *Context) Int(name string) int { - return lookupInt(name, c.flagSet) -} - -// GlobalInt looks up the value of a global IntFlag, returns -// 0 if not found -func (c *Context) GlobalInt(name string) int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt(name, fs) - } - return 0 -} - -func lookupInt(name string, set *flag.FlagSet) int { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return int(parsed) - } - return 0 -} - -// IntSliceFlag is a flag with type *IntSlice -type IntSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *IntSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntSliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f IntSliceFlag) IsRequired() bool { - return f.Required -} - -// IntSlice looks up the value of a local IntSliceFlag, returns -// nil if not found -func (c *Context) IntSlice(name string) []int { - return lookupIntSlice(name, c.flagSet) -} - -// GlobalIntSlice looks up the value of a global IntSliceFlag, returns -// nil if not found -func (c *Context) GlobalIntSlice(name string) []int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupIntSlice(name, fs) - } - return nil -} - -func lookupIntSlice(name string, set *flag.FlagSet) []int { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64SliceFlag is a flag with type *Int64Slice -type Int64SliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *Int64Slice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64SliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64SliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Int64SliceFlag) IsRequired() bool { - return f.Required -} - -// Int64Slice looks up the value of a local Int64SliceFlag, returns -// nil if not found -func (c *Context) Int64Slice(name string) []int64 { - return lookupInt64Slice(name, c.flagSet) -} - -// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns -// nil if not found -func (c *Context) GlobalInt64Slice(name string) []int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64Slice(name, fs) - } - return nil -} - -func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// StringFlag is a flag with type string -type StringFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value string - Destination *string -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f StringFlag) IsRequired() bool { - return f.Required -} - -// String looks up the value of a local StringFlag, returns -// "" if not found -func (c *Context) String(name string) string { - return lookupString(name, c.flagSet) -} - -// GlobalString looks up the value of a global StringFlag, returns -// "" if not found -func (c *Context) GlobalString(name string) string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupString(name, fs) - } - return "" -} - -func lookupString(name string, set *flag.FlagSet) string { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value.String(), error(nil) - if err != nil { - return "" - } - return parsed - } - return "" -} - -// StringSliceFlag is a flag with type *StringSlice -type StringSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *StringSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringSliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f StringSliceFlag) IsRequired() bool { - return f.Required -} - -// StringSlice looks up the value of a local StringSliceFlag, returns -// nil if not found -func (c *Context) StringSlice(name string) []string { - return lookupStringSlice(name, c.flagSet) -} - -// GlobalStringSlice looks up the value of a global StringSliceFlag, returns -// nil if not found -func (c *Context) GlobalStringSlice(name string) []string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupStringSlice(name, fs) - } - return nil -} - -func lookupStringSlice(name string, set *flag.FlagSet) []string { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Uint64Flag is a flag with type uint64 -type Uint64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint64 - Destination *uint64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Uint64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Uint64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Uint64Flag) IsRequired() bool { - return f.Required -} - -// Uint64 looks up the value of a local Uint64Flag, returns -// 0 if not found -func (c *Context) Uint64(name string) uint64 { - return lookupUint64(name, c.flagSet) -} - -// GlobalUint64 looks up the value of a global Uint64Flag, returns -// 0 if not found -func (c *Context) GlobalUint64(name string) uint64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint64(name, fs) - } - return 0 -} - -func lookupUint64(name string, set *flag.FlagSet) uint64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// UintFlag is a flag with type uint -type UintFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint - Destination *uint -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f UintFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f UintFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f UintFlag) IsRequired() bool { - return f.Required -} - -// Uint looks up the value of a local UintFlag, returns -// 0 if not found -func (c *Context) Uint(name string) uint { - return lookupUint(name, c.flagSet) -} - -// GlobalUint looks up the value of a global UintFlag, returns -// 0 if not found -func (c *Context) GlobalUint(name string) uint { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint(name, fs) - } - return 0 -} - -func lookupUint(name string, set *flag.FlagSet) uint { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return uint(parsed) - } - return 0 -} From 04948f21526ed8343ae9697600e284962d8972be Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 11:06:19 +0530 Subject: [PATCH 137/215] generate test with go generate --- altsrc/flag_generated.go | 346 +++++++++++++++++++ flag_generated.go | 718 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1064 insertions(+) create mode 100644 altsrc/flag_generated.go create mode 100644 flag_generated.go diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go new file mode 100644 index 0000000..0c45914 --- /dev/null +++ b/altsrc/flag_generated.go @@ -0,0 +1,346 @@ +// Code generated by fg; DO NOT EDIT. + +package altsrc + +import ( + "flag" + "github.com/urfave/cli" +) + +// BoolFlag is the flag type that wraps cli.BoolFlag to allow +// for other values to be specified +type BoolFlag struct { + cli.BoolFlag + set *flag.FlagSet +} + +// NewBoolFlag creates a new BoolFlag +func NewBoolFlag(fl cli.BoolFlag) *BoolFlag { + return &BoolFlag{BoolFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped BoolFlag.Apply +func (f *BoolFlag) Apply(set *flag.FlagSet) { + f.set = set + f.BoolFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped BoolFlag.ApplyWithError +func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.BoolFlag.ApplyWithError(set) +} + +// BoolTFlag is the flag type that wraps cli.BoolTFlag to allow +// for other values to be specified +type BoolTFlag struct { + cli.BoolTFlag + set *flag.FlagSet +} + +// NewBoolTFlag creates a new BoolTFlag +func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag { + return &BoolTFlag{BoolTFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped BoolTFlag.Apply +func (f *BoolTFlag) Apply(set *flag.FlagSet) { + f.set = set + f.BoolTFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped BoolTFlag.ApplyWithError +func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.BoolTFlag.ApplyWithError(set) +} + +// DurationFlag is the flag type that wraps cli.DurationFlag to allow +// for other values to be specified +type DurationFlag struct { + cli.DurationFlag + set *flag.FlagSet +} + +// NewDurationFlag creates a new DurationFlag +func NewDurationFlag(fl cli.DurationFlag) *DurationFlag { + return &DurationFlag{DurationFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped DurationFlag.Apply +func (f *DurationFlag) Apply(set *flag.FlagSet) { + f.set = set + f.DurationFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped DurationFlag.ApplyWithError +func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.DurationFlag.ApplyWithError(set) +} + +// Float64Flag is the flag type that wraps cli.Float64Flag to allow +// for other values to be specified +type Float64Flag struct { + cli.Float64Flag + set *flag.FlagSet +} + +// NewFloat64Flag creates a new Float64Flag +func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag { + return &Float64Flag{Float64Flag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Float64Flag.Apply +func (f *Float64Flag) Apply(set *flag.FlagSet) { + f.set = set + f.Float64Flag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Float64Flag.ApplyWithError +func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.Float64Flag.ApplyWithError(set) +} + +// GenericFlag is the flag type that wraps cli.GenericFlag to allow +// for other values to be specified +type GenericFlag struct { + cli.GenericFlag + set *flag.FlagSet +} + +// NewGenericFlag creates a new GenericFlag +func NewGenericFlag(fl cli.GenericFlag) *GenericFlag { + return &GenericFlag{GenericFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped GenericFlag.Apply +func (f *GenericFlag) Apply(set *flag.FlagSet) { + f.set = set + f.GenericFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped GenericFlag.ApplyWithError +func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.GenericFlag.ApplyWithError(set) +} + +// Int64Flag is the flag type that wraps cli.Int64Flag to allow +// for other values to be specified +type Int64Flag struct { + cli.Int64Flag + set *flag.FlagSet +} + +// NewInt64Flag creates a new Int64Flag +func NewInt64Flag(fl cli.Int64Flag) *Int64Flag { + return &Int64Flag{Int64Flag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Int64Flag.Apply +func (f *Int64Flag) Apply(set *flag.FlagSet) { + f.set = set + f.Int64Flag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Int64Flag.ApplyWithError +func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.Int64Flag.ApplyWithError(set) +} + +// IntFlag is the flag type that wraps cli.IntFlag to allow +// for other values to be specified +type IntFlag struct { + cli.IntFlag + set *flag.FlagSet +} + +// NewIntFlag creates a new IntFlag +func NewIntFlag(fl cli.IntFlag) *IntFlag { + return &IntFlag{IntFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped IntFlag.Apply +func (f *IntFlag) Apply(set *flag.FlagSet) { + f.set = set + f.IntFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped IntFlag.ApplyWithError +func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.IntFlag.ApplyWithError(set) +} + +// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow +// for other values to be specified +type IntSliceFlag struct { + cli.IntSliceFlag + set *flag.FlagSet +} + +// NewIntSliceFlag creates a new IntSliceFlag +func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag { + return &IntSliceFlag{IntSliceFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped IntSliceFlag.Apply +func (f *IntSliceFlag) Apply(set *flag.FlagSet) { + f.set = set + f.IntSliceFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped IntSliceFlag.ApplyWithError +func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.IntSliceFlag.ApplyWithError(set) +} + +// Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow +// for other values to be specified +type Int64SliceFlag struct { + cli.Int64SliceFlag + set *flag.FlagSet +} + +// NewInt64SliceFlag creates a new Int64SliceFlag +func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag { + return &Int64SliceFlag{Int64SliceFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Int64SliceFlag.Apply +func (f *Int64SliceFlag) Apply(set *flag.FlagSet) { + f.set = set + f.Int64SliceFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Int64SliceFlag.ApplyWithError +func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.Int64SliceFlag.ApplyWithError(set) +} + +// StringFlag is the flag type that wraps cli.StringFlag to allow +// for other values to be specified +type StringFlag struct { + cli.StringFlag + set *flag.FlagSet +} + +// NewStringFlag creates a new StringFlag +func NewStringFlag(fl cli.StringFlag) *StringFlag { + return &StringFlag{StringFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped StringFlag.Apply +func (f *StringFlag) Apply(set *flag.FlagSet) { + f.set = set + f.StringFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped StringFlag.ApplyWithError +func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.StringFlag.ApplyWithError(set) +} + +// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow +// for other values to be specified +type StringSliceFlag struct { + cli.StringSliceFlag + set *flag.FlagSet +} + +// NewStringSliceFlag creates a new StringSliceFlag +func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag { + return &StringSliceFlag{StringSliceFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped StringSliceFlag.Apply +func (f *StringSliceFlag) Apply(set *flag.FlagSet) { + f.set = set + f.StringSliceFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped StringSliceFlag.ApplyWithError +func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.StringSliceFlag.ApplyWithError(set) +} + +// Uint64Flag is the flag type that wraps cli.Uint64Flag to allow +// for other values to be specified +type Uint64Flag struct { + cli.Uint64Flag + set *flag.FlagSet +} + +// NewUint64Flag creates a new Uint64Flag +func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag { + return &Uint64Flag{Uint64Flag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped Uint64Flag.Apply +func (f *Uint64Flag) Apply(set *flag.FlagSet) { + f.set = set + f.Uint64Flag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped Uint64Flag.ApplyWithError +func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.Uint64Flag.ApplyWithError(set) +} + +// UintFlag is the flag type that wraps cli.UintFlag to allow +// for other values to be specified +type UintFlag struct { + cli.UintFlag + set *flag.FlagSet +} + +// NewUintFlag creates a new UintFlag +func NewUintFlag(fl cli.UintFlag) *UintFlag { + return &UintFlag{UintFlag: fl, set: nil} +} + +// Apply saves the flagSet for later usage calls, then calls +// the wrapped UintFlag.Apply +func (f *UintFlag) Apply(set *flag.FlagSet) { + f.set = set + f.UintFlag.Apply(set) +} + +// ApplyWithError saves the flagSet for later usage calls, then calls +// the wrapped UintFlag.ApplyWithError +func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error { + f.set = set + return f.UintFlag.ApplyWithError(set) +} diff --git a/flag_generated.go b/flag_generated.go new file mode 100644 index 0000000..f106d23 --- /dev/null +++ b/flag_generated.go @@ -0,0 +1,718 @@ +// Code generated by fg; DO NOT EDIT. + +package cli + +import ( + "flag" + "strconv" + "time" +) + +// BoolFlag is a flag with type bool +type BoolFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f BoolFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f BoolFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f BoolFlag) IsRequired() bool { + return f.Required +} + +// Bool looks up the value of a local BoolFlag, returns +// false if not found +func (c *Context) Bool(name string) bool { + return lookupBool(name, c.flagSet) +} + +// GlobalBool looks up the value of a global BoolFlag, returns +// false if not found +func (c *Context) GlobalBool(name string) bool { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupBool(name, fs) + } + return false +} + +func lookupBool(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return parsed + } + return false +} + +// BoolTFlag is a flag with type bool that is true by default +type BoolTFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f BoolTFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f BoolTFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f BoolTFlag) IsRequired() bool { + return f.Required +} + +// BoolT looks up the value of a local BoolTFlag, returns +// false if not found +func (c *Context) BoolT(name string) bool { + return lookupBoolT(name, c.flagSet) +} + +// GlobalBoolT looks up the value of a global BoolTFlag, returns +// false if not found +func (c *Context) GlobalBoolT(name string) bool { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupBoolT(name, fs) + } + return false +} + +func lookupBoolT(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return parsed + } + return false +} + +// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) +type DurationFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value time.Duration + Destination *time.Duration +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f DurationFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f DurationFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f DurationFlag) IsRequired() bool { + return f.Required +} + +// Duration looks up the value of a local DurationFlag, returns +// 0 if not found +func (c *Context) Duration(name string) time.Duration { + return lookupDuration(name, c.flagSet) +} + +// GlobalDuration looks up the value of a global DurationFlag, returns +// 0 if not found +func (c *Context) GlobalDuration(name string) time.Duration { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupDuration(name, fs) + } + return 0 +} + +func lookupDuration(name string, set *flag.FlagSet) time.Duration { + f := set.Lookup(name) + if f != nil { + parsed, err := time.ParseDuration(f.Value.String()) + if err != nil { + return 0 + } + return parsed + } + return 0 +} + +// Float64Flag is a flag with type float64 +type Float64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value float64 + Destination *float64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Float64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Float64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Float64Flag) IsRequired() bool { + return f.Required +} + +// Float64 looks up the value of a local Float64Flag, returns +// 0 if not found +func (c *Context) Float64(name string) float64 { + return lookupFloat64(name, c.flagSet) +} + +// GlobalFloat64 looks up the value of a global Float64Flag, returns +// 0 if not found +func (c *Context) GlobalFloat64(name string) float64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupFloat64(name, fs) + } + return 0 +} + +func lookupFloat64(name string, set *flag.FlagSet) float64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseFloat(f.Value.String(), 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} + +// GenericFlag is a flag with type Generic +type GenericFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value Generic +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f GenericFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f GenericFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f GenericFlag) IsRequired() bool { + return f.Required +} + +// Generic looks up the value of a local GenericFlag, returns +// nil if not found +func (c *Context) Generic(name string) interface{} { + return lookupGeneric(name, c.flagSet) +} + +// GlobalGeneric looks up the value of a global GenericFlag, returns +// nil if not found +func (c *Context) GlobalGeneric(name string) interface{} { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupGeneric(name, fs) + } + return nil +} + +func lookupGeneric(name string, set *flag.FlagSet) interface{} { + f := set.Lookup(name) + if f != nil { + parsed, err := f.Value, error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} + +// Int64Flag is a flag with type int64 +type Int64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int64 + Destination *int64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Int64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Int64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Int64Flag) IsRequired() bool { + return f.Required +} + +// Int64 looks up the value of a local Int64Flag, returns +// 0 if not found +func (c *Context) Int64(name string) int64 { + return lookupInt64(name, c.flagSet) +} + +// GlobalInt64 looks up the value of a global Int64Flag, returns +// 0 if not found +func (c *Context) GlobalInt64(name string) int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64(name, fs) + } + return 0 +} + +func lookupInt64(name string, set *flag.FlagSet) int64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} + +// IntFlag is a flag with type int +type IntFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int + Destination *int +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f IntFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f IntFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f IntFlag) IsRequired() bool { + return f.Required +} + +// Int looks up the value of a local IntFlag, returns +// 0 if not found +func (c *Context) Int(name string) int { + return lookupInt(name, c.flagSet) +} + +// GlobalInt looks up the value of a global IntFlag, returns +// 0 if not found +func (c *Context) GlobalInt(name string) int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt(name, fs) + } + return 0 +} + +func lookupInt(name string, set *flag.FlagSet) int { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return int(parsed) + } + return 0 +} + +// IntSliceFlag is a flag with type *IntSlice +type IntSliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *IntSlice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f IntSliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f IntSliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f IntSliceFlag) IsRequired() bool { + return f.Required +} + +// IntSlice looks up the value of a local IntSliceFlag, returns +// nil if not found +func (c *Context) IntSlice(name string) []int { + return lookupIntSlice(name, c.flagSet) +} + +// GlobalIntSlice looks up the value of a global IntSliceFlag, returns +// nil if not found +func (c *Context) GlobalIntSlice(name string) []int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupIntSlice(name, fs) + } + return nil +} + +func lookupIntSlice(name string, set *flag.FlagSet) []int { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} + +// Int64SliceFlag is a flag with type *Int64Slice +type Int64SliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *Int64Slice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Int64SliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Int64SliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Int64SliceFlag) IsRequired() bool { + return f.Required +} + +// Int64Slice looks up the value of a local Int64SliceFlag, returns +// nil if not found +func (c *Context) Int64Slice(name string) []int64 { + return lookupInt64Slice(name, c.flagSet) +} + +// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns +// nil if not found +func (c *Context) GlobalInt64Slice(name string) []int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64Slice(name, fs) + } + return nil +} + +func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} + +// StringFlag is a flag with type string +type StringFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value string + Destination *string +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f StringFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f StringFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f StringFlag) IsRequired() bool { + return f.Required +} + +// String looks up the value of a local StringFlag, returns +// "" if not found +func (c *Context) String(name string) string { + return lookupString(name, c.flagSet) +} + +// GlobalString looks up the value of a global StringFlag, returns +// "" if not found +func (c *Context) GlobalString(name string) string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupString(name, fs) + } + return "" +} + +func lookupString(name string, set *flag.FlagSet) string { + f := set.Lookup(name) + if f != nil { + parsed, err := f.Value.String(), error(nil) + if err != nil { + return "" + } + return parsed + } + return "" +} + +// StringSliceFlag is a flag with type *StringSlice +type StringSliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *StringSlice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f StringSliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f StringSliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f StringSliceFlag) IsRequired() bool { + return f.Required +} + +// StringSlice looks up the value of a local StringSliceFlag, returns +// nil if not found +func (c *Context) StringSlice(name string) []string { + return lookupStringSlice(name, c.flagSet) +} + +// GlobalStringSlice looks up the value of a global StringSliceFlag, returns +// nil if not found +func (c *Context) GlobalStringSlice(name string) []string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupStringSlice(name, fs) + } + return nil +} + +func lookupStringSlice(name string, set *flag.FlagSet) []string { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} + +// Uint64Flag is a flag with type uint64 +type Uint64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint64 + Destination *uint64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Uint64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Uint64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Uint64Flag) IsRequired() bool { + return f.Required +} + +// Uint64 looks up the value of a local Uint64Flag, returns +// 0 if not found +func (c *Context) Uint64(name string) uint64 { + return lookupUint64(name, c.flagSet) +} + +// GlobalUint64 looks up the value of a global Uint64Flag, returns +// 0 if not found +func (c *Context) GlobalUint64(name string) uint64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint64(name, fs) + } + return 0 +} + +func lookupUint64(name string, set *flag.FlagSet) uint64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} + +// UintFlag is a flag with type uint +type UintFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint + Destination *uint +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f UintFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f UintFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f UintFlag) IsRequired() bool { + return f.Required +} + +// Uint looks up the value of a local UintFlag, returns +// 0 if not found +func (c *Context) Uint(name string) uint { + return lookupUint(name, c.flagSet) +} + +// GlobalUint looks up the value of a global UintFlag, returns +// 0 if not found +func (c *Context) GlobalUint(name string) uint { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint(name, fs) + } + return 0 +} + +func lookupUint(name string, set *flag.FlagSet) uint { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return uint(parsed) + } + return 0 +} From 826954c97919610e0f4086ab3bcc007ac3326184 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 11:09:34 +0530 Subject: [PATCH 138/215] update app name and remove version --- flag-gen/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/flag-gen/main.go b/flag-gen/main.go index 3e463ff..099a5ff 100644 --- a/flag-gen/main.go +++ b/flag-gen/main.go @@ -32,9 +32,8 @@ type FlagType struct { func main() { app := cli.NewApp() - app.Name = "fg" + app.Name = "flag-generator" app.Usage = "Generate flag type code!" - app.Version = "v0.1.0" app.Action = ActionFunc From b6bfbe97f8430a4dfe05791363719d61cb921793 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 12:16:07 +0530 Subject: [PATCH 139/215] update appveyor to go 1.11, add support for code coverage generation in tests --- .travis.yml | 28 ++++++++++++++++------------ appveyor.yml | 18 +++++++++--------- coverage.sh | 8 ++++++++ runtests | 4 ---- 4 files changed, 33 insertions(+), 25 deletions(-) create mode 100755 coverage.sh diff --git a/.travis.yml b/.travis.yml index 8bb0e9c..75d9ea3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,23 +5,27 @@ osx_image: xcode8.3 go: 1.11.x os: -- linux -- osx + - linux + - osx cache: directories: - - node_modules + - node_modules before_script: -- go get github.com/urfave/gfmrun/... || true -- go get golang.org/x/tools/cmd/goimports -- if [ ! -f node_modules/.bin/markdown-toc ] ; then + - go get github.com/urfave/gfmrun/... || true + - go get golang.org/x/tools/cmd/goimports + - if [ ! -f node_modules/.bin/markdown-toc ] ; then npm install markdown-toc ; - fi + fi script: -- ./runtests gen -- ./runtests vet -- ./runtests test -- ./runtests gfmrun -- ./runtests toc + - ./runtests gen + - ./runtests vet + - ./runtests test + - ./runtests gfmrun + - ./runtests toc + +after_success: + - ./coverage.sh + - bash <(curl -s https://codecov.io/bash) diff --git a/appveyor.yml b/appveyor.yml index 1e1489c..f841e61 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,19 +8,19 @@ clone_folder: c:\gopath\src\github.com\urfave\cli environment: GOPATH: C:\gopath - GOVERSION: 1.8.x + GOVERSION: 1.11.x PYTHON: C:\Python36-x64 PYTHON_VERSION: 3.6.x PYTHON_ARCH: 64 install: -- set PATH=%GOPATH%\bin;C:\go\bin;%PATH% -- go version -- go env -- go get github.com/urfave/gfmrun/... -- go get -v -t ./... + - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% + - go version + - go env + - go get github.com/urfave/gfmrun/... + - go get -v -t ./... build_script: -- python runtests vet -- python runtests test -- python runtests gfmrun + - python runtests vet + - python runtests test + - python runtests gfmrun diff --git a/coverage.sh b/coverage.sh new file mode 100755 index 0000000..03c720c --- /dev/null +++ b/coverage.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e +echo "" > coverage.txt + +for f in $(ls *.coverprofile); do + cat $f >> coverage.txt + rm $f +done \ No newline at end of file diff --git a/runtests b/runtests index ee22bde..80d225a 100755 --- a/runtests +++ b/runtests @@ -34,10 +34,6 @@ def main(sysargs=sys.argv[:]): def _test(): - if check_output('go version'.split()).split()[2] < 'go1.2': - _run('go test -v .') - return - coverprofiles = [] for subpackage in ['', 'altsrc']: coverprofile = 'cli.coverprofile' From 365557021fe254d059d0f7065bb0c94686208723 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 12:17:27 +0530 Subject: [PATCH 140/215] remove legacy version check code --- runtests | 9 --------- 1 file changed, 9 deletions(-) diff --git a/runtests b/runtests index 80d225a..b073dc2 100755 --- a/runtests +++ b/runtests @@ -53,10 +53,6 @@ def _test(): 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']) @@ -70,11 +66,6 @@ def _toc(): def _gen(): - go_version = check_output('go version'.split()).split()[2] - if go_version < 'go1.5': - print('runtests: skip on {}'.format(go_version), file=sys.stderr) - return - _run('go generate ./...') _run('git diff --exit-code') From fb4cea5f30995b6caaa700ea789902af499d63b2 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 12:22:45 +0530 Subject: [PATCH 141/215] add new generation logic to travis --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 75d9ea3..ad53098 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ cache: - node_modules before_script: + - go get github.com/urfave/cli/flag-gen || true - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - if [ ! -f node_modules/.bin/markdown-toc ] ; then @@ -20,7 +21,7 @@ before_script: fi script: - - ./runtests gen + - go generate ./... - ./runtests vet - ./runtests test - ./runtests gfmrun From 4b0a4104630bddd01a7a6c5aa8a70dc94642ab52 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 12:26:48 +0530 Subject: [PATCH 142/215] fix travis build --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ad53098..33b8a1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ cache: before_script: - go get github.com/urfave/cli/flag-gen || true + - go get github.com/shurcooL/vfsgen || true + - go get github.com/shurcooL/httpfs/union || true - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - if [ ! -f node_modules/.bin/markdown-toc ] ; then @@ -21,7 +23,7 @@ before_script: fi script: - - go generate ./... + #- go generate ./... # to be uncommented after merge as we cannot compile the package which is not present in master - ./runtests vet - ./runtests test - ./runtests gfmrun From 8547458f1d93654348b3bbeccb6d04424d5eab3e Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 19:01:47 +0530 Subject: [PATCH 143/215] remove zero mod fs --- flag-gen/assets_generate.go | 42 ++++++------------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/flag-gen/assets_generate.go b/flag-gen/assets_generate.go index b587253..c3847d6 100644 --- a/flag-gen/assets_generate.go +++ b/flag-gen/assets_generate.go @@ -7,45 +7,15 @@ import ( "github.com/shurcooL/vfsgen" "log" "net/http" - "os" - "time" ) -// zeroModTimeFileSystem is an http.FileSystem wrapper. -// It exposes a filesystem exactly like Source, except -// all file modification times are changed to zero. -// See https://github.com/shurcooL/vfsgen/pull/40#issuecomment-355416103 -type zeroModTimeFileSystem struct { - Source http.FileSystem -} - -func (fs zeroModTimeFileSystem) Open(name string) (http.File, error) { - f, err := fs.Source.Open(name) - return file{f}, err -} - -type file struct { - http.File -} - -func (f file) Stat() (os.FileInfo, error) { - fi, err := f.File.Stat() - return fileInfo{fi}, err -} - -type fileInfo struct { - os.FileInfo -} - -func (fi fileInfo) ModTime() time.Time { return time.Time{} } - func main() { - err := vfsgen.Generate(zeroModTimeFileSystem{ - Source: union.New(map[string]http.FileSystem{ - "/templates": http.Dir("templates"), - "/source": http.Dir("source"), - }), - }, vfsgen.Options{}) + fs := union.New(map[string]http.FileSystem{ + "/templates": http.Dir("templates"), + "/source": http.Dir("source"), + }) + + err := vfsgen.Generate(fs, vfsgen.Options{}) if err != nil { log.Fatal(err) From 58ae5eb590667b0115d3f82e03593d87e2924b1c Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 20:06:44 +0530 Subject: [PATCH 144/215] move entire build system to go --- .travis.yml | 12 ++- appveyor.yml | 12 +-- build/build.go | 179 +++++++++++++++++++++++++++++++++++++ cli.go | 2 +- coverage.sh | 8 -- flag-gen/assets_vfsdata.go | 14 +-- flag-gen/main.go | 4 +- go.mod | 2 + go.sum | 4 + 9 files changed, 206 insertions(+), 31 deletions(-) create mode 100644 build/build.go delete mode 100755 coverage.sh diff --git a/.travis.yml b/.travis.yml index 33b8a1f..20f3a58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ cache: - node_modules before_script: - - go get github.com/urfave/cli/flag-gen || true - go get github.com/shurcooL/vfsgen || true - go get github.com/shurcooL/httpfs/union || true - go get github.com/urfave/gfmrun/... || true @@ -23,12 +22,11 @@ before_script: fi script: - #- go generate ./... # to be uncommented after merge as we cannot compile the package which is not present in master - - ./runtests vet - - ./runtests test - - ./runtests gfmrun - - ./runtests toc + - go run build/build.go generate + - go run build/build.go vet + - go run build/build.go test + - go run build/build.go gfmrun + - go run build/build.go toc after_success: - - ./coverage.sh - bash <(curl -s https://codecov.io/bash) diff --git a/appveyor.yml b/appveyor.yml index f841e61..39c6aac 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,18 +9,18 @@ clone_folder: c:\gopath\src\github.com\urfave\cli environment: GOPATH: C:\gopath GOVERSION: 1.11.x - PYTHON: C:\Python36-x64 - PYTHON_VERSION: 3.6.x - PYTHON_ARCH: 64 install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env + - go get github.com/shurcooL/vfsgen + - go get github.com/shurcooL/httpfs/union - go get github.com/urfave/gfmrun/... - go get -v -t ./... build_script: - - python runtests vet - - python runtests test - - python runtests gfmrun + - go run build/build.go generate + - go rub build/build.go vet + - go rub build/build.go test + - go rub build/build.go gfmrun diff --git a/build/build.go b/build/build.go new file mode 100644 index 0000000..b83a8e0 --- /dev/null +++ b/build/build.go @@ -0,0 +1,179 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "github.com/urfave/cli" + "io/ioutil" + "log" + "os" + "os/exec" + "strings" +) + +var packages []string + +func main() { + app := cli.NewApp() + + app.Name = "builder" + app.Usage = "Generates a new urfave/cli build!" + + app.Commands = cli.Commands{ + cli.Command{ + Name: "vet", + Action: VetActionFunc, + }, + cli.Command{ + Name: "test", + Action: TestActionFunc, + }, + cli.Command{ + Name: "gfmrun", + Action: GfmrunActionFunc, + }, + cli.Command{ + Name: "toc", + Action: TocActionFunc, + }, + cli.Command{ + Name: "generate", + Action: GenActionFunc, + }, + } + + packages = []string{"cli", "altsrc"} + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} + +func VetActionFunc(_ *cli.Context) error { + return exec.Command("go", "vet").Run() +} + +func TestActionFunc(c *cli.Context) error { + for _, pkg := range packages { + var packageName string + + if pkg == "cli" { + packageName = fmt.Sprintf("github.com/urfave/cli") + } else { + packageName = fmt.Sprintf("github.com/urfave/cli/%s", pkg) + } + + coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg) + + err := exec.Command( + "go", "test", "-v", coverProfile, packageName, + ).Run() + + if err != nil { + return err + } + } + + return testCleanup() +} + +func testCleanup() error { + var out bytes.Buffer + + for _, pkg := range packages { + file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg)) + if err != nil { + return err + } + + b, err := ioutil.ReadAll(file) + if err != nil { + return err + } + + out.Write(b) + err = file.Close() + if err != nil { + return err + } + + err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg)) + if err != nil { + return err + } + } + + outFile, err := os.Create("coverage.txt") + if err != nil { + return err + } + + _, err = out.WriteTo(outFile) + if err != nil { + return err + } + + err = outFile.Close() + if err != nil { + return err + } + + return nil +} + +func GfmrunActionFunc(_ *cli.Context) error { + file, err := os.Open("README.md") + if err != nil { + return err + } + + var counter int + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if strings.Contains(scanner.Text(), "package main") { + counter++ + } + } + + err = scanner.Err() + if err != nil { + return err + } + + return exec.Command("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md").Run() +} + +func TocActionFunc(_ *cli.Context) error { + err := exec.Command("node_modules/.bin/markdown-toc", "-i", "README.md").Run() + if err != nil { + return err + } + + err = exec.Command("git", "diff", "--exit-code").Run() + if err != nil { + return err + } + + return nil +} + +func GenActionFunc(_ *cli.Context) error { + err := exec.Command("go", "generate", "flag-gen/main.go").Run() + if err != nil { + return err + } + + err = exec.Command("go", "generate", "cli.go").Run() + if err != nil { + return err + } + + err = exec.Command("git", "diff", "--exit-code").Run() + if err != nil { + return err + } + + return nil +} diff --git a/cli.go b/cli.go index 80d4357..4bd2508 100644 --- a/cli.go +++ b/cli.go @@ -19,4 +19,4 @@ // } package cli -//go:generate flag-generator +//go:generate go run flag-gen/main.go flag-gen/assets_vfsdata.go diff --git a/coverage.sh b/coverage.sh deleted file mode 100755 index 03c720c..0000000 --- a/coverage.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -e -echo "" > coverage.txt - -for f in $(ls *.coverprofile); do - cat $f >> coverage.txt - rm $f -done \ No newline at end of file diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index 4b89ce3..c98c71d 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -23,29 +23,29 @@ var assets = func() http.FileSystem { }, "/source": &vfsgenÛ°DirInfo{ name: "source", - modTime: time.Time{}, + modTime: time.Date(2019, 8, 4, 4, 7, 51, 386498815, time.UTC), }, "/source/flag-types.json": &vfsgenÛ°CompressedFileInfo{ name: "flag-types.json", - modTime: time.Time{}, + modTime: time.Date(2019, 8, 4, 2, 47, 4, 104288603, time.UTC), uncompressedSize: 2559, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x94\xc1\x6e\x9c\x30\x10\x86\xef\x3c\xc5\xc8\xbd\x40\xb5\x82\x1c\x56\x7b\xd8\x63\x55\xb5\xda\x5b\xa5\x34\xbd\x24\x51\xe4\x80\x21\x56\x1d\x1b\xd9\x43\xd4\x28\xca\xbb\x57\xf6\x2e\xbb\x60\x0c\x62\x5b\x2d\xb9\xfe\x23\x98\xef\x9b\xb1\xe6\x36\x02\x78\x8b\x00\x00\x88\xa4\xcf\x8c\x6c\x81\x7c\x51\x4a\x90\xd5\x3e\xc3\xd7\xda\x65\x8f\x9d\xec\x85\x8a\xc6\x86\x25\x15\x86\x1d\xb2\x82\x19\x24\x5b\x40\xdd\xb4\x49\xae\x24\xb2\x3f\xf8\x50\xb0\x92\x36\xc2\x16\x89\xfb\xa0\xfd\x4b\x4d\xb5\x61\xda\xc6\x06\x75\xae\xe4\x4b\xfa\xc3\x26\xb6\x79\x5c\xa6\xbf\x6c\x8f\xf4\x1a\x35\x97\x55\x9c\x24\x24\x02\x78\x5f\x85\x51\x7f\xfe\x3f\x6b\xa1\x72\xa4\x5c\xd8\x8f\x01\x9f\x28\x02\x37\xae\x0c\x8f\xaf\xd0\xf2\x2f\xa9\xf5\xb5\xd1\x14\xb9\x92\xbe\x19\xf2\x67\x96\xfa\xc5\x56\xb1\xeb\x33\x69\x18\x1b\xc6\xe0\x09\xb1\x36\xdb\x2c\xab\x94\xa0\xb2\x4a\x95\xae\xb2\xfa\x77\x95\xd9\x0e\xd9\x27\x87\xdc\xf6\x49\x26\xd4\xaf\x86\xda\x8e\xb1\xf7\x83\xf9\xde\xdf\x84\xa2\xb8\x59\xfb\xda\x65\x3f\x9e\x25\x3c\x0f\xb6\xb7\x23\xd7\x7d\x00\xbb\x82\xcd\x7a\x14\xf8\x3b\x93\x4c\xf3\xdc\x07\xf6\xe2\x71\xe0\xee\xb3\x0c\x10\x4b\x2e\xfc\xe1\xb7\x2d\xb8\x44\xa6\x4b\x9a\xb3\xb7\xf7\x31\xb8\x9d\x0c\xcc\x92\xcb\x05\x26\xb9\x93\xa1\x39\x5e\x4d\x8e\x72\x27\x31\xc0\xfa\xa1\xa4\xbd\x4f\x1f\x72\xea\xfa\x59\xaa\xd8\x45\xc5\x94\xcb\xb5\xe0\x39\xf3\x85\x3e\xfb\x85\x4b\xbc\x8c\xdb\xfb\xce\xdc\x4e\xda\x47\xcd\xf8\x48\x91\x24\xfb\xc8\x2a\x33\xad\x95\x8e\x25\x17\x53\x52\x9b\xf5\xa8\x96\x57\xba\x98\xd8\xe9\xf1\x8e\xa9\x1d\x48\xce\x93\xdb\xef\xde\x17\x33\xbd\xf4\x5f\xdf\xe0\x1d\xb9\x23\x43\xe8\xe1\xab\x9b\x4b\x19\xde\x41\xa0\x76\x99\x25\xf4\x87\x12\xdc\x42\x87\xe5\xbc\x35\xdc\xf0\xd0\xcd\x6a\x16\x39\x5a\xb6\xf7\xd9\x57\xeb\x86\x0f\xcf\x56\xb3\xc0\xdd\x9a\x86\x0d\x1f\xae\xc6\xbf\x5c\xd1\x7d\xf4\x37\x00\x00\xff\xff\x66\x52\x85\x44\xff\x09\x00\x00"), }, "/templates": &vfsgenÛ°DirInfo{ name: "templates", - modTime: time.Time{}, + modTime: time.Date(2019, 8, 4, 5, 32, 59, 292856915, time.UTC), }, "/templates/altsrc_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "altsrc_flags_generated.gotpl", - modTime: time.Time{}, - uncompressedSize: 1063, + modTime: time.Date(2019, 8, 4, 5, 32, 59, 176187795, time.UTC), + uncompressedSize: 1062, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x41\x8b\xdb\x30\x10\x85\xcf\xab\x5f\xf1\x08\x4b\x89\x97\x60\xdf\x53\xf6\x50\xba\x5b\xe8\x25\x5b\xd8\x85\x9e\x15\x7b\x64\x8b\x2a\xb6\x91\xc6\x09\x41\xf8\xbf\x97\x91\x97\x34\xa5\x4a\xe9\xa5\x37\x59\xa3\xf7\xde\x7c\x33\xae\x2a\x7c\x1e\x1a\x42\x4b\x3d\x79\xcd\xd4\x60\x7f\x86\x69\x3f\xe2\xe9\x05\xbb\x97\x37\x3c\x3f\x7d\x7d\x2b\x95\x1a\x75\xfd\x43\xb7\x84\x18\x51\x7e\x5b\xce\x3b\x7d\x20\xcc\xb3\x52\xf6\x30\x0e\x9e\xb1\x56\x77\x2b\xe3\x74\xbb\x52\x00\xb0\x6a\x2d\x77\xd3\xbe\xac\x87\x43\x35\x79\xa3\x8f\x54\xd5\xce\xae\x54\xa1\x54\x8c\xf0\xba\x6f\x09\xf7\x76\x83\x7b\x91\x60\xfb\x88\xf2\x8b\xd3\x6d\x10\xc3\xaa\x92\x98\x54\x28\xdf\x43\xa4\x06\x1b\xc0\x1d\x21\x09\xf8\x3c\x12\xb8\xd3\x8c\x93\xd7\x63\x40\xed\x6c\x99\x15\xf1\x00\xed\xdc\x70\x12\x57\x33\x78\x0c\xdc\x91\xc7\x51\xbb\x89\x82\x14\xf7\x84\x30\x52\x6d\x8d\xa5\x46\x25\xd7\xac\x4d\x60\x3f\xd5\x8c\x98\xd8\x6e\x85\xa5\x62\x20\xc6\x43\xaa\xc8\xd5\x2b\xb1\x9a\x95\x84\xef\xe8\x94\x75\xae\x3d\x69\xa6\x00\x8d\x9e\x4e\xd9\x70\x65\xa6\xbe\xbe\xa5\x5f\x1b\x77\xb3\x9f\x02\x0f\xd9\xc8\x85\xc2\x13\x4f\xbe\xc7\x87\xdc\x93\x98\x6d\x64\x0b\xe3\x36\x02\xb8\x45\x6f\x1d\xe6\x77\xb2\x4f\xe3\xe8\xce\x08\xfa\x48\xbf\x16\xf4\x4a\x9c\xc6\xed\x34\x93\xc7\x14\xe4\xdf\xa9\xb5\x73\x61\x23\x4f\xfa\xe5\x2c\x62\x11\xc8\x0a\x47\x6a\xb2\x99\x65\x72\x5f\x46\xb0\x36\x79\xa0\x62\x69\x61\xfd\xc7\xe8\x0b\x44\x75\x67\x4a\xb9\x7f\x94\xbe\xe5\xe3\x76\x88\xe8\x8b\x6b\xa6\xef\x96\xbb\x67\xef\x07\xff\x7f\xe1\x2e\x31\xff\x44\x79\x79\x9d\xc3\xa5\xd4\xed\xb2\xdf\x6b\xee\xab\x7d\xff\x6d\x02\xbf\x79\xcb\x28\x62\x04\xf5\x0d\xe6\xf9\x67\x00\x00\x00\xff\xff\x1d\x0d\x69\x06\x27\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x41\x8b\xdb\x30\x10\x85\xcf\xab\x5f\xf1\x08\x4b\x89\x97\x60\xdf\x53\xf6\x50\xba\x5b\xe8\x25\x5b\xd8\x85\x9e\x15\x7b\x64\x8b\x2a\xb6\x91\xc6\x09\x41\xf8\xbf\x97\x91\x97\x34\xa5\x4a\xe9\xa5\x37\x59\xa3\xf7\xde\x7c\x33\xae\x2a\x7c\x1e\x1a\x42\x4b\x3d\x79\xcd\xd4\x60\x7f\x86\x69\x3f\xe2\xe9\x05\xbb\x97\x37\x3c\x3f\x7d\x7d\x2b\x95\x1a\x75\xfd\x43\xb7\x84\x18\x51\x7e\x5b\xce\x3b\x7d\x20\xcc\xb3\x52\xf6\x30\x0e\x9e\xb1\x56\x77\x2b\xe3\x74\xbb\x52\x00\xb0\x6a\x2d\x77\xd3\xbe\xac\x87\x43\x35\x79\xa3\x8f\x54\xd5\xce\xae\x54\xa1\x62\x84\xd7\x7d\x4b\xb8\xb7\x1b\xdc\x8b\x02\xdb\x47\x94\x5f\x9c\x6e\x83\xf8\x55\x95\xa4\xa4\x42\xf9\x9e\x21\x35\xd8\x00\xee\x08\x49\xc0\xe7\x91\xc0\x9d\x66\x9c\xbc\x1e\x03\x6a\x67\xcb\xac\x88\x07\x68\xe7\x86\x93\xb8\x9a\xc1\x63\xe0\x8e\x3c\x8e\xda\x4d\x14\xa4\xb8\x27\x84\x91\x6a\x6b\x2c\x35\x2a\xb9\x66\x6d\x02\xfb\xa9\x66\xc4\x84\x76\x2b\x2c\x15\x03\x31\x1e\x52\x45\xae\x5e\x89\xd5\xac\x24\x7c\x47\xa7\xac\x73\xed\x49\x33\x05\x68\xf4\x74\xca\x86\x2b\x33\xf5\xf5\x2d\xfd\xda\xb8\x9b\xfd\x14\x78\xc8\x46\x2e\x14\x9e\x78\xf2\x3d\x3e\xe4\x9e\xc4\x6c\x23\x5b\x18\xb7\x11\xc0\x2d\x7a\xeb\x30\xbf\x93\x7d\x1a\x47\x77\x46\xd0\x47\xfa\xb5\xa0\x57\xe2\x34\x6e\xa7\x99\x3c\xa6\x20\xbf\x4e\xad\x9d\x0b\x1b\x79\xd2\x2f\x67\x11\x8b\x40\x56\x38\x52\x93\xcd\x2c\x93\xfb\x32\x82\xb5\xc9\x03\x15\x4b\x0b\xeb\x3f\x46\x5f\x20\xaa\x3b\x53\xca\xfd\xa3\xf4\x2d\x1f\xb7\x43\x44\x5f\x5c\x33\x7d\xb7\xdc\x3d\x7b\x3f\xf8\xff\x0b\x77\x89\xf9\x27\xca\xcb\xeb\x1c\x2e\xa5\x6e\x97\xfd\x5e\x73\x5f\xed\xfb\x6f\x13\xf8\xcd\x5b\x46\x11\x23\xa8\x6f\x30\xcf\x3f\x03\x00\x00\xff\xff\xb0\x66\x7b\xfe\x26\x04\x00\x00"), }, "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", - modTime: time.Time{}, + modTime: time.Date(2019, 8, 4, 5, 32, 38, 55742173, time.UTC), uncompressedSize: 2230, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x41\x4f\xeb\x38\x10\x3e\x37\xbf\x62\xb6\xe2\x90\xa0\x6e\x7b\x5f\xc4\x89\xc2\x2e\xd2\x0a\xd0\x83\xc7\xdd\x4d\xc6\xa9\x85\xb1\x8b\xed\xc0\x43\x51\xff\xfb\xd3\x8c\x9d\xb4\x4d\x0b\xf4\xc2\xeb\xc9\x9e\xf9\xfc\xcd\x37\xdf\x8c\x9a\xd9\x0c\x2e\x6c\x85\x50\xa3\x41\x27\x02\x56\xb0\x78\x07\x59\x9f\xc1\xfc\x16\x6e\x6e\x1f\xe0\x72\x7e\xfd\x30\xcd\xb2\x95\x28\x9f\x44\x8d\xd0\xb6\x30\xbd\x8b\xe7\x1b\xf1\x8c\xb0\x5e\x67\x99\x7a\x5e\x59\x17\x20\xcf\x46\x63\xa9\x45\x3d\xce\x46\x63\x1f\x5c\x69\xcd\x2b\x1d\x83\x7a\xc6\x71\x56\x64\x6d\x0b\x4e\x98\x1a\xe1\x44\x4d\xe0\x84\x80\xf0\xcf\x39\x4c\xaf\xb4\xa8\x3d\xd1\xcc\x66\x44\xce\x89\x69\xa2\xa6\x1c\x28\x0f\x02\x18\xfe\xa6\xc2\x12\xc2\xfb\x0a\x37\xc0\x07\xba\xad\xd7\xfd\x7d\x6e\xcb\x20\x94\x26\xbe\x5d\xe0\x36\xa3\x0f\xae\x29\x03\xb4\x19\x00\x00\x27\xd2\xcf\x07\xa7\x4c\xcd\xe1\x9f\x9e\xba\xdd\x0b\x5f\x9a\xd7\x47\xe1\xf6\xc2\x57\x4a\xe3\x9d\x08\xcb\x41\xf8\x07\xbe\x34\xca\x61\x45\xe7\x85\xb5\x9a\x83\xff\xa9\xaa\x42\x13\x29\xfa\x60\xdb\xfe\x0d\x4a\x02\xbe\x24\xb9\x8f\x42\x37\x08\xc1\x35\xec\xf0\x88\xaf\xa3\xd1\xb0\xed\x6c\x44\xef\xd0\x54\xfd\x79\x9b\x63\x8e\x3e\x28\x23\x82\xb2\x66\xc3\xb4\x15\x1c\x9d\x7e\xca\xb7\xce\x68\x24\xf7\xdc\x0d\x38\x0c\x8d\x33\x34\x09\x87\xa2\x12\x0b\x8d\xe0\x70\xe5\xd0\xa3\x09\xb1\x82\x95\x10\x96\xca\xc3\x2b\x49\xa5\x97\xb9\xb4\x0e\x1a\xb6\xb1\x42\x29\x1a\x1d\x7c\x91\xc9\xc6\x94\x90\xcb\x83\x73\x29\x52\xb1\xbc\x48\x1e\xa6\x09\xc5\xda\x40\x90\x08\x40\x97\xcb\x22\xe9\xfb\x17\x03\x53\x74\x02\xc3\x12\xc1\x50\x80\xf5\x20\x2f\xce\x17\x55\x13\xc5\x07\x65\x25\x83\x53\xb5\x6b\xdf\x8f\xb4\x2b\xf8\xb6\xc4\xb0\x44\x07\xd6\x81\xb1\xa1\xaf\x49\x6b\xeb\x12\xf6\x8b\xfa\x1b\xd2\xbc\xe0\x8d\x18\x0a\xe8\xd2\x49\xc4\x90\x05\xb4\xb5\x4f\x1e\x9a\x15\x17\x67\xff\xa9\x7b\x01\xda\x96\x42\x1f\x2c\x3a\xe9\xe4\xef\xf0\x5d\x58\x13\xf0\x57\x98\xc7\x69\x11\xb3\x92\xdc\x94\xb4\x8d\xe9\xba\x28\xe1\x34\xe1\x8a\x3d\xea\x9c\x9d\x8f\x2e\x16\x6d\xcb\xcf\x11\x3a\x5e\x5e\xb2\xf1\x98\x78\x87\x25\xd3\xfe\x51\x1c\xb5\xc7\x1d\xc8\x26\xd7\xad\xe6\xae\x3f\xd4\x7d\xb3\x3a\x28\x65\x02\xe5\x94\x82\xf7\x18\xfa\x7d\xd1\x76\x21\xf4\xd1\x16\xd6\x0c\xff\x4e\x0f\x0f\x0b\xfa\xb3\x4e\x2a\x09\xd2\xd3\xbf\x71\xf4\x32\x4a\xba\x8a\xc6\x75\x46\x16\x67\x84\xf9\xeb\x1c\x8c\xea\x16\xf4\xd8\x21\x48\x5f\x30\x7e\xbd\x3d\xb6\x8f\x1d\xa3\x49\xb1\x4f\x9f\x90\x26\x6b\x26\xe0\x31\xc0\x29\xa7\x93\xde\xef\x77\x4b\x92\x53\x1e\xc3\xf4\x7f\xd6\xc7\x7a\x8a\xde\xc7\x7d\x8b\x36\x82\xee\x84\xf3\xe8\xa2\x96\x15\x9d\xab\x09\xa0\x73\xc4\xd7\x97\x4d\x18\xfe\xa6\x25\x4d\x03\xa8\x8c\x5f\x07\xbe\x5b\x97\x1b\xa5\xa9\xe7\xf4\xaf\xdd\x15\xa5\x0f\x81\x73\xfb\x62\x8e\x9b\x40\x87\x5c\x7f\xd8\xc4\x85\xf0\x21\x36\x32\x64\xdb\xca\x6f\x37\x91\x60\xb1\x97\x5d\xbd\xc7\xef\x45\xff\xee\x77\x00\x00\x00\xff\xff\x4d\xaf\x90\xd0\xb6\x08\x00\x00"), diff --git a/flag-gen/main.go b/flag-gen/main.go index 099a5ff..9caa94e 100644 --- a/flag-gen/main.go +++ b/flag-gen/main.go @@ -1,7 +1,7 @@ -//go:generate go run assets_generate.go - package main +//go:generate go run assets_generate.go + import ( "encoding/json" "fmt" diff --git a/go.mod b/go.mod index d5b55c4..31f39cc 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,7 @@ go 1.12 require ( github.com/BurntSushi/toml v0.3.1 + github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect + github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd // indirect gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index a2ea702..f3cb3d0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= From 7a6f3d4394003447c99b223ce1c1e19e23b20124 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 20:23:32 +0530 Subject: [PATCH 145/215] fix tests --- altsrc/flag_generated.go | 26 +-- appveyor.yml | 6 +- flag-gen/assets_vfsdata.go | 14 +- .../templates/altsrc_flags_generated.gotpl | 12 +- flag-gen/templates/cli_flags_generated.gotpl | 56 ++--- flag_generated.go | 196 +++++++++--------- 6 files changed, 155 insertions(+), 155 deletions(-) diff --git a/altsrc/flag_generated.go b/altsrc/flag_generated.go index 0c45914..e568872 100644 --- a/altsrc/flag_generated.go +++ b/altsrc/flag_generated.go @@ -16,7 +16,7 @@ type BoolFlag struct { // NewBoolFlag creates a new BoolFlag func NewBoolFlag(fl cli.BoolFlag) *BoolFlag { - return &BoolFlag{BoolFlag: fl, set: nil} + return &BoolFlag{ BoolFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -42,7 +42,7 @@ type BoolTFlag struct { // NewBoolTFlag creates a new BoolTFlag func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag { - return &BoolTFlag{BoolTFlag: fl, set: nil} + return &BoolTFlag{ BoolTFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -68,7 +68,7 @@ type DurationFlag struct { // NewDurationFlag creates a new DurationFlag func NewDurationFlag(fl cli.DurationFlag) *DurationFlag { - return &DurationFlag{DurationFlag: fl, set: nil} + return &DurationFlag{ DurationFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -94,7 +94,7 @@ type Float64Flag struct { // NewFloat64Flag creates a new Float64Flag func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag { - return &Float64Flag{Float64Flag: fl, set: nil} + return &Float64Flag{ Float64Flag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -120,7 +120,7 @@ type GenericFlag struct { // NewGenericFlag creates a new GenericFlag func NewGenericFlag(fl cli.GenericFlag) *GenericFlag { - return &GenericFlag{GenericFlag: fl, set: nil} + return &GenericFlag{ GenericFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -146,7 +146,7 @@ type Int64Flag struct { // NewInt64Flag creates a new Int64Flag func NewInt64Flag(fl cli.Int64Flag) *Int64Flag { - return &Int64Flag{Int64Flag: fl, set: nil} + return &Int64Flag{ Int64Flag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -172,7 +172,7 @@ type IntFlag struct { // NewIntFlag creates a new IntFlag func NewIntFlag(fl cli.IntFlag) *IntFlag { - return &IntFlag{IntFlag: fl, set: nil} + return &IntFlag{ IntFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -198,7 +198,7 @@ type IntSliceFlag struct { // NewIntSliceFlag creates a new IntSliceFlag func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag { - return &IntSliceFlag{IntSliceFlag: fl, set: nil} + return &IntSliceFlag{ IntSliceFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -224,7 +224,7 @@ type Int64SliceFlag struct { // NewInt64SliceFlag creates a new Int64SliceFlag func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag { - return &Int64SliceFlag{Int64SliceFlag: fl, set: nil} + return &Int64SliceFlag{ Int64SliceFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -250,7 +250,7 @@ type StringFlag struct { // NewStringFlag creates a new StringFlag func NewStringFlag(fl cli.StringFlag) *StringFlag { - return &StringFlag{StringFlag: fl, set: nil} + return &StringFlag{ StringFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -276,7 +276,7 @@ type StringSliceFlag struct { // NewStringSliceFlag creates a new StringSliceFlag func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag { - return &StringSliceFlag{StringSliceFlag: fl, set: nil} + return &StringSliceFlag{ StringSliceFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -302,7 +302,7 @@ type Uint64Flag struct { // NewUint64Flag creates a new Uint64Flag func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag { - return &Uint64Flag{Uint64Flag: fl, set: nil} + return &Uint64Flag{ Uint64Flag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -328,7 +328,7 @@ type UintFlag struct { // NewUintFlag creates a new UintFlag func NewUintFlag(fl cli.UintFlag) *UintFlag { - return &UintFlag{UintFlag: fl, set: nil} + return &UintFlag{ UintFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls diff --git a/appveyor.yml b/appveyor.yml index 39c6aac..6e4806b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,6 +21,6 @@ install: build_script: - go run build/build.go generate - - go rub build/build.go vet - - go rub build/build.go test - - go rub build/build.go gfmrun + - go run build/build.go vet + - go run build/build.go test + - go run build/build.go gfmrun diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index c98c71d..f5fd185 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -34,21 +34,21 @@ var assets = func() http.FileSystem { }, "/templates": &vfsgenÛ°DirInfo{ name: "templates", - modTime: time.Date(2019, 8, 4, 5, 32, 59, 292856915, time.UTC), + modTime: time.Date(2019, 8, 4, 14, 51, 15, 358024804, time.UTC), }, "/templates/altsrc_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "altsrc_flags_generated.gotpl", - modTime: time.Date(2019, 8, 4, 5, 32, 59, 176187795, time.UTC), - uncompressedSize: 1062, + modTime: time.Date(2019, 8, 4, 14, 47, 17, 700754849, time.UTC), + uncompressedSize: 1044, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x41\x8b\xdb\x30\x10\x85\xcf\xab\x5f\xf1\x08\x4b\x89\x97\x60\xdf\x53\xf6\x50\xba\x5b\xe8\x25\x5b\xd8\x85\x9e\x15\x7b\x64\x8b\x2a\xb6\x91\xc6\x09\x41\xf8\xbf\x97\x91\x97\x34\xa5\x4a\xe9\xa5\x37\x59\xa3\xf7\xde\x7c\x33\xae\x2a\x7c\x1e\x1a\x42\x4b\x3d\x79\xcd\xd4\x60\x7f\x86\x69\x3f\xe2\xe9\x05\xbb\x97\x37\x3c\x3f\x7d\x7d\x2b\x95\x1a\x75\xfd\x43\xb7\x84\x18\x51\x7e\x5b\xce\x3b\x7d\x20\xcc\xb3\x52\xf6\x30\x0e\x9e\xb1\x56\x77\x2b\xe3\x74\xbb\x52\x00\xb0\x6a\x2d\x77\xd3\xbe\xac\x87\x43\x35\x79\xa3\x8f\x54\xd5\xce\xae\x54\xa1\x62\x84\xd7\x7d\x4b\xb8\xb7\x1b\xdc\x8b\x02\xdb\x47\x94\x5f\x9c\x6e\x83\xf8\x55\x95\xa4\xa4\x42\xf9\x9e\x21\x35\xd8\x00\xee\x08\x49\xc0\xe7\x91\xc0\x9d\x66\x9c\xbc\x1e\x03\x6a\x67\xcb\xac\x88\x07\x68\xe7\x86\x93\xb8\x9a\xc1\x63\xe0\x8e\x3c\x8e\xda\x4d\x14\xa4\xb8\x27\x84\x91\x6a\x6b\x2c\x35\x2a\xb9\x66\x6d\x02\xfb\xa9\x66\xc4\x84\x76\x2b\x2c\x15\x03\x31\x1e\x52\x45\xae\x5e\x89\xd5\xac\x24\x7c\x47\xa7\xac\x73\xed\x49\x33\x05\x68\xf4\x74\xca\x86\x2b\x33\xf5\xf5\x2d\xfd\xda\xb8\x9b\xfd\x14\x78\xc8\x46\x2e\x14\x9e\x78\xf2\x3d\x3e\xe4\x9e\xc4\x6c\x23\x5b\x18\xb7\x11\xc0\x2d\x7a\xeb\x30\xbf\x93\x7d\x1a\x47\x77\x46\xd0\x47\xfa\xb5\xa0\x57\xe2\x34\x6e\xa7\x99\x3c\xa6\x20\xbf\x4e\xad\x9d\x0b\x1b\x79\xd2\x2f\x67\x11\x8b\x40\x56\x38\x52\x93\xcd\x2c\x93\xfb\x32\x82\xb5\xc9\x03\x15\x4b\x0b\xeb\x3f\x46\x5f\x20\xaa\x3b\x53\xca\xfd\xa3\xf4\x2d\x1f\xb7\x43\x44\x5f\x5c\x33\x7d\xb7\xdc\x3d\x7b\x3f\xf8\xff\x0b\x77\x89\xf9\x27\xca\xcb\xeb\x1c\x2e\xa5\x6e\x97\xfd\x5e\x73\x5f\xed\xfb\x6f\x13\xf8\xcd\x5b\x46\x11\x23\xa8\x6f\x30\xcf\x3f\x03\x00\x00\xff\xff\xb0\x66\x7b\xfe\x26\x04\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x4f\x8b\xdb\x30\x10\xc5\xcf\xd6\xa7\x78\x84\xa5\xc4\x4b\xb0\xef\x29\x7b\x28\xdd\x2d\xf4\x92\x2d\xec\x42\xcf\x8a\x3c\xb2\x45\x15\xdb\x48\xe3\x84\x20\xfc\xdd\xcb\x38\x61\xfb\x4f\x29\xbd\xf4\x66\x69\xf4\xde\x9b\xdf\x8c\xeb\x1a\x1f\x87\x86\xd0\x52\x4f\x41\x33\x35\xd8\x9f\x61\xdb\xf7\x78\x7c\xc6\xee\xf9\x15\x4f\x8f\x9f\x5f\x2b\xa5\x46\x6d\xbe\xe9\x96\x90\x12\xaa\x2f\x97\xef\x9d\x3e\x10\xe6\x59\x29\x77\x18\x87\xc0\x58\xab\x62\x65\xbd\x6e\x57\xaa\x58\xb5\x8e\xbb\x69\x5f\x99\xe1\x50\x4f\xc1\xea\x23\xd5\xc6\xbb\x95\x2a\x55\x4a\x08\xba\x6f\x09\x77\x6e\x83\x3b\x79\x8e\xed\x03\xaa\x4f\x5e\xb7\x51\xcc\xea\x5a\x22\x96\x42\x75\x0d\x90\x1a\x5c\x04\x77\x84\x45\xc0\xe7\x91\xc0\x9d\x66\x9c\x82\x1e\x23\x8c\x77\x55\x56\xc4\x03\xb4\xf7\xc3\x49\x5c\xed\x10\x30\x70\x47\x01\x47\xed\x27\x8a\x52\xdc\x13\xe2\x48\xc6\x59\x47\x8d\x5a\x5c\xb3\x36\x91\xc3\x64\x18\x49\x15\xb7\x92\x54\x11\x89\x71\xbf\x5c\xcb\xf9\x85\x58\xcd\x4a\x62\x77\x74\xca\x7a\x9a\x40\x9a\x29\x42\xa3\xa7\x53\x36\x56\xd9\xa9\x37\xb7\xf4\x6b\xeb\x6f\x62\x97\xb8\xcf\x46\x26\x55\x04\xe2\x29\xf4\x78\x97\xab\xa7\x6c\x17\x5b\x58\xbf\x41\x24\xde\xa2\x77\x1e\xf3\x15\xeb\xc3\x38\xfa\x33\xa2\x3e\xd2\x8f\xbd\xbc\x10\x2f\x53\xf6\x9a\x29\x60\x8a\xf2\xbb\x18\xed\x7d\xdc\xc8\x93\xfe\xf2\x2d\x62\x11\xc8\xe6\x46\x6a\xb2\x99\xd5\xe2\x7e\xe1\x5f\xdb\x3c\x4d\x79\x69\x61\xfd\xc7\xdc\x4b\xe1\xb4\x95\xdc\x3f\x48\xdf\x72\xb8\x1d\x22\xfa\xf2\x67\xa6\xaf\x8e\xbb\xa7\x10\x86\xf0\x7f\xe1\xde\x62\xfe\x89\xf2\xed\x75\x0e\x97\x96\x6e\x7f\x87\xbe\x6e\xfa\x6f\xec\xbf\xb8\xca\x10\x52\x02\xf5\x0d\xe6\xf9\x7b\x00\x00\x00\xff\xff\x6d\x3d\x9e\xe0\x14\x04\x00\x00"), }, "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", - modTime: time.Date(2019, 8, 4, 5, 32, 38, 55742173, time.UTC), - uncompressedSize: 2230, + modTime: time.Date(2019, 8, 4, 14, 51, 15, 321357993, time.UTC), + uncompressedSize: 2102, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x41\x4f\xeb\x38\x10\x3e\x37\xbf\x62\xb6\xe2\x90\xa0\x6e\x7b\x5f\xc4\x89\xc2\x2e\xd2\x0a\xd0\x83\xc7\xdd\x4d\xc6\xa9\x85\xb1\x8b\xed\xc0\x43\x51\xff\xfb\xd3\x8c\x9d\xb4\x4d\x0b\xf4\xc2\xeb\xc9\x9e\xf9\xfc\xcd\x37\xdf\x8c\x9a\xd9\x0c\x2e\x6c\x85\x50\xa3\x41\x27\x02\x56\xb0\x78\x07\x59\x9f\xc1\xfc\x16\x6e\x6e\x1f\xe0\x72\x7e\xfd\x30\xcd\xb2\x95\x28\x9f\x44\x8d\xd0\xb6\x30\xbd\x8b\xe7\x1b\xf1\x8c\xb0\x5e\x67\x99\x7a\x5e\x59\x17\x20\xcf\x46\x63\xa9\x45\x3d\xce\x46\x63\x1f\x5c\x69\xcd\x2b\x1d\x83\x7a\xc6\x71\x56\x64\x6d\x0b\x4e\x98\x1a\xe1\x44\x4d\xe0\x84\x80\xf0\xcf\x39\x4c\xaf\xb4\xa8\x3d\xd1\xcc\x66\x44\xce\x89\x69\xa2\xa6\x1c\x28\x0f\x02\x18\xfe\xa6\xc2\x12\xc2\xfb\x0a\x37\xc0\x07\xba\xad\xd7\xfd\x7d\x6e\xcb\x20\x94\x26\xbe\x5d\xe0\x36\xa3\x0f\xae\x29\x03\xb4\x19\x00\x00\x27\xd2\xcf\x07\xa7\x4c\xcd\xe1\x9f\x9e\xba\xdd\x0b\x5f\x9a\xd7\x47\xe1\xf6\xc2\x57\x4a\xe3\x9d\x08\xcb\x41\xf8\x07\xbe\x34\xca\x61\x45\xe7\x85\xb5\x9a\x83\xff\xa9\xaa\x42\x13\x29\xfa\x60\xdb\xfe\x0d\x4a\x02\xbe\x24\xb9\x8f\x42\x37\x08\xc1\x35\xec\xf0\x88\xaf\xa3\xd1\xb0\xed\x6c\x44\xef\xd0\x54\xfd\x79\x9b\x63\x8e\x3e\x28\x23\x82\xb2\x66\xc3\xb4\x15\x1c\x9d\x7e\xca\xb7\xce\x68\x24\xf7\xdc\x0d\x38\x0c\x8d\x33\x34\x09\x87\xa2\x12\x0b\x8d\xe0\x70\xe5\xd0\xa3\x09\xb1\x82\x95\x10\x96\xca\xc3\x2b\x49\xa5\x97\xb9\xb4\x0e\x1a\xb6\xb1\x42\x29\x1a\x1d\x7c\x91\xc9\xc6\x94\x90\xcb\x83\x73\x29\x52\xb1\xbc\x48\x1e\xa6\x09\xc5\xda\x40\x90\x08\x40\x97\xcb\x22\xe9\xfb\x17\x03\x53\x74\x02\xc3\x12\xc1\x50\x80\xf5\x20\x2f\xce\x17\x55\x13\xc5\x07\x65\x25\x83\x53\xb5\x6b\xdf\x8f\xb4\x2b\xf8\xb6\xc4\xb0\x44\x07\xd6\x81\xb1\xa1\xaf\x49\x6b\xeb\x12\xf6\x8b\xfa\x1b\xd2\xbc\xe0\x8d\x18\x0a\xe8\xd2\x49\xc4\x90\x05\xb4\xb5\x4f\x1e\x9a\x15\x17\x67\xff\xa9\x7b\x01\xda\x96\x42\x1f\x2c\x3a\xe9\xe4\xef\xf0\x5d\x58\x13\xf0\x57\x98\xc7\x69\x11\xb3\x92\xdc\x94\xb4\x8d\xe9\xba\x28\xe1\x34\xe1\x8a\x3d\xea\x9c\x9d\x8f\x2e\x16\x6d\xcb\xcf\x11\x3a\x5e\x5e\xb2\xf1\x98\x78\x87\x25\xd3\xfe\x51\x1c\xb5\xc7\x1d\xc8\x26\xd7\xad\xe6\xae\x3f\xd4\x7d\xb3\x3a\x28\x65\x02\xe5\x94\x82\xf7\x18\xfa\x7d\xd1\x76\x21\xf4\xd1\x16\xd6\x0c\xff\x4e\x0f\x0f\x0b\xfa\xb3\x4e\x2a\x09\xd2\xd3\xbf\x71\xf4\x32\x4a\xba\x8a\xc6\x75\x46\x16\x67\x84\xf9\xeb\x1c\x8c\xea\x16\xf4\xd8\x21\x48\x5f\x30\x7e\xbd\x3d\xb6\x8f\x1d\xa3\x49\xb1\x4f\x9f\x90\x26\x6b\x26\xe0\x31\xc0\x29\xa7\x93\xde\xef\x77\x4b\x92\x53\x1e\xc3\xf4\x7f\xd6\xc7\x7a\x8a\xde\xc7\x7d\x8b\x36\x82\xee\x84\xf3\xe8\xa2\x96\x15\x9d\xab\x09\xa0\x73\xc4\xd7\x97\x4d\x18\xfe\xa6\x25\x4d\x03\xa8\x8c\x5f\x07\xbe\x5b\x97\x1b\xa5\xa9\xe7\xf4\xaf\xdd\x15\xa5\x0f\x81\x73\xfb\x62\x8e\x9b\x40\x87\x5c\x7f\xd8\xc4\x85\xf0\x21\x36\x32\x64\xdb\xca\x6f\x37\x91\x60\xb1\x97\x5d\xbd\xc7\xef\x45\xff\xee\x77\x00\x00\x00\xff\xff\x4d\xaf\x90\xd0\xb6\x08\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x54\xc1\x52\xdb\x30\x10\x3d\xdb\x5f\xb1\xcd\x70\xb0\x99\x34\xb9\x97\xe1\x44\xa0\x65\xa6\x03\x4c\xa1\xdc\x15\x7b\xe5\x68\x10\x52\x90\xd6\x50\x26\x93\x7f\xef\xac\x24\x3b\x4e\x48\x21\x17\x7a\xd3\x6a\x9f\xde\x7b\x7a\x5a\x7b\x3a\x85\x33\x5b\x23\x34\x68\xd0\x09\xc2\x1a\xe6\xaf\x20\x9b\x13\x98\x5d\xc3\xd5\xf5\x1d\x9c\xcf\x2e\xef\x26\x79\xbe\x14\xd5\x83\x68\x10\x56\x2b\x98\xdc\xc4\xf5\x95\x78\x44\x58\xaf\xf3\x5c\x3d\x2e\xad\x23\x28\xf2\x6c\x24\xb5\x68\x46\x79\x36\xf2\xe4\x2a\x6b\x9e\x79\x49\xea\x11\x47\x79\x99\xaf\x56\xe0\x84\x69\x10\x8e\xd4\x18\x8e\x18\x08\xdf\x4e\x61\x72\xa1\x45\xe3\x99\x66\x3a\x65\xf2\xd0\x98\x24\x6a\xee\x81\xf2\x20\x20\xc0\x5f\x14\x2d\x80\x5e\x97\xb8\x01\xde\x71\xb5\x5e\xf7\xf5\xcc\x56\x24\x94\x66\xbe\x6d\xe0\x90\xd1\x93\x6b\x2b\x82\x55\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\xdb\x8b\x66\x50\x9e\x9b\xe7\x7b\xe1\x36\xf5\x85\xd2\x78\x23\x68\xd1\x6f\xfc\xc2\xa7\x56\x39\xac\xb3\xb9\xb5\x3a\xcf\x7e\xa8\xba\x46\x93\xa5\x6a\xb5\xfa\x0a\x4a\x02\x3e\x25\x03\xf7\x42\xb7\x08\xe4\xda\x90\x59\x16\xca\x6c\xf7\x1e\xf1\x18\x9a\xba\x5f\x0f\x29\x66\xe8\x49\x19\x41\xca\x9a\x0d\xd1\x60\x33\x3b\x7e\x97\x6f\x9d\x73\xc6\xb7\xc1\x3c\x38\xa4\xd6\x19\x8e\xd6\xa1\xa8\xc5\x5c\x23\x38\x5c\x3a\xf4\x68\x28\x2a\x58\x09\xb4\x50\x1e\x9e\xd9\x29\x9f\x2c\xa4\x75\xd0\x72\x46\x50\xa3\x14\xad\x26\x5f\xe6\xb2\x35\x15\x14\x72\x6f\xd0\x65\x12\x2b\x4a\x88\x91\x71\xe4\x51\x18\xb8\x1f\xbb\xe8\x0a\x59\x26\x73\xdf\x91\xc2\xf9\xce\x1d\x2d\x10\x0c\x6f\x04\x33\x18\xc6\xe0\x03\xc9\x44\xb1\x4f\x53\x06\x64\x92\xba\xf4\xdd\xf3\xf5\x6a\x2f\x0b\xa4\x05\x3a\xb0\x0e\x8c\xa5\x5e\x90\x27\xd0\x25\xec\x07\xe2\x1b\xd2\xa2\x04\x9e\x83\x2d\xf5\xae\x97\x1c\xec\x52\x80\xb6\xf6\xc1\x43\xbb\x0c\xca\x21\x76\xbe\xb7\x00\x6d\x2b\xa1\xf7\x2a\x8e\x3b\xef\x5b\x7c\x67\xd6\x10\xfe\xa1\x59\x7c\x24\x66\x56\x32\xdc\x48\xda\xd6\x74\x57\xa8\xe0\x38\xe1\xca\x37\xd4\x45\xc8\x3c\xe6\x17\xba\x7c\x1e\xa1\x23\x0e\xc3\x35\x1a\x0d\xbf\xbb\x61\x27\x6c\xa3\xf6\x5b\x1f\x66\xdf\xe9\xc6\x71\x90\x0c\xdf\xbb\x5d\xee\x35\x31\x86\x6a\xc2\x9b\xb7\x48\xfd\x8c\x68\x3b\x17\xfa\xe0\xf0\x9a\x00\xff\xcc\xf4\xf6\x1b\xfa\x5f\x19\x2a\x09\xd2\xf3\x9f\x34\xa6\x18\xcd\x5c\xc4\xc8\xba\x08\xcb\x13\xc6\x7c\x39\x05\xa3\xc2\x44\x1e\x14\xbc\xf4\x65\x9e\xad\xfb\x47\xfa\x77\x3e\xfc\x2e\x21\x95\x77\xe8\x52\x10\x63\xf0\x48\x70\x1c\xda\xc9\xe3\x67\x66\x23\x39\x17\x8f\x34\xf9\x19\x9c\x05\x27\x65\x8c\x6c\x98\xc6\xc6\xc0\x8d\x70\x1e\x5d\xd4\x5e\xf2\xba\x1e\x03\x3a\xc7\x34\xbd\x52\xc2\x0c\x5d\xec\x40\x65\xfc\xd9\x87\xda\xba\xc2\x28\x5d\x32\x36\xfd\xd5\x59\x9e\x81\x03\x03\x07\x64\x9c\xf1\x53\xbc\x71\x7a\x26\x3c\x45\xb7\xbb\x0c\x83\xfe\xd0\x69\x82\x45\xc3\x03\x53\x07\xbe\x73\x7f\xe2\x6f\x00\x00\x00\xff\xff\x9b\xde\x9c\x04\x36\x08\x00\x00"), }, } fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ diff --git a/flag-gen/templates/altsrc_flags_generated.gotpl b/flag-gen/templates/altsrc_flags_generated.gotpl index e89eb8e..07f6e82 100644 --- a/flag-gen/templates/altsrc_flags_generated.gotpl +++ b/flag-gen/templates/altsrc_flags_generated.gotpl @@ -4,19 +4,19 @@ package {{ .PackageName }} import ( "flag" - "github.com/urfave/cli" + "github.com/urfave/cli" ) {{ range $i, $flag := .Flags }} // {{ $flag.Name }}Flag is the flag type that wraps cli.{{ $flag.Name }}Flag to allow // for other values to be specified type {{ $flag.Name }}Flag struct { - cli.{{ $flag.Name }}Flag - set *flag.FlagSet + cli.{{ $flag.Name }}Flag + set *flag.FlagSet } // New{{ $flag.Name }}Flag creates a new {{ $flag.Name }}Flag func New{{ $flag.Name }}Flag(fl cli.{{ $flag.Name }}Flag) *{{ $flag.Name }}Flag { - return &{{ $flag.Name }}Flag{ {{ $flag.Name }}Flag: fl, set: nil } + return &{{ $flag.Name }}Flag{ {{ $flag.Name }}Flag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls @@ -29,7 +29,7 @@ func (f *{{ $flag.Name }}Flag) Apply(set *flag.FlagSet) { // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped {{ $flag.Name }}Flag.ApplyWithError func (f *{{ $flag.Name }}Flag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.{{ $flag.Name }}Flag.ApplyWithError(set) + f.set = set + return f.{{ $flag.Name }}Flag.ApplyWithError(set) } {{ end }} \ No newline at end of file diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index 3a6ca7c..3d4c64d 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -10,14 +10,14 @@ import ( {{ range $i, $flag := .Flags }} // {{ $flag.Name }}Flag is a flag with type {{ $flag.Type }}{{ $flag.Doctail }} type {{ $flag.Name }}Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - {{- if eq $flag.Value true }} - Value {{ $flag.Type }} + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + {{- if eq $flag.Value true }} + Value {{ $flag.Type }} {{- end }} {{- if eq $flag.Destination true }} Destination *{{ $flag.Type }} @@ -27,43 +27,43 @@ type {{ $flag.Name }}Flag struct { // String returns a readable representation of this value // (for usage defaults) func (f {{ $flag.Name }}Flag) String() string { - return FlagStringer(f) + return FlagStringer(f) } // GetName returns the name of the flag func (f {{ $flag.Name }}Flag) GetName() string { - return f.Name + return f.Name } // IsRequired returns whether or not the flag is required func (f {{ $flag.Name }}Flag) IsRequired() bool { - return f.Required + return f.Required } // {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns // {{ $flag.ContextDefault }} if not found -func (c *Context) {{ $flag.Name }}(name string){{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { - return lookup{{ $flag.Name }}(name, c.flagSet) +func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { + return lookup{{ $flag.Name }}(name, c.flagSet) } // Global{{ $flag.Name }} looks up the value of a global {{ $flag.Name }}Flag, returns // {{ $flag.ContextDefault }} if not found -func (c *Context) Global{{ $flag.Name }}(name string){{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookup{{ $flag.Name }}(name, fs) - } - return {{ $flag.ContextDefault }} +func (c *Context) Global{{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookup{{ $flag.Name }}(name, fs) + } + return {{ $flag.ContextDefault }} } -func lookup{{ $flag.Name }}(name string, set *flag.FlagSet){{ if ne .ContextType "" }} {{ $flag.ContextType }} {{ else }} {{ $flag.Type }} {{- end }} { - f := set.Lookup(name) - if f != nil { - {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} - if err != nil { - return {{ $flag.ContextDefault }} - } - {{ if ne .ParserCast "" }}return {{ $flag.ParserCast }}{{ else }}return parsed{{ end }} - } - return {{ $flag.ContextDefault }} +func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { + f := set.Lookup(name) + if f != nil { + {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} + if err != nil { + return {{ $flag.ContextDefault }} + } + {{ if ne .ParserCast "" }}return {{ $flag.ParserCast }}{{ else }}return parsed{{ end }} + } + return {{ $flag.ContextDefault }} } {{ end }} \ No newline at end of file diff --git a/flag_generated.go b/flag_generated.go index f106d23..83e10d7 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -10,13 +10,13 @@ import ( // BoolFlag is a flag with type bool type BoolFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool } // String returns a readable representation of this value @@ -64,13 +64,13 @@ func lookupBool(name string, set *flag.FlagSet) bool { // BoolTFlag is a flag with type bool that is true by default type BoolTFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool } // String returns a readable representation of this value @@ -118,14 +118,14 @@ func lookupBoolT(name string, set *flag.FlagSet) bool { // DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) type DurationFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value time.Duration - Destination *time.Duration + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value time.Duration + Destination *time.Duration } // String returns a readable representation of this value @@ -173,14 +173,14 @@ func lookupDuration(name string, set *flag.FlagSet) time.Duration { // Float64Flag is a flag with type float64 type Float64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value float64 - Destination *float64 + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value float64 + Destination *float64 } // String returns a readable representation of this value @@ -228,13 +228,13 @@ func lookupFloat64(name string, set *flag.FlagSet) float64 { // GenericFlag is a flag with type Generic type GenericFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value Generic + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value Generic } // String returns a readable representation of this value @@ -282,14 +282,14 @@ func lookupGeneric(name string, set *flag.FlagSet) interface{} { // Int64Flag is a flag with type int64 type Int64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int64 - Destination *int64 + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int64 + Destination *int64 } // String returns a readable representation of this value @@ -337,14 +337,14 @@ func lookupInt64(name string, set *flag.FlagSet) int64 { // IntFlag is a flag with type int type IntFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int - Destination *int + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int + Destination *int } // String returns a readable representation of this value @@ -392,13 +392,13 @@ func lookupInt(name string, set *flag.FlagSet) int { // IntSliceFlag is a flag with type *IntSlice type IntSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *IntSlice + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *IntSlice } // String returns a readable representation of this value @@ -446,13 +446,13 @@ func lookupIntSlice(name string, set *flag.FlagSet) []int { // Int64SliceFlag is a flag with type *Int64Slice type Int64SliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *Int64Slice + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *Int64Slice } // String returns a readable representation of this value @@ -500,14 +500,14 @@ func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { // StringFlag is a flag with type string type StringFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value string - Destination *string + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value string + Destination *string } // String returns a readable representation of this value @@ -555,13 +555,13 @@ func lookupString(name string, set *flag.FlagSet) string { // StringSliceFlag is a flag with type *StringSlice type StringSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *StringSlice + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *StringSlice } // String returns a readable representation of this value @@ -609,14 +609,14 @@ func lookupStringSlice(name string, set *flag.FlagSet) []string { // Uint64Flag is a flag with type uint64 type Uint64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint64 - Destination *uint64 + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint64 + Destination *uint64 } // String returns a readable representation of this value @@ -664,14 +664,14 @@ func lookupUint64(name string, set *flag.FlagSet) uint64 { // UintFlag is a flag with type uint type UintFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint - Destination *uint + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint + Destination *uint } // String returns a readable representation of this value From 798e1f3d3aba8d04c9118962870b41ad099b7bea Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 20:29:53 +0530 Subject: [PATCH 146/215] fix spacing issue --- flag-gen/templates/cli_flags_generated.gotpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index 3d4c64d..97bd49f 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -10,7 +10,7 @@ import ( {{ range $i, $flag := .Flags }} // {{ $flag.Name }}Flag is a flag with type {{ $flag.Type }}{{ $flag.Doctail }} type {{ $flag.Name }}Flag struct { - Name string + Name string Usage string EnvVar string FilePath string From a7f0d86509d0845980b77999dc5882b32f244818 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 20:33:21 +0530 Subject: [PATCH 147/215] add zero mod fs back and commit file with latest ts --- flag-gen/assets_generate.go | 38 +++++++++++++++++++++++++++++++++---- flag-gen/assets_vfsdata.go | 4 ++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/flag-gen/assets_generate.go b/flag-gen/assets_generate.go index c3847d6..50f6045 100644 --- a/flag-gen/assets_generate.go +++ b/flag-gen/assets_generate.go @@ -9,11 +9,41 @@ import ( "net/http" ) +// zeroModTimeFileSystem is an http.FileSystem wrapper. +// It exposes a filesystem exactly like Source, except +// all file modification times are changed to zero. +// See https://github.com/shurcooL/vfsgen/pull/40#issuecomment-355416103 +type zeroModTimeFileSystem struct { + Source http.FileSystem +} + +func (fs zeroModTimeFileSystem) Open(name string) (http.File, error) { + f, err := fs.Source.Open(name) + return file{f}, err +} + +type file struct { + http.File +} + +func (f file) Stat() (os.FileInfo, error) { + fi, err := f.File.Stat() + return fileInfo{fi}, err +} + +type fileInfo struct { + os.FileInfo +} + +func (fi fileInfo) ModTime() time.Time { return time.Time{} } + func main() { - fs := union.New(map[string]http.FileSystem{ - "/templates": http.Dir("templates"), - "/source": http.Dir("source"), - }) + fs := zeroModTimeFileSystem{ + Source: union.New(map[string]http.FileSystem{ + "/templates": http.Dir("templates"), + "/source": http.Dir("source"), + }), + } err := vfsgen.Generate(fs, vfsgen.Options{}) diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index f5fd185..b230722 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -34,7 +34,7 @@ var assets = func() http.FileSystem { }, "/templates": &vfsgenÛ°DirInfo{ name: "templates", - modTime: time.Date(2019, 8, 4, 14, 51, 15, 358024804, time.UTC), + modTime: time.Date(2019, 8, 4, 14, 58, 18, 980401456, time.UTC), }, "/templates/altsrc_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "altsrc_flags_generated.gotpl", @@ -45,7 +45,7 @@ var assets = func() http.FileSystem { }, "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", - modTime: time.Date(2019, 8, 4, 14, 51, 15, 321357993, time.UTC), + modTime: time.Date(2019, 8, 4, 14, 58, 18, 943734538, time.UTC), uncompressedSize: 2102, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x54\xc1\x52\xdb\x30\x10\x3d\xdb\x5f\xb1\xcd\x70\xb0\x99\x34\xb9\x97\xe1\x44\xa0\x65\xa6\x03\x4c\xa1\xdc\x15\x7b\xe5\x68\x10\x52\x90\xd6\x50\x26\x93\x7f\xef\xac\x24\x3b\x4e\x48\x21\x17\x7a\xd3\x6a\x9f\xde\x7b\x7a\x5a\x7b\x3a\x85\x33\x5b\x23\x34\x68\xd0\x09\xc2\x1a\xe6\xaf\x20\x9b\x13\x98\x5d\xc3\xd5\xf5\x1d\x9c\xcf\x2e\xef\x26\x79\xbe\x14\xd5\x83\x68\x10\x56\x2b\x98\xdc\xc4\xf5\x95\x78\x44\x58\xaf\xf3\x5c\x3d\x2e\xad\x23\x28\xf2\x6c\x24\xb5\x68\x46\x79\x36\xf2\xe4\x2a\x6b\x9e\x79\x49\xea\x11\x47\x79\x99\xaf\x56\xe0\x84\x69\x10\x8e\xd4\x18\x8e\x18\x08\xdf\x4e\x61\x72\xa1\x45\xe3\x99\x66\x3a\x65\xf2\xd0\x98\x24\x6a\xee\x81\xf2\x20\x20\xc0\x5f\x14\x2d\x80\x5e\x97\xb8\x01\xde\x71\xb5\x5e\xf7\xf5\xcc\x56\x24\x94\x66\xbe\x6d\xe0\x90\xd1\x93\x6b\x2b\x82\x55\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\xdb\x8b\x66\x50\x9e\x9b\xe7\x7b\xe1\x36\xf5\x85\xd2\x78\x23\x68\xd1\x6f\xfc\xc2\xa7\x56\x39\xac\xb3\xb9\xb5\x3a\xcf\x7e\xa8\xba\x46\x93\xa5\x6a\xb5\xfa\x0a\x4a\x02\x3e\x25\x03\xf7\x42\xb7\x08\xe4\xda\x90\x59\x16\xca\x6c\xf7\x1e\xf1\x18\x9a\xba\x5f\x0f\x29\x66\xe8\x49\x19\x41\xca\x9a\x0d\xd1\x60\x33\x3b\x7e\x97\x6f\x9d\x73\xc6\xb7\xc1\x3c\x38\xa4\xd6\x19\x8e\xd6\xa1\xa8\xc5\x5c\x23\x38\x5c\x3a\xf4\x68\x28\x2a\x58\x09\xb4\x50\x1e\x9e\xd9\x29\x9f\x2c\xa4\x75\xd0\x72\x46\x50\xa3\x14\xad\x26\x5f\xe6\xb2\x35\x15\x14\x72\x6f\xd0\x65\x12\x2b\x4a\x88\x91\x71\xe4\x51\x18\xb8\x1f\xbb\xe8\x0a\x59\x26\x73\xdf\x91\xc2\xf9\xce\x1d\x2d\x10\x0c\x6f\x04\x33\x18\xc6\xe0\x03\xc9\x44\xb1\x4f\x53\x06\x64\x92\xba\xf4\xdd\xf3\xf5\x6a\x2f\x0b\xa4\x05\x3a\xb0\x0e\x8c\xa5\x5e\x90\x27\xd0\x25\xec\x07\xe2\x1b\xd2\xa2\x04\x9e\x83\x2d\xf5\xae\x97\x1c\xec\x52\x80\xb6\xf6\xc1\x43\xbb\x0c\xca\x21\x76\xbe\xb7\x00\x6d\x2b\xa1\xf7\x2a\x8e\x3b\xef\x5b\x7c\x67\xd6\x10\xfe\xa1\x59\x7c\x24\x66\x56\x32\xdc\x48\xda\xd6\x74\x57\xa8\xe0\x38\xe1\xca\x37\xd4\x45\xc8\x3c\xe6\x17\xba\x7c\x1e\xa1\x23\x0e\xc3\x35\x1a\x0d\xbf\xbb\x61\x27\x6c\xa3\xf6\x5b\x1f\x66\xdf\xe9\xc6\x71\x90\x0c\xdf\xbb\x5d\xee\x35\x31\x86\x6a\xc2\x9b\xb7\x48\xfd\x8c\x68\x3b\x17\xfa\xe0\xf0\x9a\x00\xff\xcc\xf4\xf6\x1b\xfa\x5f\x19\x2a\x09\xd2\xf3\x9f\x34\xa6\x18\xcd\x5c\xc4\xc8\xba\x08\xcb\x13\xc6\x7c\x39\x05\xa3\xc2\x44\x1e\x14\xbc\xf4\x65\x9e\xad\xfb\x47\xfa\x77\x3e\xfc\x2e\x21\x95\x77\xe8\x52\x10\x63\xf0\x48\x70\x1c\xda\xc9\xe3\x67\x66\x23\x39\x17\x8f\x34\xf9\x19\x9c\x05\x27\x65\x8c\x6c\x98\xc6\xc6\xc0\x8d\x70\x1e\x5d\xd4\x5e\xf2\xba\x1e\x03\x3a\xc7\x34\xbd\x52\xc2\x0c\x5d\xec\x40\x65\xfc\xd9\x87\xda\xba\xc2\x28\x5d\x32\x36\xfd\xd5\x59\x9e\x81\x03\x03\x07\x64\x9c\xf1\x53\xbc\x71\x7a\x26\x3c\x45\xb7\xbb\x0c\x83\xfe\xd0\x69\x82\x45\xc3\x03\x53\x07\xbe\x73\x7f\xe2\x6f\x00\x00\x00\xff\xff\x9b\xde\x9c\x04\x36\x08\x00\x00"), From 489d92d2e2477b240015e83af07658e826eaa7cb Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 20:44:15 +0530 Subject: [PATCH 148/215] add missing os package --- flag-gen/assets_generate.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flag-gen/assets_generate.go b/flag-gen/assets_generate.go index 50f6045..021da71 100644 --- a/flag-gen/assets_generate.go +++ b/flag-gen/assets_generate.go @@ -7,6 +7,8 @@ import ( "github.com/shurcooL/vfsgen" "log" "net/http" + "os" + "time" ) // zeroModTimeFileSystem is an http.FileSystem wrapper. From ac5c97b41844032ae47d5f94d7f73533af629f11 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Sun, 4 Aug 2019 20:45:28 +0530 Subject: [PATCH 149/215] add latest assets file --- flag-gen/assets_vfsdata.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index b230722..024039a 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -23,29 +23,29 @@ var assets = func() http.FileSystem { }, "/source": &vfsgenÛ°DirInfo{ name: "source", - modTime: time.Date(2019, 8, 4, 4, 7, 51, 386498815, time.UTC), + modTime: time.Time{}, }, "/source/flag-types.json": &vfsgenÛ°CompressedFileInfo{ name: "flag-types.json", - modTime: time.Date(2019, 8, 4, 2, 47, 4, 104288603, time.UTC), + modTime: time.Time{}, uncompressedSize: 2559, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x94\xc1\x6e\x9c\x30\x10\x86\xef\x3c\xc5\xc8\xbd\x40\xb5\x82\x1c\x56\x7b\xd8\x63\x55\xb5\xda\x5b\xa5\x34\xbd\x24\x51\xe4\x80\x21\x56\x1d\x1b\xd9\x43\xd4\x28\xca\xbb\x57\xf6\x2e\xbb\x60\x0c\x62\x5b\x2d\xb9\xfe\x23\x98\xef\x9b\xb1\xe6\x36\x02\x78\x8b\x00\x00\x88\xa4\xcf\x8c\x6c\x81\x7c\x51\x4a\x90\xd5\x3e\xc3\xd7\xda\x65\x8f\x9d\xec\x85\x8a\xc6\x86\x25\x15\x86\x1d\xb2\x82\x19\x24\x5b\x40\xdd\xb4\x49\xae\x24\xb2\x3f\xf8\x50\xb0\x92\x36\xc2\x16\x89\xfb\xa0\xfd\x4b\x4d\xb5\x61\xda\xc6\x06\x75\xae\xe4\x4b\xfa\xc3\x26\xb6\x79\x5c\xa6\xbf\x6c\x8f\xf4\x1a\x35\x97\x55\x9c\x24\x24\x02\x78\x5f\x85\x51\x7f\xfe\x3f\x6b\xa1\x72\xa4\x5c\xd8\x8f\x01\x9f\x28\x02\x37\xae\x0c\x8f\xaf\xd0\xf2\x2f\xa9\xf5\xb5\xd1\x14\xb9\x92\xbe\x19\xf2\x67\x96\xfa\xc5\x56\xb1\xeb\x33\x69\x18\x1b\xc6\xe0\x09\xb1\x36\xdb\x2c\xab\x94\xa0\xb2\x4a\x95\xae\xb2\xfa\x77\x95\xd9\x0e\xd9\x27\x87\xdc\xf6\x49\x26\xd4\xaf\x86\xda\x8e\xb1\xf7\x83\xf9\xde\xdf\x84\xa2\xb8\x59\xfb\xda\x65\x3f\x9e\x25\x3c\x0f\xb6\xb7\x23\xd7\x7d\x00\xbb\x82\xcd\x7a\x14\xf8\x3b\x93\x4c\xf3\xdc\x07\xf6\xe2\x71\xe0\xee\xb3\x0c\x10\x4b\x2e\xfc\xe1\xb7\x2d\xb8\x44\xa6\x4b\x9a\xb3\xb7\xf7\x31\xb8\x9d\x0c\xcc\x92\xcb\x05\x26\xb9\x93\xa1\x39\x5e\x4d\x8e\x72\x27\x31\xc0\xfa\xa1\xa4\xbd\x4f\x1f\x72\xea\xfa\x59\xaa\xd8\x45\xc5\x94\xcb\xb5\xe0\x39\xf3\x85\x3e\xfb\x85\x4b\xbc\x8c\xdb\xfb\xce\xdc\x4e\xda\x47\xcd\xf8\x48\x91\x24\xfb\xc8\x2a\x33\xad\x95\x8e\x25\x17\x53\x52\x9b\xf5\xa8\x96\x57\xba\x98\xd8\xe9\xf1\x8e\xa9\x1d\x48\xce\x93\xdb\xef\xde\x17\x33\xbd\xf4\x5f\xdf\xe0\x1d\xb9\x23\x43\xe8\xe1\xab\x9b\x4b\x19\xde\x41\xa0\x76\x99\x25\xf4\x87\x12\xdc\x42\x87\xe5\xbc\x35\xdc\xf0\xd0\xcd\x6a\x16\x39\x5a\xb6\xf7\xd9\x57\xeb\x86\x0f\xcf\x56\xb3\xc0\xdd\x9a\x86\x0d\x1f\xae\xc6\xbf\x5c\xd1\x7d\xf4\x37\x00\x00\xff\xff\x66\x52\x85\x44\xff\x09\x00\x00"), }, "/templates": &vfsgenÛ°DirInfo{ name: "templates", - modTime: time.Date(2019, 8, 4, 14, 58, 18, 980401456, time.UTC), + modTime: time.Time{}, }, "/templates/altsrc_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "altsrc_flags_generated.gotpl", - modTime: time.Date(2019, 8, 4, 14, 47, 17, 700754849, time.UTC), + modTime: time.Time{}, uncompressedSize: 1044, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x4f\x8b\xdb\x30\x10\xc5\xcf\xd6\xa7\x78\x84\xa5\xc4\x4b\xb0\xef\x29\x7b\x28\xdd\x2d\xf4\x92\x2d\xec\x42\xcf\x8a\x3c\xb2\x45\x15\xdb\x48\xe3\x84\x20\xfc\xdd\xcb\x38\x61\xfb\x4f\x29\xbd\xf4\x66\x69\xf4\xde\x9b\xdf\x8c\xeb\x1a\x1f\x87\x86\xd0\x52\x4f\x41\x33\x35\xd8\x9f\x61\xdb\xf7\x78\x7c\xc6\xee\xf9\x15\x4f\x8f\x9f\x5f\x2b\xa5\x46\x6d\xbe\xe9\x96\x90\x12\xaa\x2f\x97\xef\x9d\x3e\x10\xe6\x59\x29\x77\x18\x87\xc0\x58\xab\x62\x65\xbd\x6e\x57\xaa\x58\xb5\x8e\xbb\x69\x5f\x99\xe1\x50\x4f\xc1\xea\x23\xd5\xc6\xbb\x95\x2a\x55\x4a\x08\xba\x6f\x09\x77\x6e\x83\x3b\x79\x8e\xed\x03\xaa\x4f\x5e\xb7\x51\xcc\xea\x5a\x22\x96\x42\x75\x0d\x90\x1a\x5c\x04\x77\x84\x45\xc0\xe7\x91\xc0\x9d\x66\x9c\x82\x1e\x23\x8c\x77\x55\x56\xc4\x03\xb4\xf7\xc3\x49\x5c\xed\x10\x30\x70\x47\x01\x47\xed\x27\x8a\x52\xdc\x13\xe2\x48\xc6\x59\x47\x8d\x5a\x5c\xb3\x36\x91\xc3\x64\x18\x49\x15\xb7\x92\x54\x11\x89\x71\xbf\x5c\xcb\xf9\x85\x58\xcd\x4a\x62\x77\x74\xca\x7a\x9a\x40\x9a\x29\x42\xa3\xa7\x53\x36\x56\xd9\xa9\x37\xb7\xf4\x6b\xeb\x6f\x62\x97\xb8\xcf\x46\x26\x55\x04\xe2\x29\xf4\x78\x97\xab\xa7\x6c\x17\x5b\x58\xbf\x41\x24\xde\xa2\x77\x1e\xf3\x15\xeb\xc3\x38\xfa\x33\xa2\x3e\xd2\x8f\xbd\xbc\x10\x2f\x53\xf6\x9a\x29\x60\x8a\xf2\xbb\x18\xed\x7d\xdc\xc8\x93\xfe\xf2\x2d\x62\x11\xc8\xe6\x46\x6a\xb2\x99\xd5\xe2\x7e\xe1\x5f\xdb\x3c\x4d\x79\x69\x61\xfd\xc7\xdc\x4b\xe1\xb4\x95\xdc\x3f\x48\xdf\x72\xb8\x1d\x22\xfa\xf2\x67\xa6\xaf\x8e\xbb\xa7\x10\x86\xf0\x7f\xe1\xde\x62\xfe\x89\xf2\xed\x75\x0e\x97\x96\x6e\x7f\x87\xbe\x6e\xfa\x6f\xec\xbf\xb8\xca\x10\x52\x02\xf5\x0d\xe6\xf9\x7b\x00\x00\x00\xff\xff\x6d\x3d\x9e\xe0\x14\x04\x00\x00"), }, "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", - modTime: time.Date(2019, 8, 4, 14, 58, 18, 943734538, time.UTC), + modTime: time.Time{}, uncompressedSize: 2102, compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x54\xc1\x52\xdb\x30\x10\x3d\xdb\x5f\xb1\xcd\x70\xb0\x99\x34\xb9\x97\xe1\x44\xa0\x65\xa6\x03\x4c\xa1\xdc\x15\x7b\xe5\x68\x10\x52\x90\xd6\x50\x26\x93\x7f\xef\xac\x24\x3b\x4e\x48\x21\x17\x7a\xd3\x6a\x9f\xde\x7b\x7a\x5a\x7b\x3a\x85\x33\x5b\x23\x34\x68\xd0\x09\xc2\x1a\xe6\xaf\x20\x9b\x13\x98\x5d\xc3\xd5\xf5\x1d\x9c\xcf\x2e\xef\x26\x79\xbe\x14\xd5\x83\x68\x10\x56\x2b\x98\xdc\xc4\xf5\x95\x78\x44\x58\xaf\xf3\x5c\x3d\x2e\xad\x23\x28\xf2\x6c\x24\xb5\x68\x46\x79\x36\xf2\xe4\x2a\x6b\x9e\x79\x49\xea\x11\x47\x79\x99\xaf\x56\xe0\x84\x69\x10\x8e\xd4\x18\x8e\x18\x08\xdf\x4e\x61\x72\xa1\x45\xe3\x99\x66\x3a\x65\xf2\xd0\x98\x24\x6a\xee\x81\xf2\x20\x20\xc0\x5f\x14\x2d\x80\x5e\x97\xb8\x01\xde\x71\xb5\x5e\xf7\xf5\xcc\x56\x24\x94\x66\xbe\x6d\xe0\x90\xd1\x93\x6b\x2b\x82\x55\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\xdb\x8b\x66\x50\x9e\x9b\xe7\x7b\xe1\x36\xf5\x85\xd2\x78\x23\x68\xd1\x6f\xfc\xc2\xa7\x56\x39\xac\xb3\xb9\xb5\x3a\xcf\x7e\xa8\xba\x46\x93\xa5\x6a\xb5\xfa\x0a\x4a\x02\x3e\x25\x03\xf7\x42\xb7\x08\xe4\xda\x90\x59\x16\xca\x6c\xf7\x1e\xf1\x18\x9a\xba\x5f\x0f\x29\x66\xe8\x49\x19\x41\xca\x9a\x0d\xd1\x60\x33\x3b\x7e\x97\x6f\x9d\x73\xc6\xb7\xc1\x3c\x38\xa4\xd6\x19\x8e\xd6\xa1\xa8\xc5\x5c\x23\x38\x5c\x3a\xf4\x68\x28\x2a\x58\x09\xb4\x50\x1e\x9e\xd9\x29\x9f\x2c\xa4\x75\xd0\x72\x46\x50\xa3\x14\xad\x26\x5f\xe6\xb2\x35\x15\x14\x72\x6f\xd0\x65\x12\x2b\x4a\x88\x91\x71\xe4\x51\x18\xb8\x1f\xbb\xe8\x0a\x59\x26\x73\xdf\x91\xc2\xf9\xce\x1d\x2d\x10\x0c\x6f\x04\x33\x18\xc6\xe0\x03\xc9\x44\xb1\x4f\x53\x06\x64\x92\xba\xf4\xdd\xf3\xf5\x6a\x2f\x0b\xa4\x05\x3a\xb0\x0e\x8c\xa5\x5e\x90\x27\xd0\x25\xec\x07\xe2\x1b\xd2\xa2\x04\x9e\x83\x2d\xf5\xae\x97\x1c\xec\x52\x80\xb6\xf6\xc1\x43\xbb\x0c\xca\x21\x76\xbe\xb7\x00\x6d\x2b\xa1\xf7\x2a\x8e\x3b\xef\x5b\x7c\x67\xd6\x10\xfe\xa1\x59\x7c\x24\x66\x56\x32\xdc\x48\xda\xd6\x74\x57\xa8\xe0\x38\xe1\xca\x37\xd4\x45\xc8\x3c\xe6\x17\xba\x7c\x1e\xa1\x23\x0e\xc3\x35\x1a\x0d\xbf\xbb\x61\x27\x6c\xa3\xf6\x5b\x1f\x66\xdf\xe9\xc6\x71\x90\x0c\xdf\xbb\x5d\xee\x35\x31\x86\x6a\xc2\x9b\xb7\x48\xfd\x8c\x68\x3b\x17\xfa\xe0\xf0\x9a\x00\xff\xcc\xf4\xf6\x1b\xfa\x5f\x19\x2a\x09\xd2\xf3\x9f\x34\xa6\x18\xcd\x5c\xc4\xc8\xba\x08\xcb\x13\xc6\x7c\x39\x05\xa3\xc2\x44\x1e\x14\xbc\xf4\x65\x9e\xad\xfb\x47\xfa\x77\x3e\xfc\x2e\x21\x95\x77\xe8\x52\x10\x63\xf0\x48\x70\x1c\xda\xc9\xe3\x67\x66\x23\x39\x17\x8f\x34\xf9\x19\x9c\x05\x27\x65\x8c\x6c\x98\xc6\xc6\xc0\x8d\x70\x1e\x5d\xd4\x5e\xf2\xba\x1e\x03\x3a\xc7\x34\xbd\x52\xc2\x0c\x5d\xec\x40\x65\xfc\xd9\x87\xda\xba\xc2\x28\x5d\x32\x36\xfd\xd5\x59\x9e\x81\x03\x03\x07\x64\x9c\xf1\x53\xbc\x71\x7a\x26\x3c\x45\xb7\xbb\x0c\x83\xfe\xd0\x69\x82\x45\xc3\x03\x53\x07\xbe\x73\x7f\xe2\x6f\x00\x00\x00\xff\xff\x9b\xde\x9c\x04\x36\x08\x00\x00"), From 03153b9cf8988d787fe79d02bd4138283ea507bd Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Mon, 5 Aug 2019 06:16:30 -0400 Subject: [PATCH 150/215] Allow combining short flags globally --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++------------ app.go | 26 +++++++++------ app_test.go | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ command.go | 93 +++++++++++------------------------------------------ flag.go | 1 + parse.go | 80 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 277 insertions(+), 104 deletions(-) create mode 100644 parse.go diff --git a/README.md b/README.md index 17d0900..46e934e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ applications in an expressive way. * [Subcommands](#subcommands) * [Subcommands categories](#subcommands-categories) * [Exit code](#exit-code) + * [Combining short options](#combining-short-options) * [Bash Completion](#bash-completion) + [Enabling](#enabling) + [Distribution](#distribution) @@ -47,7 +48,6 @@ applications in an expressive way. * [Version Flag](#version-flag) + [Customization](#customization-2) + [Full API Example](#full-api-example) - * [Combining short Bool options](#combining-short-bool-options) - [Contribution Guidelines](#contribution-guidelines) @@ -921,6 +921,76 @@ func main() { } ``` +### Combining short options + +Traditional use of options using their shortnames look like this: + +``` +$ cmd -s -o -m "Some message" +``` + +Suppose you want users to be able to combine options with their shortnames. This +can be done using the `UseShortOptionHandling` bool in your app configuration, +or for individual commands by attaching it to the command configuration. For +example: + + +``` go +package main + +import ( + "fmt" + "log" + "os" + + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + app.UseShortOptionHandling = true + app.Commands = []cli.Command{ + { + Name: "short", + Usage: "complete a task on the list", + Flags: []cli.Flag{ + cli.BoolFlag{Name: "serve, s"}, + cli.BoolFlag{Name: "option, o"}, + cli.StringFlag{Name: "message, m"}, + }, + Action: func(c *cli.Context) error { + fmt.Println("serve:", c.Bool("serve")) + fmt.Println("option:", c.Bool("option")) + fmt.Println("message:", c.String("message")) + return nil + }, + }, + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} +``` + +If your program has any number of bool flags such as `serve` and `option`, and +optionally one non-bool flag `message`, with the short options of `-s`, `-o`, +and `-m` respectively, setting `UseShortOptionHandling` will also support the +following syntax: + +``` +$ cmd -som "Some message" +``` + +If you enable the `UseShortOptionHandling`, then you must not use any flags that +have a single leading `-` or this will result in failures. For example, +`-option` can no longer be used. Flags with two leading dashes (such as +`--options`) are still valid. + ### Bash Completion You can enable completion commands by setting the `EnableBashCompletion` @@ -1372,6 +1442,7 @@ func main() { cli.Uint64Flag{Name: "bigage"}, } app.EnableBashCompletion = true + app.UseShortOptionHandling = true app.HideHelp = false app.HideVersion = false app.BashComplete = func(c *cli.Context) { @@ -1502,26 +1573,6 @@ func wopAction(c *cli.Context) error { } ``` -### Combining short Bool options - -Traditional use of boolean options using their shortnames look like this: -``` -# cmd foobar -s -o -``` - -Suppose you want users to be able to combine your bool options with their shortname. This -can be done using the **UseShortOptionHandling** bool in your commands. Suppose your program -has a two bool flags such as *serve* and *option* with the short options of *-o* and -*-s* respectively. With **UseShortOptionHandling** set to *true*, a user can use a syntax -like: -``` -# cmd foobar -so -``` - -If you enable the **UseShortOptionHandling*, then you must not use any flags that have a single -leading *-* or this will result in failures. For example, **-option** can no longer be used. Flags -with two leading dashes (such as **--options**) are still valid. - ## Contribution Guidelines See [./CONTRIBUTING.md](./CONTRIBUTING.md) diff --git a/app.go b/app.go index 9ed492f..93c123d 100644 --- a/app.go +++ b/app.go @@ -1,9 +1,9 @@ package cli import ( + "flag" "fmt" "io" - "io/ioutil" "os" "path/filepath" "sort" @@ -94,6 +94,10 @@ type App struct { // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomAppHelpTemplate string + // Boolean to enable short-option handling so user can combine several + // single-character bool arguements into one + // i.e. foobar -o -v -> foobar -ov + UseShortOptionHandling bool didSetup bool } @@ -173,6 +177,14 @@ func (a *App) Setup() { } } +func (a *App) newFlagSet() (*flag.FlagSet, error) { + return flagSet(a.Name, a.Flags) +} + +func (a *App) useShortOptionHandling() bool { + return a.UseShortOptionHandling +} + // Run is the entry point to the cli app. Parses the arguments slice and routes // to the proper flag/args combination func (a *App) Run(arguments []string) (err error) { @@ -186,14 +198,12 @@ func (a *App) Run(arguments []string) (err error) { // always appends the completion flag at the end of the command shellComplete, arguments := checkShellCompleteFlag(a, arguments) - // parse flags - set, err := flagSet(a.Name, a.Flags) + _, err = a.newFlagSet() if err != nil { return err } - set.SetOutput(ioutil.Discard) - err = set.Parse(arguments[1:]) + set, err := parseIter(a, arguments[1:]) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { @@ -311,14 +321,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } a.Commands = newCmds - // parse flags - set, err := flagSet(a.Name, a.Flags) + _, err = a.newFlagSet() if err != nil { return err } - set.SetOutput(ioutil.Discard) - err = set.Parse(ctx.Args().Tail()) + set, err := parseIter(a, ctx.Args().Tail()) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, ctx) diff --git a/app_test.go b/app_test.go index 69d1418..6ebd217 100644 --- a/app_test.go +++ b/app_test.go @@ -551,6 +551,94 @@ func TestApp_VisibleCommands(t *testing.T) { } } +func TestApp_UseShortOptionHandling(t *testing.T) { + var one, two bool + var name string + expected := "expectedName" + + app := NewApp() + app.UseShortOptionHandling = true + app.Flags = []Flag{ + BoolFlag{Name: "one, o"}, + BoolFlag{Name: "two, t"}, + StringFlag{Name: "name, n"}, + } + app.Action = func(c *Context) error { + one = c.Bool("one") + two = c.Bool("two") + name = c.String("name") + return nil + } + + app.Run([]string{"", "-on", expected}) + expect(t, one, true) + expect(t, two, false) + expect(t, name, expected) +} + +func TestApp_UseShortOptionHandlingCommand(t *testing.T) { + var one, two bool + var name string + expected := "expectedName" + + app := NewApp() + app.UseShortOptionHandling = true + command := Command{ + Name: "cmd", + Flags: []Flag{ + BoolFlag{Name: "one, o"}, + BoolFlag{Name: "two, t"}, + StringFlag{Name: "name, n"}, + }, + Action: func(c *Context) error { + one = c.Bool("one") + two = c.Bool("two") + name = c.String("name") + return nil + }, + } + app.Commands = []Command{command} + + app.Run([]string{"", "cmd", "-on", expected}) + expect(t, one, true) + expect(t, two, false) + expect(t, name, expected) +} + +func TestApp_UseShortOptionHandlingSubCommand(t *testing.T) { + var one, two bool + var name string + expected := "expectedName" + + app := NewApp() + app.UseShortOptionHandling = true + command := Command{ + Name: "cmd", + } + subCommand := Command{ + Name: "sub", + Flags: []Flag{ + BoolFlag{Name: "one, o"}, + BoolFlag{Name: "two, t"}, + StringFlag{Name: "name, n"}, + }, + Action: func(c *Context) error { + one = c.Bool("one") + two = c.Bool("two") + name = c.String("name") + return nil + }, + } + command.Subcommands = []Command{subCommand} + app.Commands = []Command{command} + + err := app.Run([]string{"", "cmd", "sub", "-on", expected}) + expect(t, err, nil) + expect(t, one, true) + expect(t, two, false) + expect(t, name, expected) +} + func TestApp_Float64Flag(t *testing.T) { var meters float64 diff --git a/command.go b/command.go index f1ca02d..c74baeb 100644 --- a/command.go +++ b/command.go @@ -3,7 +3,6 @@ package cli import ( "flag" "fmt" - "io/ioutil" "sort" "strings" ) @@ -111,6 +110,10 @@ func (c Command) Run(ctx *Context) (err error) { ) } + if ctx.App.UseShortOptionHandling { + c.UseShortOptionHandling = true + } + set, err := c.parseFlags(ctx.Args().Tail()) context := NewContext(ctx.App, set, ctx) @@ -177,13 +180,12 @@ func (c Command) Run(ctx *Context) (err error) { } func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { - set, err := flagSet(c.Name, c.Flags) - if err != nil { - return nil, err - } - set.SetOutput(ioutil.Discard) - if c.SkipFlagParsing { + set, err := c.newFlagSet() + if err != nil { + return nil, err + } + return set, set.Parse(append([]string{"--"}, args...)) } @@ -191,45 +193,8 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { args = reorderArgs(args) } -PARSE: - err = set.Parse(args) + set, err := parseIter(c, args) if err != nil { - if c.UseShortOptionHandling { - // To enable short-option handling (e.g., "-it" vs "-i -t") - // we have to iteratively catch parsing errors. This way - // we achieve LR parsing without transforming any arguments. - // Otherwise, there is no way we can discriminate combined - // short options from common arguments that should be left - // untouched. - errStr := err.Error() - trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ") - if errStr == trimmed { - return nil, err - } - // regenerate the initial args with the split short opts - newArgs := Args{} - for i, arg := range args { - if arg != trimmed { - newArgs = append(newArgs, arg) - continue - } - shortOpts := translateShortOptions(set, Args{trimmed}) - if len(shortOpts) == 1 { - return nil, err - } - // add each short option and all remaining arguments - newArgs = append(newArgs, shortOpts...) - newArgs = append(newArgs, args[i+1:]...) - args = newArgs - // now reset the flagset parse again - set, err = flagSet(c.Name, c.Flags) - if err != nil { - return nil, err - } - set.SetOutput(ioutil.Discard) - goto PARSE - } - } return nil, err } @@ -241,6 +206,14 @@ PARSE: return set, nil } +func (c *Command) newFlagSet() (*flag.FlagSet, error) { + return flagSet(c.Name, c.Flags) +} + +func (c *Command) useShortOptionHandling() bool { + return c.UseShortOptionHandling +} + // reorderArgs moves all flags before arguments as this is what flag expects func reorderArgs(args []string) []string { var nonflags, flags []string @@ -271,35 +244,6 @@ func reorderArgs(args []string) []string { return append(flags, nonflags...) } -func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { - allCharsFlags := func (s string) bool { - for i := range s { - f := set.Lookup(string(s[i])) - if f == nil { - return false - } - } - return true - } - - // separate combined flags - var flagArgsSeparated []string - for _, flagArg := range flagArgs { - if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 { - if !allCharsFlags(flagArg[1:]) { - flagArgsSeparated = append(flagArgsSeparated, flagArg) - continue - } - for _, flagChar := range flagArg[1:] { - flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar)) - } - } else { - flagArgsSeparated = append(flagArgsSeparated, flagArg) - } - } - return flagArgsSeparated -} - // Names returns the names including short names and aliases. func (c Command) Names() []string { names := []string{c.Name} @@ -352,6 +296,7 @@ func (c Command) startApp(ctx *Context) error { app.Email = ctx.App.Email app.Writer = ctx.App.Writer app.ErrWriter = ctx.App.ErrWriter + app.UseShortOptionHandling = ctx.App.UseShortOptionHandling app.categories = CommandCategories{} for _, command := range c.Subcommands { diff --git a/flag.go b/flag.go index d98c808..29e6f76 100644 --- a/flag.go +++ b/flag.go @@ -105,6 +105,7 @@ func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { f.Apply(set) } } + set.SetOutput(ioutil.Discard) return set, nil } diff --git a/parse.go b/parse.go new file mode 100644 index 0000000..865accf --- /dev/null +++ b/parse.go @@ -0,0 +1,80 @@ +package cli + +import ( + "flag" + "strings" +) + +type iterativeParser interface { + newFlagSet() (*flag.FlagSet, error) + useShortOptionHandling() bool +} + +// To enable short-option handling (e.g., "-it" vs "-i -t") we have to +// iteratively catch parsing errors. This way we achieve LR parsing without +// transforming any arguments. Otherwise, there is no way we can discriminate +// combined short options from common arguments that should be left untouched. +func parseIter(ip iterativeParser, args []string) (*flag.FlagSet, error) { + for { + set, err := ip.newFlagSet() + if err != nil { + return nil, err + } + + err = set.Parse(args) + if !ip.useShortOptionHandling() || err == nil { + return set, err + } + + errStr := err.Error() + trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ") + if errStr == trimmed { + return nil, err + } + + // regenerate the initial args with the split short opts + newArgs := []string{} + for i, arg := range args { + if arg != trimmed { + newArgs = append(newArgs, arg) + continue + } + + shortOpts := splitShortOptions(set, trimmed) + if len(shortOpts) == 1 { + return nil, err + } + + // add each short option and all remaining arguments + newArgs = append(newArgs, shortOpts...) + newArgs = append(newArgs, args[i+1:]...) + args = newArgs + } + } +} + +func splitShortOptions(set *flag.FlagSet, arg string) []string { + shortFlagsExist := func(s string) bool { + for _, c := range s[1:] { + if f := set.Lookup(string(c)); f == nil { + return false + } + } + return true + } + + if !isSplittable(arg) || !shortFlagsExist(arg) { + return []string{arg} + } + + separated := make([]string, 0, len(arg)-1) + for _, flagChar := range arg[1:] { + separated = append(separated, "-"+string(flagChar)) + } + + return separated +} + +func isSplittable(flagArg string) bool { + return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2 +} From 8d31c5e167103ef4235cc5553b0fb45a2f6e8f74 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Mon, 5 Aug 2019 07:05:07 -0400 Subject: [PATCH 151/215] Update README.md Co-Authored-By: Ajitem Sahasrabuddhe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 46e934e..60e78e9 100644 --- a/README.md +++ b/README.md @@ -986,7 +986,7 @@ following syntax: $ cmd -som "Some message" ``` -If you enable the `UseShortOptionHandling`, then you must not use any flags that +If you enable `UseShortOptionHandling`, then you must not use any flags that have a single leading `-` or this will result in failures. For example, `-option` can no longer be used. Flags with two leading dashes (such as `--options`) are still valid. From c5612e8cd21e0cd99f73d23103df99a9af70f853 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 16:58:04 +0200 Subject: [PATCH 152/215] Fix review comments --- autocomplete/bash_autocomplete | 2 +- help.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index f5a8d1f..a118bda 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -7,7 +7,7 @@ _cli_bash_autocomplete() { local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - if [[ $cur == -* ]]; then + if [[ "$cur" == "-"* ]]; then opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) else opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) diff --git a/help.go b/help.go index 8e74ec4..efc3889 100644 --- a/help.go +++ b/help.go @@ -183,7 +183,7 @@ func printCommandSuggestions(commands []Command, writer io.Writer) { func cliArgContains(flagName string) bool { for _, name := range strings.Split(flagName, ",") { - name = strings.Trim(name, " ") + name = strings.TrimSpace(name) count := utf8.RuneCountInString(name) if count > 2 { count = 2 From c3f51bed6fffdf84227c5b59bd3f2e90683314df Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 17:07:46 +0200 Subject: [PATCH 153/215] Fix SC2199: Arrays implicitly concatenate in --- autocomplete/bash_autocomplete | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index a118bda..f0f6241 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -3,7 +3,7 @@ : ${PROG:=$(basename ${BASH_SOURCE})} _cli_bash_autocomplete() { - if [[ "${COMP_WORDS[@]:0:$COMP_CWORD}" != "source" ]]; then + if [[ "${COMP_WORDS[0]}" != "source" ]]; then local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" From 2be2bc755e4634d34136769a426a7ca52e698cc0 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 20:18:08 +0200 Subject: [PATCH 154/215] Add additional test for log flag completion and comments --- app_test.go | 29 +++++++++++++++++++++++++++++ help.go | 14 +++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/app_test.go b/app_test.go index 6d5ccd0..3fc27b5 100644 --- a/app_test.go +++ b/app_test.go @@ -274,6 +274,35 @@ func ExampleApp_Run_bashComplete_withLongFlag() { // --some-flag // --similar-flag } +func ExampleApp_Run_bashComplete_withMultipleLongFlag() { + os.Args = []string{"greet", "--st", "--generate-bash-completion"} + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Flags = []Flag{ + IntFlag{ + Name: "int-flag,i", + }, + StringFlag{ + Name: "string,s", + }, + StringFlag{ + Name: "string-flag-2", + }, + StringFlag{ + Name: "similar-flag", + }, + StringFlag{ + Name: "some-flag", + }, + } + + app.Run(os.Args) + // Output: + // --string + // --string-flag-2 +} func ExampleApp_Run_bashComplete() { // set args for examples sake diff --git a/help.go b/help.go index efc3889..d057a03 100644 --- a/help.go +++ b/help.go @@ -199,23 +199,27 @@ func cliArgContains(flagName string) bool { } func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { - cur := shortFlagRegex.ReplaceAllString(lastArg, "") - cur = shortFlagRegex.ReplaceAllString(cur, "") + cur := strings.TrimPrefix(lastArg, "-") + cur = strings.TrimPrefix(cur, "-") for _, flag := range flags { if bflag, ok := flag.(BoolFlag); ok && bflag.Hidden { continue } for _, name := range strings.Split(flag.GetName(), ",") { - name = strings.Trim(name, " ") + name = strings.TrimSpace(name) + // this will get total count utf8 letters in flag name count := utf8.RuneCountInString(name) if count > 2 { - count = 2 + count = 2 // resuse this count to generate single - or -- in flag completion } + // if flag name has more than one utf8 letter and last argument in cli has -- prefix then + // skip flag completion for short flags example -v or -x if strings.HasPrefix(lastArg, "--") && count == 1 { continue } - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + // match if last argument matches this flag and it is not repeated if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(flag.GetName()) { + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) fmt.Fprintln(writer, flagCompletion) } } From 1db049685ac49e11b2e27285e1287793cfe0ea84 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 20:22:52 +0200 Subject: [PATCH 155/215] Fix unused regex --- help.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/help.go b/help.go index d057a03..e504fc2 100644 --- a/help.go +++ b/help.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "os" - "regexp" "strings" "text/tabwriter" "text/template" @@ -157,8 +156,6 @@ func ShowAppHelp(c *Context) (err error) { return nil } -var shortFlagRegex = regexp.MustCompile(`^-`) - // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { DefaultCompleteWithFlags(nil)(c) From adfe6a09c121a6b96357ea161f2d679d509c013f Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Tue, 6 Aug 2019 12:14:18 +0530 Subject: [PATCH 156/215] indenting fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 20f3a58..282de14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ before_script: - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - if [ ! -f node_modules/.bin/markdown-toc ] ; then - npm install markdown-toc ; + npm install markdown-toc ; fi script: From 5070d00811cd5f2f21b0a6e021581e8efb2479e9 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Tue, 6 Aug 2019 12:33:33 +0530 Subject: [PATCH 157/215] move packages slice to global scope --- build/build.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/build.go b/build/build.go index b83a8e0..455f8d9 100644 --- a/build/build.go +++ b/build/build.go @@ -12,7 +12,7 @@ import ( "strings" ) -var packages []string +var packages = []string{"cli", "altsrc"} func main() { app := cli.NewApp() @@ -43,8 +43,6 @@ func main() { }, } - packages = []string{"cli", "altsrc"} - err := app.Run(os.Args) if err != nil { log.Fatal(err) From e8bbb4c3b5472320f0af5fb00961c76f1061edd4 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 01:46:22 +0530 Subject: [PATCH 158/215] remove unnecessary sprintf --- build/build.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.go b/build/build.go index 455f8d9..64a7bb4 100644 --- a/build/build.go +++ b/build/build.go @@ -58,7 +58,7 @@ func TestActionFunc(c *cli.Context) error { var packageName string if pkg == "cli" { - packageName = fmt.Sprintf("github.com/urfave/cli") + packageName = "github.com/urfave/cli" } else { packageName = fmt.Sprintf("github.com/urfave/cli/%s", pkg) } From 6ee5b89e03a0fc47c6351c902ef714f1475e8fde Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 01:50:50 +0530 Subject: [PATCH 159/215] move build.go to root --- build/build.go => build.go | 2 ++ 1 file changed, 2 insertions(+) rename build/build.go => build.go (99%) diff --git a/build/build.go b/build.go similarity index 99% rename from build/build.go rename to build.go index 64a7bb4..2a38f8b 100644 --- a/build/build.go +++ b/build.go @@ -1,3 +1,5 @@ +//+build ignore + package main import ( From c19938fbbfb19120beeca5d0af02291a99e61f27 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 01:51:02 +0530 Subject: [PATCH 160/215] update ci commands --- .travis.yml | 10 +++++----- appveyor.yml | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 282de14..2528ecb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,11 +22,11 @@ before_script: fi script: - - go run build/build.go generate - - go run build/build.go vet - - go run build/build.go test - - go run build/build.go gfmrun - - go run build/build.go toc + - go run build.go generate + - go run build.go vet + - go run build.go test + - go run build.go gfmrun + - go run build.go toc after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/appveyor.yml b/appveyor.yml index 6e4806b..ed74366 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ install: - go get -v -t ./... build_script: - - go run build/build.go generate - - go run build/build.go vet - - go run build/build.go test - - go run build/build.go gfmrun + - go run build.go generate + - go run build.go vet + - go run build.go test + - go run build.go gfmrun From e2de8c74587d464770155415cda09c3569224692 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 12:38:17 +0530 Subject: [PATCH 161/215] update readme with correct error message, add 1.12 and 1.11 to travis --- .travis.yml | 4 +++- README.md | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2528ecb..9154f35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ language: go sudo: false dist: trusty osx_image: xcode8.3 -go: 1.11.x +go: + - 1.11.x + - 1.12.x os: - linux diff --git a/README.md b/README.md index 17d0900..001d76f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ cli [![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) + [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) @@ -888,7 +889,9 @@ Calling `App.Run` will not automatically call `os.Exit`, which means that by default the exit code will "fall through" to being `0`. An explicit exit code may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a `cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.: - + ``` go package main From e3fa7e8566f9374ac6c1e08ace0e0555f9666e10 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Wed, 7 Aug 2019 11:06:15 +0300 Subject: [PATCH 162/215] Support also GoLang 1.11 compiler --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 31f39cc..d8513af 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/urfave/cli -go 1.12 +go 1.11 require ( github.com/BurntSushi/toml v0.3.1 From 1f4473209dcad42ed88ba68f1be7d4e906ae91be Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 13:50:04 +0530 Subject: [PATCH 163/215] Update .travis.yml support go versions in line with go's release policy --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9154f35..ce2a96b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ sudo: false dist: trusty osx_image: xcode8.3 go: + - 1.10.x - 1.11.x - 1.12.x From 3a41d6d7851b15f132131444865e82b17baf0be0 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Wed, 7 Aug 2019 11:21:31 +0300 Subject: [PATCH 164/215] Lower support to GoLang compiler version 1.10 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d8513af..de7b6e5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/urfave/cli -go 1.11 +go 1.10 require ( github.com/BurntSushi/toml v0.3.1 From 22e1fc84192059f056a7b53aa5ef2ee7113d9a83 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 14:02:52 +0530 Subject: [PATCH 165/215] Update README.md add codecov.io badge --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 001d76f..cb12505 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,7 @@ cli [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) -[![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) / -[![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc) +"[![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli)" This is the library formerly known as `github.com/codegangsta/cli` -- Github will automatically redirect requests to this repository, but we recommend From 40d4a25a01296d2f51bf7bbf0b6e8eb99ba4a84c Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Sat, 3 Aug 2019 12:41:50 +0200 Subject: [PATCH 166/215] Add markdown and man page docs generation methods This adds two new methods to the `App` struct: - `ToMarkdown`: creates a markdown documentation string - `ToMan`: creates a man page string Signed-off-by: Sascha Grunert --- app_test.go | 36 ++- docs.go | 146 ++++++++++++ docs_test.go | 115 ++++++++++ flag-gen/assets_vfsdata.go | 8 +- flag-gen/main.go | 1 + flag-gen/source/flag-types.json | 39 ++-- flag-gen/templates/cli_flags_generated.gotpl | 19 +- flag.go | 15 ++ flag_generated.go | 222 +++++++++++++++++++ go.mod | 1 + go.sum | 4 + help.go | 73 ------ template.go | 107 +++++++++ testdata/expected-doc-full.man | 80 +++++++ testdata/expected-doc-full.md | 62 ++++++ testdata/expected-doc-no-commands.md | 36 +++ testdata/expected-doc-no-flags.md | 47 ++++ 17 files changed, 908 insertions(+), 103 deletions(-) create mode 100644 docs.go create mode 100644 docs_test.go create mode 100644 template.go create mode 100644 testdata/expected-doc-full.man create mode 100644 testdata/expected-doc-full.md create mode 100644 testdata/expected-doc-no-commands.md create mode 100644 testdata/expected-doc-no-flags.md diff --git a/app_test.go b/app_test.go index 3fc27b5..f7f53b7 100644 --- a/app_test.go +++ b/app_test.go @@ -978,7 +978,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_empty_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -987,9 +987,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_empty_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1005,7 +1005,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_help_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--help"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1013,9 +1013,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_help_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--help"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1031,7 +1031,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_optional_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--optional", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, }}, @@ -1040,9 +1040,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_optional_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--optional", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, }}, @@ -1058,7 +1058,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_required_flag_input_on_command", appRunInput: []string{"myCLI", "myCommand", "--requiredFlag", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1066,9 +1066,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_required_flag_input_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--requiredFlag", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1825,6 +1825,18 @@ func (c *customBoolFlag) GetName() string { return c.Nombre } +func (c *customBoolFlag) TakesValue() bool { + return false +} + +func (c *customBoolFlag) GetValue() string { + return "value" +} + +func (c *customBoolFlag) GetUsage() string { + return "usage" +} + func (c *customBoolFlag) Apply(set *flag.FlagSet) { set.String(c.Nombre, c.Nombre, "") } diff --git a/docs.go b/docs.go new file mode 100644 index 0000000..ef5699a --- /dev/null +++ b/docs.go @@ -0,0 +1,146 @@ +package cli + +import ( + "bytes" + "fmt" + "io" + "sort" + "strings" + "text/template" + + "github.com/cpuguy83/go-md2man/md2man" +) + +// ToMarkdown creates a markdown string for the `*App` +// The function errors if either parsing or writing of the string fails. +func (a *App) ToMarkdown() (string, error) { + var w bytes.Buffer + if err := a.writeDocTemplate(&w); err != nil { + return "", err + } + return w.String(), nil +} + +// ToMan creates a man page string for the `*App` +// The function errors if either parsing or writing of the string fails. +func (a *App) ToMan() (string, error) { + var w bytes.Buffer + if err := a.writeDocTemplate(&w); err != nil { + return "", err + } + man := md2man.Render(w.Bytes()) + return string(man), nil +} + +type cliTemplate struct { + App *App + Date string + Commands []string + GlobalArgs []string + SynopsisArgs []string +} + +func (a *App) writeDocTemplate(w io.Writer) error { + const name = "cli" + t, err := template.New(name).Parse(MarkdownDocTemplate) + if err != nil { + return err + } + return t.ExecuteTemplate(w, name, &cliTemplate{ + App: a, + Commands: prepareCommands(a.Commands, 0), + GlobalArgs: prepareArgsWithValues(a.Flags), + SynopsisArgs: prepareArgsSynopsis(a.Flags), + }) +} + +func prepareCommands(commands []Command, level int) []string { + coms := []string{} + for i := range commands { + command := &commands[i] + usage := "" + if command.Usage != "" { + usage = command.Usage + } + + prepared := fmt.Sprintf("%s %s\n\n%s\n", + strings.Repeat("#", level+2), + strings.Join(command.Names(), ", "), + usage, + ) + + flags := prepareArgsWithValues(command.Flags) + if len(flags) > 0 { + prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n")) + } + + coms = append(coms, prepared) + + // recursevly iterate subcommands + if len(command.Subcommands) > 0 { + coms = append( + coms, + prepareCommands(command.Subcommands, level+1)..., + ) + } + } + + return coms +} + +func prepareArgsWithValues(flags []Flag) []string { + return prepareFlags(flags, ", ", "**", "**", `""`, true) +} + +func prepareArgsSynopsis(flags []Flag) []string { + return prepareFlags(flags, "|", "[", "]", "[value]", false) +} + +func prepareFlags( + flags []Flag, + sep, opener, closer, value string, + addDetails bool, +) []string { + args := []string{} + for _, f := range flags { + flag, ok := f.(DocGenerationFlag) + if !ok { + continue + } + modifiedArg := opener + for _, s := range strings.Split(flag.GetName(), ",") { + trimmed := strings.TrimSpace(s) + if len(modifiedArg) > len(opener) { + modifiedArg += sep + } + if len(trimmed) > 1 { + modifiedArg += fmt.Sprintf("--%s", trimmed) + } else { + modifiedArg += fmt.Sprintf("-%s", trimmed) + } + } + modifiedArg += closer + if flag.TakesValue() { + modifiedArg += fmt.Sprintf("=%s", value) + } + + if addDetails { + modifiedArg += flagDetails(flag) + } + + args = append(args, modifiedArg+"\n") + + } + sort.Strings(args) + return args +} + +// flagDetails returns a string containing the flags metadata +func flagDetails(flag DocGenerationFlag) string { + description := flag.GetUsage() + value := flag.GetValue() + if value != "" { + description += " (default: " + value + ")" + } + return ": " + description +} diff --git a/docs_test.go b/docs_test.go new file mode 100644 index 0000000..177eb19 --- /dev/null +++ b/docs_test.go @@ -0,0 +1,115 @@ +package cli + +import ( + "io/ioutil" + "testing" +) + +func testApp() *App { + app := NewApp() + app.Name = "greet" + app.Flags = []Flag{ + StringFlag{ + Name: "socket, s", + Usage: "some usage text", + Value: "value", + }, + StringFlag{Name: "flag, fl, f"}, + BoolFlag{ + Name: "another-flag, b", + Usage: "another usage text", + }, + } + app.Commands = []Command{{ + Aliases: []string{"c"}, + Flags: []Flag{ + StringFlag{Name: "flag, fl, f"}, + BoolFlag{ + Name: "another-flag, b", + Usage: "another usage text", + }, + }, + Name: "config", + Usage: "another usage test", + Subcommands: []Command{{ + Aliases: []string{"s", "ss"}, + Flags: []Flag{ + StringFlag{Name: "sub-flag, sub-fl, s"}, + BoolFlag{ + Name: "sub-command-flag, s", + Usage: "some usage text", + }, + }, + Name: "sub-config", + Usage: "another usage test", + }}, + }, { + Aliases: []string{"i", "in"}, + Name: "info", + Usage: "retrieve generic information", + }, { + Name: "some-command", + }} + app.UsageText = "app [first_arg] [second_arg]" + app.Usage = "Some app" + app.Author = "Harrison" + app.Email = "harrison@lolwut.com" + app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} + return app +} + +func expectFileContent(t *testing.T, file, expected string) { + data, err := ioutil.ReadFile(file) + expect(t, err, nil) + expect(t, string(data), expected) +} + +func TestToMarkdownFull(t *testing.T) { + // Given + app := testApp() + + // When + res, err := app.ToMarkdown() + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-doc-full.md", res) +} + +func TestToMarkdownNoFlags(t *testing.T) { + // Given + app := testApp() + app.Flags = nil + + // When + res, err := app.ToMarkdown() + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-doc-no-flags.md", res) +} + +func TestToMarkdownNoCommands(t *testing.T) { + // Given + app := testApp() + app.Commands = nil + + // When + res, err := app.ToMarkdown() + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-doc-no-commands.md", res) +} + +func TestToMan(t *testing.T) { + // Given + app := testApp() + + // When + res, err := app.ToMan() + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-doc-full.man", res) +} diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index 024039a..2e31627 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -28,9 +28,9 @@ var assets = func() http.FileSystem { "/source/flag-types.json": &vfsgenÛ°CompressedFileInfo{ name: "flag-types.json", modTime: time.Time{}, - uncompressedSize: 2559, + uncompressedSize: 3364, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x94\xc1\x6e\x9c\x30\x10\x86\xef\x3c\xc5\xc8\xbd\x40\xb5\x82\x1c\x56\x7b\xd8\x63\x55\xb5\xda\x5b\xa5\x34\xbd\x24\x51\xe4\x80\x21\x56\x1d\x1b\xd9\x43\xd4\x28\xca\xbb\x57\xf6\x2e\xbb\x60\x0c\x62\x5b\x2d\xb9\xfe\x23\x98\xef\x9b\xb1\xe6\x36\x02\x78\x8b\x00\x00\x88\xa4\xcf\x8c\x6c\x81\x7c\x51\x4a\x90\xd5\x3e\xc3\xd7\xda\x65\x8f\x9d\xec\x85\x8a\xc6\x86\x25\x15\x86\x1d\xb2\x82\x19\x24\x5b\x40\xdd\xb4\x49\xae\x24\xb2\x3f\xf8\x50\xb0\x92\x36\xc2\x16\x89\xfb\xa0\xfd\x4b\x4d\xb5\x61\xda\xc6\x06\x75\xae\xe4\x4b\xfa\xc3\x26\xb6\x79\x5c\xa6\xbf\x6c\x8f\xf4\x1a\x35\x97\x55\x9c\x24\x24\x02\x78\x5f\x85\x51\x7f\xfe\x3f\x6b\xa1\x72\xa4\x5c\xd8\x8f\x01\x9f\x28\x02\x37\xae\x0c\x8f\xaf\xd0\xf2\x2f\xa9\xf5\xb5\xd1\x14\xb9\x92\xbe\x19\xf2\x67\x96\xfa\xc5\x56\xb1\xeb\x33\x69\x18\x1b\xc6\xe0\x09\xb1\x36\xdb\x2c\xab\x94\xa0\xb2\x4a\x95\xae\xb2\xfa\x77\x95\xd9\x0e\xd9\x27\x87\xdc\xf6\x49\x26\xd4\xaf\x86\xda\x8e\xb1\xf7\x83\xf9\xde\xdf\x84\xa2\xb8\x59\xfb\xda\x65\x3f\x9e\x25\x3c\x0f\xb6\xb7\x23\xd7\x7d\x00\xbb\x82\xcd\x7a\x14\xf8\x3b\x93\x4c\xf3\xdc\x07\xf6\xe2\x71\xe0\xee\xb3\x0c\x10\x4b\x2e\xfc\xe1\xb7\x2d\xb8\x44\xa6\x4b\x9a\xb3\xb7\xf7\x31\xb8\x9d\x0c\xcc\x92\xcb\x05\x26\xb9\x93\xa1\x39\x5e\x4d\x8e\x72\x27\x31\xc0\xfa\xa1\xa4\xbd\x4f\x1f\x72\xea\xfa\x59\xaa\xd8\x45\xc5\x94\xcb\xb5\xe0\x39\xf3\x85\x3e\xfb\x85\x4b\xbc\x8c\xdb\xfb\xce\xdc\x4e\xda\x47\xcd\xf8\x48\x91\x24\xfb\xc8\x2a\x33\xad\x95\x8e\x25\x17\x53\x52\x9b\xf5\xa8\x96\x57\xba\x98\xd8\xe9\xf1\x8e\xa9\x1d\x48\xce\x93\xdb\xef\xde\x17\x33\xbd\xf4\x5f\xdf\xe0\x1d\xb9\x23\x43\xe8\xe1\xab\x9b\x4b\x19\xde\x41\xa0\x76\x99\x25\xf4\x87\x12\xdc\x42\x87\xe5\xbc\x35\xdc\xf0\xd0\xcd\x6a\x16\x39\x5a\xb6\xf7\xd9\x57\xeb\x86\x0f\xcf\x56\xb3\xc0\xdd\x9a\x86\x0d\x1f\xae\xc6\xbf\x5c\xd1\x7d\xf4\x37\x00\x00\xff\xff\x66\x52\x85\x44\xff\x09\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x95\x41\x6f\x9b\x40\x10\x85\xef\xfc\x8a\xe9\x56\x95\xa0\xb2\x20\x87\x28\x07\x4b\xbd\x54\x55\x2b\xdf\x2a\xb9\xe9\x25\x44\xd1\x06\x16\xb2\xea\x7a\x16\x2d\x43\xd4\xc8\xf2\x7f\xaf\x16\x9b\x04\x16\x70\x48\x5b\x2c\x5f\x7c\x78\xe3\xe5\xed\xfb\x66\x47\x73\xe3\x01\x6c\x3d\x00\x00\x86\x7c\x23\xd8\x12\xd8\x67\xad\x15\x5b\xec\x35\x7a\x2a\x6a\xed\xbe\xa5\x3d\x72\x55\x59\x31\xe3\xaa\x14\x07\x2d\x15\x25\xb1\x25\x90\xa9\x1a\x25\xd1\x48\xe2\x37\xdd\xa5\x22\xe3\x95\xb2\x45\x56\x1f\x68\xbe\x52\x70\x53\x0a\x63\xe5\x92\x4c\xa2\xf1\x31\xfc\x6e\x15\x6b\xee\x67\xe1\x4f\xeb\x11\xae\xc9\x48\xcc\xfd\x20\xe8\x58\xef\x55\x7b\xd2\x08\xaa\x0c\x42\xcc\x62\xc6\x3c\x80\xdd\x62\x38\xcc\x8f\x7f\x4f\x93\xea\x84\xb8\x54\xf6\x30\xd0\x03\x27\x90\x65\x5d\x86\xfb\x27\x68\x12\x9e\x57\xf0\x2f\x95\xe1\x24\x35\xba\xd9\x49\x6e\x44\xe8\x16\x1b\x08\xed\xc4\x47\x19\xf8\xa5\x10\xf0\x40\x54\x94\xcb\x28\xca\xb5\xe2\x98\x87\xda\xe4\x51\xf1\x2b\x8f\xac\x43\xf4\xbe\x0e\xd5\xf8\x04\x47\xe0\x5c\xf4\xc1\xd4\x77\xec\x7c\xe0\xad\x64\xdc\xbf\x8f\x51\xfa\xaa\x34\xa7\xab\x4b\x17\x52\xd6\x95\x27\xe1\x99\x16\xad\xd3\xf3\xda\xbd\x17\x6d\x01\x57\x97\xaf\xc5\xdb\x50\xb8\x2e\x8c\x44\xca\xfc\x98\x7d\xc8\x62\xb6\x68\x22\x8f\x46\xfd\x26\x50\x18\x99\xb8\x51\x1d\x79\x3c\x6a\x7b\x40\x06\xb2\xa2\x54\x6e\x93\x1b\x0b\x89\x24\x4c\xc6\x13\xb1\xdd\x8d\xc4\x92\x59\x73\x7d\x78\xf7\x09\x50\x2a\xd8\xc6\x18\x53\x4c\x23\xed\xb4\xc5\x9d\xfd\x99\x30\x08\x2b\x1c\x68\xb0\xc4\x13\xb4\x77\x85\x43\xcd\xbd\x78\x7b\x7f\xd3\x29\xfd\x5d\x21\x0d\xa4\x3c\x87\x8c\xfb\xa3\x77\x09\xaf\xfd\xec\xad\xfc\x5a\x4a\xe7\xa1\xb0\x56\x32\x11\x2e\x8a\x8f\x6e\x61\x8e\x87\x7e\x73\xdb\x22\xfe\x02\xec\x19\x90\xff\x7c\x8b\x20\xd8\x4b\x16\x96\x30\x46\x1b\x1f\xa5\x1a\xc3\x31\xfb\x74\x8c\x12\x73\x4a\xb3\x31\x7b\x99\xc5\x31\x6a\x87\x9b\x9c\x0d\xb7\x83\x8b\xc3\xac\xec\xa8\x7f\x3b\x73\xb5\x6f\x8f\x47\x7f\xca\x5e\x05\xd0\xcd\x79\x3c\xca\xf0\x1b\x18\xa8\xcd\xf3\x08\xba\xe4\x06\x5f\x41\xeb\x2e\x67\xf3\x0c\xae\xe5\xd0\x76\xa9\x4e\xb2\x5e\xac\xf7\x09\xf7\xcb\xb5\xec\x2f\x98\xea\x04\x1b\x66\x52\x4c\x67\xc5\x54\xff\x6f\xc7\x78\xb7\xde\x9f\x00\x00\x00\xff\xff\x61\x79\x63\x47\x24\x0d\x00\x00"), }, "/templates": &vfsgenÛ°DirInfo{ name: "templates", @@ -46,9 +46,9 @@ var assets = func() http.FileSystem { "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", modTime: time.Time{}, - uncompressedSize: 2102, + uncompressedSize: 2575, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x54\xc1\x52\xdb\x30\x10\x3d\xdb\x5f\xb1\xcd\x70\xb0\x99\x34\xb9\x97\xe1\x44\xa0\x65\xa6\x03\x4c\xa1\xdc\x15\x7b\xe5\x68\x10\x52\x90\xd6\x50\x26\x93\x7f\xef\xac\x24\x3b\x4e\x48\x21\x17\x7a\xd3\x6a\x9f\xde\x7b\x7a\x5a\x7b\x3a\x85\x33\x5b\x23\x34\x68\xd0\x09\xc2\x1a\xe6\xaf\x20\x9b\x13\x98\x5d\xc3\xd5\xf5\x1d\x9c\xcf\x2e\xef\x26\x79\xbe\x14\xd5\x83\x68\x10\x56\x2b\x98\xdc\xc4\xf5\x95\x78\x44\x58\xaf\xf3\x5c\x3d\x2e\xad\x23\x28\xf2\x6c\x24\xb5\x68\x46\x79\x36\xf2\xe4\x2a\x6b\x9e\x79\x49\xea\x11\x47\x79\x99\xaf\x56\xe0\x84\x69\x10\x8e\xd4\x18\x8e\x18\x08\xdf\x4e\x61\x72\xa1\x45\xe3\x99\x66\x3a\x65\xf2\xd0\x98\x24\x6a\xee\x81\xf2\x20\x20\xc0\x5f\x14\x2d\x80\x5e\x97\xb8\x01\xde\x71\xb5\x5e\xf7\xf5\xcc\x56\x24\x94\x66\xbe\x6d\xe0\x90\xd1\x93\x6b\x2b\x82\x55\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\xdb\x8b\x66\x50\x9e\x9b\xe7\x7b\xe1\x36\xf5\x85\xd2\x78\x23\x68\xd1\x6f\xfc\xc2\xa7\x56\x39\xac\xb3\xb9\xb5\x3a\xcf\x7e\xa8\xba\x46\x93\xa5\x6a\xb5\xfa\x0a\x4a\x02\x3e\x25\x03\xf7\x42\xb7\x08\xe4\xda\x90\x59\x16\xca\x6c\xf7\x1e\xf1\x18\x9a\xba\x5f\x0f\x29\x66\xe8\x49\x19\x41\xca\x9a\x0d\xd1\x60\x33\x3b\x7e\x97\x6f\x9d\x73\xc6\xb7\xc1\x3c\x38\xa4\xd6\x19\x8e\xd6\xa1\xa8\xc5\x5c\x23\x38\x5c\x3a\xf4\x68\x28\x2a\x58\x09\xb4\x50\x1e\x9e\xd9\x29\x9f\x2c\xa4\x75\xd0\x72\x46\x50\xa3\x14\xad\x26\x5f\xe6\xb2\x35\x15\x14\x72\x6f\xd0\x65\x12\x2b\x4a\x88\x91\x71\xe4\x51\x18\xb8\x1f\xbb\xe8\x0a\x59\x26\x73\xdf\x91\xc2\xf9\xce\x1d\x2d\x10\x0c\x6f\x04\x33\x18\xc6\xe0\x03\xc9\x44\xb1\x4f\x53\x06\x64\x92\xba\xf4\xdd\xf3\xf5\x6a\x2f\x0b\xa4\x05\x3a\xb0\x0e\x8c\xa5\x5e\x90\x27\xd0\x25\xec\x07\xe2\x1b\xd2\xa2\x04\x9e\x83\x2d\xf5\xae\x97\x1c\xec\x52\x80\xb6\xf6\xc1\x43\xbb\x0c\xca\x21\x76\xbe\xb7\x00\x6d\x2b\xa1\xf7\x2a\x8e\x3b\xef\x5b\x7c\x67\xd6\x10\xfe\xa1\x59\x7c\x24\x66\x56\x32\xdc\x48\xda\xd6\x74\x57\xa8\xe0\x38\xe1\xca\x37\xd4\x45\xc8\x3c\xe6\x17\xba\x7c\x1e\xa1\x23\x0e\xc3\x35\x1a\x0d\xbf\xbb\x61\x27\x6c\xa3\xf6\x5b\x1f\x66\xdf\xe9\xc6\x71\x90\x0c\xdf\xbb\x5d\xee\x35\x31\x86\x6a\xc2\x9b\xb7\x48\xfd\x8c\x68\x3b\x17\xfa\xe0\xf0\x9a\x00\xff\xcc\xf4\xf6\x1b\xfa\x5f\x19\x2a\x09\xd2\xf3\x9f\x34\xa6\x18\xcd\x5c\xc4\xc8\xba\x08\xcb\x13\xc6\x7c\x39\x05\xa3\xc2\x44\x1e\x14\xbc\xf4\x65\x9e\xad\xfb\x47\xfa\x77\x3e\xfc\x2e\x21\x95\x77\xe8\x52\x10\x63\xf0\x48\x70\x1c\xda\xc9\xe3\x67\x66\x23\x39\x17\x8f\x34\xf9\x19\x9c\x05\x27\x65\x8c\x6c\x98\xc6\xc6\xc0\x8d\x70\x1e\x5d\xd4\x5e\xf2\xba\x1e\x03\x3a\xc7\x34\xbd\x52\xc2\x0c\x5d\xec\x40\x65\xfc\xd9\x87\xda\xba\xc2\x28\x5d\x32\x36\xfd\xd5\x59\x9e\x81\x03\x03\x07\x64\x9c\xf1\x53\xbc\x71\x7a\x26\x3c\x45\xb7\xbb\x0c\x83\xfe\xd0\x69\x82\x45\xc3\x03\x53\x07\xbe\x73\x7f\xe2\x6f\x00\x00\x00\xff\xff\x9b\xde\x9c\x04\x36\x08\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x8f\xdb\x36\x10\x3d\x4b\xbf\x62\x6a\xe4\x20\x2d\x5c\xfb\xde\x20\xa7\x75\x36\x0d\x50\x24\x8b\xc6\xcd\x9d\x96\x86\x32\xb1\x34\xa9\x90\xa3\xdd\x2e\x0c\xff\xf7\x62\x48\xea\xc3\x1f\x58\xfb\xb2\x3d\x89\xe4\x0c\xdf\x7b\x7c\x33\xa4\x96\x4b\xb8\xb7\x35\x42\x83\x06\x9d\x20\xac\x61\xf3\x0a\xb2\xf9\x08\xab\xef\xf0\xed\xfb\x1a\x3e\xaf\xbe\xae\x17\x79\xde\x8a\xea\x49\x34\x08\xfb\x3d\x2c\x1e\xe3\xf8\x9b\xd8\x21\x1c\x0e\x79\xae\x76\xad\x75\x04\x45\x9e\xcd\xa4\x16\xcd\x8c\xbf\x3b\xe2\x8f\x27\x57\x59\xf3\xcc\x43\x52\x3b\x9c\xe5\x65\xbe\xdf\x83\x13\xa6\x41\xf8\xa0\xe6\xf0\x81\xf3\xe1\x8f\x4f\xb0\x78\xd0\xa2\xf1\x8c\xb6\x5c\x32\x47\x08\x2c\x12\x03\xc7\x40\x79\x10\x10\xd2\x5f\x14\x6d\x81\x5e\x5b\x1c\x13\xd7\x3c\x3b\x1c\x86\xf9\xca\x56\x24\x94\x66\xbc\xe3\xc4\x29\xa2\x27\xd7\x55\x04\xfb\x3c\xe3\xd5\x2c\xf3\xe4\x94\x69\xf2\xec\x1f\x2f\x9a\xc9\xf4\xb3\x79\xfe\x29\xdc\x38\x7f\x50\x1a\x1f\x05\x6d\x87\x85\xbf\xf1\x57\xa7\x1c\xd6\xd9\xc6\x5a\x9d\x67\x7f\xaa\xba\x46\x93\xa5\xd9\x7e\xff\x3b\x28\x09\xf8\x2b\x09\xf8\x29\x74\x87\x40\xae\x0b\xd6\x65\x61\x9a\x9d\x9e\x23\x6e\x43\x53\x0f\xe3\x29\xc4\x0a\x3d\x29\x23\x48\x59\x33\x02\x4d\x16\xb3\xbb\x37\xf1\x0e\x39\x7b\xfc\x23\x88\x07\x87\xd4\x39\xc3\xd6\x3a\x14\xb5\xd8\x68\x04\x87\xad\x43\x8f\x86\x22\x83\x95\x40\x5b\xe5\xe1\x99\x95\xf2\xce\x42\x5a\x07\x1d\x7b\x04\x35\x4a\xd1\x69\xf2\x65\x2e\x3b\x53\x41\x21\x2f\x1a\x5d\x26\xb2\xa2\x84\x68\x19\x5b\x1e\x89\x81\xe3\x31\x8a\xae\x90\x65\x12\xf7\x05\x29\xec\xef\xd5\xd1\x16\xc1\xf0\x42\x10\x83\xa1\x0d\xae\x50\x26\x88\x4b\x9c\x32\x64\x26\xaa\xaf\xbe\x2f\xdf\xc0\xf6\xb2\x45\xda\xa2\x03\xeb\xc0\x58\x1a\x08\xb9\x03\x5d\xca\xbd\x42\x3e\x82\x16\x25\x70\x1f\x1c\xb1\xf7\xb1\xa4\x60\x2d\x9e\xd0\xc7\xb6\x18\xce\xcb\x55\x9d\x9c\x15\x88\x73\x40\xc4\x22\xcc\xc1\xb2\xbe\x17\xe5\x11\xa4\xd0\x1e\xaf\xa8\x19\x09\xce\xd5\x0c\x3b\xa2\x80\xa1\x3d\xbe\x20\x85\x6b\x70\x54\x82\x58\xf4\xe4\x27\x77\xc1\xed\xb5\x08\x60\x97\x8b\x11\x42\x23\xed\x89\x13\x89\x21\xf5\x1f\x08\xdf\x43\x9c\xb4\xa9\x30\x35\x08\x03\xb8\x6b\xe9\x95\x91\x52\x96\x3a\x33\xd1\xd8\x1e\x8a\x40\x68\xbd\xb8\xae\xbd\xb7\x6e\xd4\x7e\xec\x5a\xba\x4a\x83\x77\xa7\x40\xa0\xad\x7d\xf2\xd0\xb5\x41\x4a\x24\xb7\x12\x04\x68\x5b\x09\x7d\x91\x77\xde\x1b\x70\x84\x77\x6f\x0d\xe1\xbf\xb4\x8a\xb7\x8e\x91\x95\x0c\x2d\x2a\x6d\x67\xfa\x9e\xac\xe0\x2e\xe5\x95\x67\xd0\x45\xb8\x44\xf1\x1c\x21\xca\xfb\x11\x7a\xe0\xf0\x5a\xcc\x66\xd3\x87\x74\x1a\x09\xcb\xa8\xfd\xd1\x4b\x3b\x44\xfa\xf7\x65\x52\x5b\x3e\x77\xd7\x5e\x14\x31\x87\x6a\xc1\x8b\x3f\x90\x86\x4b\xaf\xed\x46\xe8\x9b\xcd\x6b\x42\xfa\x7b\xba\x77\x59\xd0\xff\xe5\xa1\x92\x20\x3d\xff\x1a\xa3\x8b\x51\xcc\x43\xb4\xac\xb7\xb0\xfc\xc8\x39\xbf\x7d\x02\xa3\xc2\xa5\xbe\xc9\x78\xe9\xcb\x3c\x3b\x9c\xbf\x00\x67\xfe\x70\x5d\x82\x2b\x6f\xc0\x25\x23\xe6\xe0\x91\xe0\x2e\x84\x93\xc6\xf7\xf4\x46\xb2\x2f\x1e\x69\xf1\x57\x50\x16\x94\x94\xd1\xb2\xa9\x1b\xa3\x80\x47\xe1\x3c\xba\xc8\xdd\xf2\xb8\x9e\x03\x3a\xc7\x30\x03\x53\xca\x99\xaa\x38\x49\x95\xf1\xbe\x87\xb9\x75\x85\x51\xba\xe4\xdc\xf4\x9b\x66\x7a\x4e\x9c\x08\xb8\xc1\xe3\x8c\x4b\x71\xa6\xf4\x5e\x78\x8a\x6a\x4f\x11\x26\xf1\xa9\xd2\x94\x16\x05\x4f\x44\xdd\x58\xe7\x71\xc7\x7f\x01\x00\x00\xff\xff\xa4\x4d\x6e\xce\x0f\x0a\x00\x00"), }, } fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ diff --git a/flag-gen/main.go b/flag-gen/main.go index 9caa94e..579a7dd 100644 --- a/flag-gen/main.go +++ b/flag-gen/main.go @@ -27,6 +27,7 @@ type FlagType struct { ContextType string `json:"context_type"` Parser string `json:"parser"` ParserCast string `json:"parser_cast"` + ValueString string `json:"valueString"` } func main() { diff --git a/flag-gen/source/flag-types.json b/flag-gen/source/flag-types.json index 3306025..7ec5ddd 100644 --- a/flag-gen/source/flag-types.json +++ b/flag-gen/source/flag-types.json @@ -5,7 +5,8 @@ "value": false, "dest": true, "context_default": "false", - "parser": "strconv.ParseBool(f.Value.String())" + "parser": "strconv.ParseBool(f.Value.String())", + "valueString": "return \"\"" }, { "name": "BoolT", @@ -14,7 +15,8 @@ "dest": true, "doctail": " that is true by default", "context_default": "false", - "parser": "strconv.ParseBool(f.Value.String())" + "parser": "strconv.ParseBool(f.Value.String())", + "valueString": "return \"\"" }, { "name": "Duration", @@ -23,7 +25,8 @@ "dest": true, "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", "context_default": "0", - "parser": "time.ParseDuration(f.Value.String())" + "parser": "time.ParseDuration(f.Value.String())", + "valueString": "return f.Value.String()" }, { "name": "Float64", @@ -31,7 +34,8 @@ "value": true, "dest": true, "context_default": "0", - "parser": "strconv.ParseFloat(f.Value.String(), 64)" + "parser": "strconv.ParseFloat(f.Value.String(), 64)", + "valueString": "return fmt.Sprintf(\"%f\", f.Value)" }, { "name": "Generic", @@ -39,7 +43,8 @@ "value": true, "dest": false, "context_default": "nil", - "context_type": "interface{}" + "context_type": "interface{}", + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" }, { "name": "Int64", @@ -47,7 +52,8 @@ "value": true, "dest": true, "context_default": "0", - "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" + "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", + "valueString": "return fmt.Sprintf(\"%d\", f.Value)" }, { "name": "Int", @@ -56,7 +62,8 @@ "dest": true, "context_default": "0", "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", - "parser_cast": "int(parsed)" + "parser_cast": "int(parsed)", + "valueString": "return fmt.Sprintf(\"%d\", f.Value)" }, { "name": "IntSlice", @@ -65,7 +72,8 @@ "dest": false, "context_default": "nil", "context_type": "[]int", - "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" + "parser": "(f.Value.(*IntSlice)).Value(), error(nil)", + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" }, { "name": "Int64Slice", @@ -74,7 +82,8 @@ "dest": false, "context_default": "nil", "context_type": "[]int64", - "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" + "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)", + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" }, { "name": "String", @@ -82,7 +91,8 @@ "value": true, "dest": true, "context_default": "\"\"", - "parser": "f.Value.String(), error(nil)" + "parser": "f.Value.String(), error(nil)", + "valueString": "return f.Value" }, { "name": "StringSlice", @@ -91,7 +101,8 @@ "dest": false, "context_default": "nil", "context_type": "[]string", - "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" + "parser": "(f.Value.(*StringSlice)).Value(), error(nil)", + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" }, { "name": "Uint64", @@ -99,7 +110,8 @@ "value": true, "dest": true, "context_default": "0", - "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" + "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", + "valueString": "return fmt.Sprintf(\"%d\", f.Value)" }, { "name": "Uint", @@ -108,6 +120,7 @@ "dest": true, "context_default": "0", "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", - "parser_cast": "uint(parsed)" + "parser_cast": "uint(parsed)", + "valueString": "return fmt.Sprintf(\"%d\", f.Value)" } ] diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index 97bd49f..ffd18ae 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -4,6 +4,7 @@ package {{ .PackageName }} import ( "flag" + "fmt" "strconv" "time" ) @@ -40,6 +41,22 @@ func (f {{ $flag.Name }}Flag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f {{ $flag.Name }}Flag) TakesValue() bool { + return {{ $flag.Value }} +} + +// GetUsage returns the usage string for the flag +func (f {{ $flag.Name }}Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f {{ $flag.Name }}Flag) GetValue() string { + {{ $flag.ValueString }} +} + // {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns // {{ $flag.ContextDefault }} if not found func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { @@ -66,4 +83,4 @@ func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextTyp } return {{ $flag.ContextDefault }} } -{{ end }} \ No newline at end of file +{{ end }} diff --git a/flag.go b/flag.go index d98c808..5b6db0c 100644 --- a/flag.go +++ b/flag.go @@ -83,6 +83,21 @@ type RequiredFlag interface { IsRequired() bool } +// DocGenerationFlag is an interface that allows documentation generation for the flag +type DocGenerationFlag interface { + Flag + + // TakesValue returns true of the flag takes a value, otherwise false + TakesValue() bool + + // GetUsage returns the usage string for the flag + GetUsage() string + + // GetValue returns the flags value as string representation and an empty + // string if the flag takes no value at all. + GetValue() string +} + // 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 diff --git a/flag_generated.go b/flag_generated.go index 83e10d7..af68f95 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -4,6 +4,7 @@ package cli import ( "flag" + "fmt" "strconv" "time" ) @@ -35,6 +36,22 @@ func (f BoolFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f BoolFlag) TakesValue() bool { + return false +} + +// GetUsage returns the usage string for the flag +func (f BoolFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolFlag) GetValue() string { + return "" +} + // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { @@ -89,6 +106,22 @@ func (f BoolTFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f BoolTFlag) TakesValue() bool { + return false +} + +// GetUsage returns the usage string for the flag +func (f BoolTFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolTFlag) GetValue() string { + return "" +} + // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { @@ -144,6 +177,22 @@ func (f DurationFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f DurationFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f DurationFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f DurationFlag) GetValue() string { + return f.Value.String() +} + // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { @@ -199,6 +248,22 @@ func (f Float64Flag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f Float64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Float64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Float64Flag) GetValue() string { + return fmt.Sprintf("%f", f.Value) +} + // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { @@ -253,6 +318,25 @@ func (f GenericFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f GenericFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f GenericFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f GenericFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { @@ -308,6 +392,22 @@ func (f Int64Flag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f Int64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Int64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64Flag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { @@ -363,6 +463,22 @@ func (f IntFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f IntFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f IntFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntFlag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { @@ -417,6 +533,25 @@ func (f IntSliceFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f IntSliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f IntSliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntSliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { @@ -471,6 +606,25 @@ func (f Int64SliceFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f Int64SliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Int64SliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64SliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { @@ -526,6 +680,22 @@ func (f StringFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f StringFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f StringFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringFlag) GetValue() string { + return f.Value +} + // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { @@ -580,6 +750,25 @@ func (f StringSliceFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f StringSliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f StringSliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringSliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { @@ -635,6 +824,22 @@ func (f Uint64Flag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f Uint64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Uint64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Uint64Flag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { @@ -690,6 +895,22 @@ func (f UintFlag) IsRequired() bool { return f.Required } +// TakesValue returns true of the flag takes a value, otherwise false +func (f UintFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f UintFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f UintFlag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { @@ -716,3 +937,4 @@ func lookupUint(name string, set *flag.FlagSet) uint { } return 0 } + diff --git a/go.mod b/go.mod index de7b6e5..91106bc 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.10 require ( github.com/BurntSushi/toml v0.3.1 + github.com/cpuguy83/go-md2man v1.0.10 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd // indirect gopkg.in/yaml.v2 v2.2.2 diff --git a/go.sum b/go.sum index f3cb3d0..2b8b863 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= diff --git a/help.go b/help.go index e504fc2..33985d3 100644 --- a/help.go +++ b/help.go @@ -10,79 +10,6 @@ import ( "unicode/utf8" ) -// AppHelpTemplate is the text template for the Default help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var AppHelpTemplate = `NAME: - {{.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}} - -VERSION: - {{.Version}}{{end}}{{end}}{{if .Description}} - -DESCRIPTION: - {{.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}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} - -GLOBAL OPTIONS: - {{range $index, $option := .VisibleFlags}}{{if $index}} - {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} - -COPYRIGHT: - {{.Copyright}}{{end}} -` - -// CommandHelpTemplate is 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. -var CommandHelpTemplate = `NAME: - {{.HelpName}} - {{.Usage}} - -USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} - -CATEGORY: - {{.Category}}{{end}}{{if .Description}} - -DESCRIPTION: - {{.Description}}{{end}}{{if .VisibleFlags}} - -OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}} -` - -// SubcommandHelpTemplate is the text template for the subcommand help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var SubcommandHelpTemplate = `NAME: - {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} - -USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} - -COMMANDS:{{range .VisibleCategories}}{{if .Name}} - - {{.Name}}:{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} - {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} - -OPTIONS: - {{range .VisibleFlags}}{{.}} - {{end}}{{end}} -` - var helpCommand = Command{ Name: "help", Aliases: []string{"h"}, diff --git a/template.go b/template.go new file mode 100644 index 0000000..24c44c8 --- /dev/null +++ b/template.go @@ -0,0 +1,107 @@ +package cli + +// AppHelpTemplate is the text template for the Default help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var AppHelpTemplate = `NAME: + {{.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}} + +VERSION: + {{.Version}}{{end}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.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}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + +GLOBAL OPTIONS: + {{range $index, $option := .VisibleFlags}}{{if $index}} + {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + +COPYRIGHT: + {{.Copyright}}{{end}} +` + +// CommandHelpTemplate is 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. +var CommandHelpTemplate = `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} + +CATEGORY: + {{.Category}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description}}{{end}}{{if .VisibleFlags}} + +OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +` + +// SubcommandHelpTemplate is the text template for the subcommand help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var SubcommandHelpTemplate = `NAME: + {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} + +USAGE: + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} + +COMMANDS:{{range .VisibleCategories}}{{if .Name}} + + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + +OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +` + +var MarkdownDocTemplate = `% {{ .App.Name }}(8) {{ .App.Description }} + +% {{ .App.Author }} + +# NAME + +{{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} + +# SYNOPSIS + +{{ .App.Name }} +{{ if .SynopsisArgs }} +` + "```" + ` +{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` +{{ end }}{{ if .App.UsageText }} +# DESCRIPTION + +{{ .App.UsageText }} +{{ end }} +**Usage**: + +` + "```" + ` +{{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +` + "```" + ` +{{ if .GlobalArgs }} +# GLOBAL OPTIONS +{{ range $v := .GlobalArgs }} +{{ $v }}{{ end }} +{{ end }}{{ if .Commands }} +# COMMANDS +{{ range $v := .Commands }} +{{ $v }}{{ end }}{{ end }}` diff --git a/testdata/expected-doc-full.man b/testdata/expected-doc-full.man new file mode 100644 index 0000000..a2d0e6d --- /dev/null +++ b/testdata/expected-doc-full.man @@ -0,0 +1,80 @@ +.nh +.TH greet(8) + +.SH Harrison + +.SH NAME +.PP +greet \- Some app + + +.SH SYNOPSIS +.PP +greet + +.PP +.RS + +.nf +[\-\-another\-flag|\-b] +[\-\-flag|\-\-fl|\-f]=[value] +[\-\-socket|\-s]=[value] + +.fi +.RE + + +.SH DESCRIPTION +.PP +app [first\_arg] [second\_arg] + +.PP +\fBUsage\fP: + +.PP +.RS + +.nf +greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] + +.fi +.RE + + +.SH GLOBAL OPTIONS +.PP +\fB\-\-another\-flag, \-b\fP: another usage text + +.PP +\fB\-\-flag, \-\-fl, \-f\fP="": + +.PP +\fB\-\-socket, \-s\fP="": some usage text (default: value) + + +.SH COMMANDS +.SH config, c +.PP +another usage test + +.PP +\fB\-\-another\-flag, \-b\fP: another usage text + +.PP +\fB\-\-flag, \-\-fl, \-f\fP="": + +.SS sub\-config, s, ss +.PP +another usage test + +.PP +\fB\-\-sub\-command\-flag, \-s\fP: some usage text + +.PP +\fB\-\-sub\-flag, \-\-sub\-fl, \-s\fP="": + +.SH info, i, in +.PP +retrieve generic information + +.SH some\-command \ No newline at end of file diff --git a/testdata/expected-doc-full.md b/testdata/expected-doc-full.md new file mode 100644 index 0000000..8fb9f58 --- /dev/null +++ b/testdata/expected-doc-full.md @@ -0,0 +1,62 @@ +% greet(8) + +% Harrison + +# NAME + +greet - Some app + +# SYNOPSIS + +greet + +``` +[--another-flag|-b] +[--flag|--fl|-f]=[value] +[--socket|-s]=[value] +``` + +# DESCRIPTION + +app [first_arg] [second_arg] + +**Usage**: + +``` +greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +``` + +# GLOBAL OPTIONS + +**--another-flag, -b**: another usage text + +**--flag, --fl, -f**="": + +**--socket, -s**="": some usage text (default: value) + + +# COMMANDS + +## config, c + +another usage test + +**--another-flag, -b**: another usage text + +**--flag, --fl, -f**="": + +### sub-config, s, ss + +another usage test + +**--sub-command-flag, -s**: some usage text + +**--sub-flag, --sub-fl, -s**="": + +## info, i, in + +retrieve generic information + +## some-command + + diff --git a/testdata/expected-doc-no-commands.md b/testdata/expected-doc-no-commands.md new file mode 100644 index 0000000..dba7bb0 --- /dev/null +++ b/testdata/expected-doc-no-commands.md @@ -0,0 +1,36 @@ +% greet(8) + +% Harrison + +# NAME + +greet - Some app + +# SYNOPSIS + +greet + +``` +[--another-flag|-b] +[--flag|--fl|-f]=[value] +[--socket|-s]=[value] +``` + +# DESCRIPTION + +app [first_arg] [second_arg] + +**Usage**: + +``` +greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +``` + +# GLOBAL OPTIONS + +**--another-flag, -b**: another usage text + +**--flag, --fl, -f**="": + +**--socket, -s**="": some usage text (default: value) + diff --git a/testdata/expected-doc-no-flags.md b/testdata/expected-doc-no-flags.md new file mode 100644 index 0000000..8ce60fd --- /dev/null +++ b/testdata/expected-doc-no-flags.md @@ -0,0 +1,47 @@ +% greet(8) + +% Harrison + +# NAME + +greet - Some app + +# SYNOPSIS + +greet + +# DESCRIPTION + +app [first_arg] [second_arg] + +**Usage**: + +``` +greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +``` + +# COMMANDS + +## config, c + +another usage test + +**--another-flag, -b**: another usage text + +**--flag, --fl, -f**="": + +### sub-config, s, ss + +another usage test + +**--sub-command-flag, -s**: some usage text + +**--sub-flag, --sub-fl, -s**="": + +## info, i, in + +retrieve generic information + +## some-command + + From fdba7e0f8c921d4ce169cb416b2eae58026c83e9 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 20:14:50 +0530 Subject: [PATCH 167/215] linter fixes code cleanup changing some test code to ensure uniformity --- altsrc/flag.go | 14 +-- altsrc/flag_test.go | 26 ++--- altsrc/json_command_test.go | 26 ++--- app.go | 43 +++---- app_test.go | 90 +++++++-------- command.go | 14 +-- command_test.go | 28 ++--- context.go | 10 +- context_test.go | 52 ++++----- flag.go | 44 ++++---- flag_test.go | 220 ++++++++++++++++-------------------- help.go | 22 ++-- help_test.go | 38 +++---- 13 files changed, 303 insertions(+), 324 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 376733a..afb4ad4 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -72,7 +72,7 @@ func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourc } if value != nil { eachName(f.Name, func(name string) { - f.set.Set(f.Name, value.String()) + _ = f.set.Set(f.Name, value.String()) }) } } @@ -135,7 +135,7 @@ func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo } if value { eachName(f.Name, func(name string) { - f.set.Set(f.Name, strconv.FormatBool(value)) + _ = f.set.Set(f.Name, strconv.FormatBool(value)) }) } } @@ -153,7 +153,7 @@ func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceC } if !value { eachName(f.Name, func(name string) { - f.set.Set(f.Name, strconv.FormatBool(value)) + _ = f.set.Set(f.Name, strconv.FormatBool(value)) }) } } @@ -171,7 +171,7 @@ func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSource } if value != "" { eachName(f.Name, func(name string) { - f.set.Set(f.Name, value) + _ = f.set.Set(f.Name, value) }) } } @@ -189,7 +189,7 @@ func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCon } if value > 0 { eachName(f.Name, func(name string) { - f.set.Set(f.Name, strconv.FormatInt(int64(value), 10)) + _ = f.set.Set(f.Name, strconv.FormatInt(int64(value), 10)) }) } } @@ -207,7 +207,7 @@ func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour } if value > 0 { eachName(f.Name, func(name string) { - f.set.Set(f.Name, value.String()) + _ = f.set.Set(f.Name, value.String()) }) } } @@ -226,7 +226,7 @@ func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourc if value > 0 { floatStr := float64ToString(value) eachName(f.Name, func(name string) { - f.set.Set(f.Name, floatStr) + _ = f.set.Set(f.Name, floatStr) }) } } diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index 5f293bb..90a96d3 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -239,30 +239,30 @@ func TestDurationApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), FlagName: "test", - MapValue: time.Duration(30 * time.Second), + MapValue: 30 * time.Second, }) - expect(t, time.Duration(30*time.Second), c.Duration("test")) + expect(t, 30*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), FlagName: "test", - MapValue: time.Duration(30 * time.Second), - ContextValueString: time.Duration(15 * time.Second).String(), + MapValue: 30 * time.Second, + ContextValueString: (15 * time.Second).String(), }) - expect(t, time.Duration(15*time.Second), c.Duration("test")) + expect(t, 15*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", - MapValue: time.Duration(30 * time.Second), + MapValue: 30 * time.Second, EnvVarName: "TEST", - EnvVarValue: time.Duration(15 * time.Second).String(), + EnvVarValue: (15 * time.Second).String(), }) - expect(t, time.Duration(15*time.Second), c.Duration("test")) + expect(t, 15*time.Second, c.Duration("test")) } func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { @@ -300,19 +300,19 @@ func runTest(t *testing.T, test testApplyInputSource) *cli.Context { set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError) c := cli.NewContext(nil, set, nil) if test.EnvVarName != "" && test.EnvVarValue != "" { - os.Setenv(test.EnvVarName, test.EnvVarValue) + _ = os.Setenv(test.EnvVarName, test.EnvVarValue) defer os.Setenv(test.EnvVarName, "") } test.Flag.Apply(set) if test.ContextValue != nil { - flag := set.Lookup(test.FlagName) - flag.Value = test.ContextValue + f := set.Lookup(test.FlagName) + f.Value = test.ContextValue } if test.ContextValueString != "" { - set.Set(test.FlagName, test.ContextValueString) + _ = set.Set(test.FlagName, test.ContextValueString) } - test.Flag.ApplyInputSourceValue(c, inputSource) + _ = test.Flag.ApplyInputSourceValue(c, inputSource) return c } diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go index f24cae6..63c19b2 100644 --- a/altsrc/json_command_test.go +++ b/altsrc/json_command_test.go @@ -22,7 +22,7 @@ func TestCommandJSONFileTest(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -52,11 +52,11 @@ func TestCommandJSONFileTestGlobalEnvVarWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) - os.Setenv("THE_TEST", "10") + _ = os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -87,11 +87,11 @@ func TestCommandJSONFileTestGlobalEnvVarWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) - os.Setenv("THE_TEST", "10") + _ = os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -123,7 +123,7 @@ func TestCommandJSONFileTestSpecifiedFlagWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName, "--test", "7"} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -155,7 +155,7 @@ func TestCommandJSONFileTestSpecifiedFlagWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName, "--top.test", "7"} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -187,7 +187,7 @@ func TestCommandJSONFileTestDefaultValueFileWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -219,7 +219,7 @@ func TestCommandJSONFileTestDefaultValueFileWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -250,11 +250,11 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWins(t *testing.T app := cli.NewApp() set := flag.NewFlagSet("test", 0) - os.Setenv("THE_TEST", "11") + _ = os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) @@ -284,11 +284,11 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *tes app := cli.NewApp() set := flag.NewFlagSet("test", 0) - os.Setenv("THE_TEST", "11") + _ = os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} - set.Parse(test) + _ = set.Parse(test) c := cli.NewContext(app, set, nil) diff --git a/app.go b/app.go index 9ed492f..22ecc58 100644 --- a/app.go +++ b/app.go @@ -11,9 +11,10 @@ import ( ) var ( - changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" - appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) - runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) + changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" + appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) + // unused variable. commented for now. will remove in future if agreed upon by everyone + //runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." @@ -138,7 +139,7 @@ func (a *App) Setup() { a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) } - newCmds := []Command{} + var newCmds []Command for _, c := range a.Commands { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) @@ -197,8 +198,8 @@ func (a *App) Run(arguments []string) (err error) { nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { - fmt.Fprintln(a.Writer, nerr) - ShowAppHelp(context) + _, _ = fmt.Fprintln(a.Writer, nerr) + _ = ShowAppHelp(context) return nerr } context.shellComplete = shellComplete @@ -213,13 +214,13 @@ func (a *App) Run(arguments []string) (err error) { a.handleExitCoder(context, err) return err } - fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - ShowAppHelp(context) + _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) + _ = ShowAppHelp(context) return err } if !a.HideHelp && checkHelp(context) { - ShowAppHelp(context) + _ = ShowAppHelp(context) return nil } @@ -230,7 +231,7 @@ func (a *App) Run(arguments []string) (err error) { cerr := checkRequiredFlags(a.Flags, context) if cerr != nil { - ShowAppHelp(context) + _ = ShowAppHelp(context) return cerr } @@ -249,8 +250,8 @@ 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) + _, _ = fmt.Fprintf(a.Writer, "%v\n\n", beforeErr) + _ = ShowAppHelp(context) a.handleExitCoder(context, beforeErr) err = beforeErr return err @@ -284,7 +285,7 @@ func (a *App) Run(arguments []string) (err error) { // code in the cli.ExitCoder func (a *App) RunAndExitOnError() { if err := a.Run(os.Args); err != nil { - fmt.Fprintln(a.errWriter(), err) + _, _ = fmt.Fprintln(a.errWriter(), err) OsExiter(1) } } @@ -323,12 +324,12 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { context := NewContext(a, set, ctx) if nerr != nil { - fmt.Fprintln(a.Writer, nerr) - fmt.Fprintln(a.Writer) + _, _ = fmt.Fprintln(a.Writer, nerr) + _, _ = fmt.Fprintln(a.Writer) if len(a.Commands) > 0 { - ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(context) } else { - ShowCommandHelp(ctx, context.Args().First()) + _ = ShowCommandHelp(ctx, context.Args().First()) } return nerr } @@ -343,8 +344,8 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { a.handleExitCoder(context, err) return err } - fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - ShowSubcommandHelp(context) + _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) + _ = ShowSubcommandHelp(context) return err } @@ -360,7 +361,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { cerr := checkRequiredFlags(a.Flags, context) if cerr != nil { - ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(context) return cerr } @@ -440,7 +441,7 @@ func (a *App) VisibleCategories() []*CommandCategory { // VisibleCommands returns a slice of the Commands with Hidden=false func (a *App) VisibleCommands() []Command { - ret := []Command{} + var ret []Command for _, command := range a.Commands { if !command.Hidden { ret = append(ret, command) diff --git a/app_test.go b/app_test.go index 3fc27b5..415471e 100644 --- a/app_test.go +++ b/app_test.go @@ -47,7 +47,7 @@ func ExampleApp_Run() { app.Author = "Harrison" app.Email = "harrison@lolwut.com" app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // Hello Jeremy } @@ -85,7 +85,7 @@ func ExampleApp_Run_subcommand() { }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // Hello, Jeremy } @@ -117,7 +117,7 @@ func ExampleApp_Run_appHelp() { }, }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // NAME: // greet - A new cli application @@ -166,7 +166,7 @@ func ExampleApp_Run_commandHelp() { }, }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // NAME: // greet describeit - use it to see a description @@ -181,7 +181,7 @@ func ExampleApp_Run_commandHelp() { func ExampleApp_Run_noAction() { app := App{} app.Name = "greet" - app.Run([]string{"greet"}) + _ = app.Run([]string{"greet"}) // Output: // NAME: // greet @@ -208,7 +208,7 @@ func ExampleApp_Run_subcommandNoAction() { Description: "This is how we describe describeit the function", }, } - app.Run([]string{"greet", "describeit"}) + _ = app.Run([]string{"greet", "describeit"}) // Output: // NAME: // describeit - use it to see a description @@ -236,7 +236,7 @@ func ExampleApp_Run_bashComplete_withShortFlag() { }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // --other // -o @@ -269,7 +269,7 @@ func ExampleApp_Run_bashComplete_withLongFlag() { }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // --some-flag // --similar-flag @@ -298,7 +298,7 @@ func ExampleApp_Run_bashComplete_withMultipleLongFlag() { }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // --string // --string-flag-2 @@ -332,7 +332,7 @@ func ExampleApp_Run_bashComplete() { }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // describeit // d @@ -344,7 +344,7 @@ func ExampleApp_Run_bashComplete() { func ExampleApp_Run_zshComplete() { // set args for examples sake os.Args = []string{"greet", "--generate-bash-completion"} - os.Setenv("_CLI_ZSH_AUTOCOMPLETE_HACK", "1") + _ = os.Setenv("_CLI_ZSH_AUTOCOMPLETE_HACK", "1") app := NewApp() app.Name = "greet" @@ -370,7 +370,7 @@ func ExampleApp_Run_zshComplete() { }, } - app.Run(os.Args) + _ = app.Run(os.Args) // Output: // describeit:use it to see a description // d:use it to see a description @@ -444,7 +444,7 @@ func TestApp_CommandWithArgBeforeFlags(t *testing.T) { } app.Commands = []Command{command} - app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) + _ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) expect(t, parsedOption, "my-option") expect(t, firstArg, "my-arg") @@ -474,7 +474,7 @@ func TestApp_CommandWithArgBeforeBoolFlags(t *testing.T) { } app.Commands = []Command{command} - app.Run([]string{"", "cmd", "my-arg", "--boolflag", "--option", "my-option", "-b", "--secondOption", "fancy-option"}) + _ = app.Run([]string{"", "cmd", "my-arg", "--boolflag", "--option", "my-option", "-b", "--secondOption", "fancy-option"}) expect(t, parsedOption, "my-option") expect(t, parsedSecondOption, "fancy-option") @@ -504,7 +504,7 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { Before: func(_ *Context) error { return nil }, }, } - a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) + _ = a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) expect(t, context.Args().Get(0), "abcd") expect(t, context.String("lang"), "spanish") @@ -519,7 +519,7 @@ func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { } set := flag.NewFlagSet("", flag.ContinueOnError) - set.Parse([]string{"", "---foo"}) + _ = set.Parse([]string{"", "---foo"}) c := &Context{flagSet: set} err := a.RunAsSubcommand(c) @@ -545,7 +545,7 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { } app.Commands = []Command{command} - app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"}) + _ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"}) expect(t, parsedOption, "my-option") expect(t, args[0], "my-arg") @@ -566,7 +566,7 @@ func TestApp_CommandWithDash(t *testing.T) { } app.Commands = []Command{command} - app.Run([]string{"", "cmd", "my-arg", "-"}) + _ = app.Run([]string{"", "cmd", "my-arg", "-"}) expect(t, args[0], "my-arg") expect(t, args[1], "-") @@ -585,7 +585,7 @@ func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) { } app.Commands = []Command{command} - app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"}) + _ = app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"}) expect(t, args[0], "my-arg") expect(t, args[1], "--") @@ -646,7 +646,7 @@ func TestApp_Float64Flag(t *testing.T) { return nil } - app.Run([]string{"", "--height", "1.93"}) + _ = app.Run([]string{"", "--height", "1.93"}) expect(t, meters, 1.93) } @@ -669,7 +669,7 @@ func TestApp_ParseSliceFlags(t *testing.T) { } app.Commands = []Command{command} - app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) + _ = app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) IntsEquals := func(a, b []int) bool { if len(a) != len(b) { @@ -725,7 +725,7 @@ func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) { } app.Commands = []Command{command} - app.Run([]string{"", "cmd", "my-arg", "-a", "2", "-str", "A"}) + _ = app.Run([]string{"", "cmd", "my-arg", "-a", "2", "-str", "A"}) var expectedIntSlice = []int{2} var expectedStringSlice = []string{"A"} @@ -978,7 +978,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_empty_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -987,9 +987,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_empty_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1005,7 +1005,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_help_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--help"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1013,9 +1013,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_help_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--help"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1031,7 +1031,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_optional_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--optional", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, }}, @@ -1040,9 +1040,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "error_case_optional_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--optional", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, }}, @@ -1058,7 +1058,7 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_required_flag_input_on_command", appRunInput: []string{"myCLI", "myCommand", "--requiredFlag", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1066,9 +1066,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { { testCase: "valid_case_required_flag_input_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--requiredFlag", "cats"}, - appCommands: []Command{Command{ + appCommands: []Command{{ Name: "myCommand", - Subcommands: []Command{Command{ + Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, @@ -1111,7 +1111,7 @@ func TestAppHelpPrinter(t *testing.T) { } app := NewApp() - app.Run([]string{"-h"}) + _ = app.Run([]string{"-h"}) if wasCalled == false { t.Errorf("Help printer expected to be called, but was not") @@ -1158,7 +1158,7 @@ func TestApp_CommandNotFound(t *testing.T) { }, } - app.Run([]string{"command", "foo"}) + _ = app.Run([]string{"command", "foo"}) expect(t, counts.CommandNotFound, 1) expect(t, counts.SubCommand, 0) @@ -1568,7 +1568,7 @@ func TestApp_Run_Categories(t *testing.T) { buf := new(bytes.Buffer) app.Writer = buf - app.Run([]string{"categories"}) + _ = app.Run([]string{"categories"}) expect := CommandCategories{ &CommandCategory{ @@ -1975,7 +1975,7 @@ func TestHandleExitCoder_Custom(t *testing.T) { } app.ExitErrHandler = func(_ *Context, _ error) { - fmt.Fprintln(ErrWriter, "I'm a Custom error handler, I print what I want!") + _, _ = fmt.Fprintln(ErrWriter, "I'm a Custom error handler, I print what I want!") } ctx := NewContext(app, fs, nil) @@ -1994,14 +1994,14 @@ func TestHandleAction_WithUnknownPanic(t *testing.T) { app := NewApp() app.Action = func(ctx *Context) error { - fn(ctx) + _ = fn(ctx) return nil } fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } - HandleAction(app.Action, NewContext(app, fs, nil)) + _ = HandleAction(app.Action, NewContext(app, fs, nil)) } func TestShellCompletionForIncompleteFlags(t *testing.T) { @@ -2019,12 +2019,12 @@ func TestShellCompletionForIncompleteFlags(t *testing.T) { } for _, name := range command.Names() { - fmt.Fprintln(ctx.App.Writer, name) + _, _ = fmt.Fprintln(ctx.App.Writer, name) } } - for _, flag := range ctx.App.Flags { - for _, name := range strings.Split(flag.GetName(), ",") { + for _, f := range ctx.App.Flags { + for _, name := range strings.Split(f.GetName(), ",") { if name == BashCompletionFlag.GetName() { continue } @@ -2032,9 +2032,9 @@ func TestShellCompletionForIncompleteFlags(t *testing.T) { switch name = strings.TrimSpace(name); len(name) { case 0: case 1: - fmt.Fprintln(ctx.App.Writer, "-"+name) + _, _ = fmt.Fprintln(ctx.App.Writer, "-"+name) default: - fmt.Fprintln(ctx.App.Writer, "--"+name) + _, _ = fmt.Fprintln(ctx.App.Writer, "--"+name) } } } diff --git a/command.go b/command.go index f1ca02d..1f531b3 100644 --- a/command.go +++ b/command.go @@ -125,9 +125,9 @@ func (c Command) Run(ctx *Context) (err error) { context.App.handleExitCoder(context, err) return err } - fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) - fmt.Fprintln(context.App.Writer) - ShowCommandHelp(context, c.Name) + _, _ = fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) + _, _ = fmt.Fprintln(context.App.Writer) + _ = ShowCommandHelp(context, c.Name) return err } @@ -137,7 +137,7 @@ func (c Command) Run(ctx *Context) (err error) { cerr := checkRequiredFlags(c.Flags, context) if cerr != nil { - ShowCommandHelp(context, c.Name) + _ = ShowCommandHelp(context, c.Name) return cerr } @@ -158,7 +158,7 @@ func (c Command) Run(ctx *Context) (err error) { if c.Before != nil { err = c.Before(context) if err != nil { - ShowCommandHelp(context, c.Name) + _ = ShowCommandHelp(context, c.Name) context.App.handleExitCoder(context, err) return err } @@ -272,7 +272,7 @@ func reorderArgs(args []string) []string { } func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { - allCharsFlags := func (s string) bool { + allCharsFlags := func(s string) bool { for i := range s { f := set.Lookup(string(s[i])) if f == nil { @@ -285,7 +285,7 @@ func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string { // separate combined flags var flagArgsSeparated []string for _, flagArg := range flagArgs { - if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 { + if strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2 { if !allCharsFlags(flagArg[1:]) { flagArgsSeparated = append(flagArgsSeparated, flagArg) continue diff --git a/command_test.go b/command_test.go index 8c2650e..56698fe 100644 --- a/command_test.go +++ b/command_test.go @@ -35,7 +35,7 @@ func TestCommandFlagParsing(t *testing.T) { app := NewApp() app.Writer = ioutil.Discard set := flag.NewFlagSet("test", 0) - set.Parse(c.testArgs) + _ = set.Parse(c.testArgs) context := NewContext(app, set, nil) @@ -59,9 +59,9 @@ func TestCommandFlagParsing(t *testing.T) { func TestParseAndRunShortOpts(t *testing.T) { cases := []struct { - testArgs []string - expectedErr error - expectedArgs []string + testArgs []string + expectedErr error + expectedArgs []string }{ {[]string{"foo", "test", "-a"}, nil, []string{}}, {[]string{"foo", "test", "-c", "arg1", "arg2"}, nil, []string{"arg1", "arg2"}}, @@ -71,18 +71,18 @@ func TestParseAndRunShortOpts(t *testing.T) { {[]string{"foo", "test", "-cf"}, nil, []string{}}, {[]string{"foo", "test", "-acf"}, nil, []string{}}, {[]string{"foo", "test", "-invalid"}, errors.New("flag provided but not defined: -invalid"), []string{}}, - {[]string{"foo", "test", "-acf", "arg1", "-invalid"}, nil, []string{"arg1" ,"-invalid"}}, + {[]string{"foo", "test", "-acf", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}}, } var args []string cmd := Command{ - Name: "test", - Usage: "this is for testing", - Description: "testing", - Action: func(c *Context) error { - args = c.Args() - return nil - }, + Name: "test", + Usage: "this is for testing", + Description: "testing", + Action: func(c *Context) error { + args = c.Args() + return nil + }, SkipArgReorder: true, UseShortOptionHandling: true, Flags: []Flag{ @@ -304,7 +304,7 @@ func TestCommandFlagReordering(t *testing.T) { for _, c := range cases { value := "" - args := []string{} + var args []string app := &App{ Commands: []Command{ { @@ -339,7 +339,7 @@ func TestCommandSkipFlagParsing(t *testing.T) { } for _, c := range cases { - args := []string{} + var args []string app := &App{ Commands: []Command{ { diff --git a/context.go b/context.go index 3e516c8..db7cd69 100644 --- a/context.go +++ b/context.go @@ -139,8 +139,8 @@ func (c *Context) GlobalIsSet(name string) bool { // FlagNames returns a slice of flag names used in this context. func (c *Context) FlagNames() (names []string) { - for _, flag := range c.Command.Flags { - name := strings.Split(flag.GetName(), ",")[0] + for _, f := range c.Command.Flags { + name := strings.Split(f.GetName(), ",")[0] if name == "help" { continue } @@ -151,8 +151,8 @@ func (c *Context) FlagNames() (names []string) { // GlobalFlagNames returns a slice of global flag names used by the app. func (c *Context) GlobalFlagNames() (names []string) { - for _, flag := range c.App.Flags { - name := strings.Split(flag.GetName(), ",")[0] + for _, f := range c.App.Flags { + name := strings.Split(f.GetName(), ",")[0] if name == "help" || name == "version" { continue } @@ -250,7 +250,7 @@ func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { switch ff.Value.(type) { case *StringSlice: default: - set.Set(name, ff.Value.String()) + _ = set.Set(name, ff.Value.String()) } } diff --git a/context_test.go b/context_test.go index f329dc7..9e594dd 100644 --- a/context_test.go +++ b/context_test.go @@ -99,9 +99,9 @@ func TestContext_GlobalFloat64(t *testing.T) { func TestContext_Duration(t *testing.T) { set := flag.NewFlagSet("test", 0) - set.Duration("myflag", time.Duration(12*time.Second), "doc") + set.Duration("myflag", 12*time.Second, "doc") c := NewContext(nil, set, nil) - expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) + expect(t, c.Duration("myflag"), 12*time.Second) } func TestContext_String(t *testing.T) { @@ -153,7 +153,7 @@ func TestContext_Args(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) - set.Parse([]string{"--myflag", "bat", "baz"}) + _ = set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, len(c.Args()), 2) expect(t, c.Bool("myflag"), true) } @@ -162,7 +162,7 @@ func TestContext_NArg(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) - set.Parse([]string{"--myflag", "bat", "baz"}) + _ = set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, c.NArg(), 2) } @@ -174,8 +174,8 @@ func TestContext_IsSet(t *testing.T) { globalSet.Bool("myflagGlobal", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) - set.Parse([]string{"--myflag", "bat", "baz"}) - globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) + _ = set.Parse([]string{"--myflag", "bat", "baz"}) + _ = globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) expect(t, c.IsSet("myflag"), true) expect(t, c.IsSet("otherflag"), false) expect(t, c.IsSet("bogusflag"), false) @@ -193,8 +193,8 @@ func TestContext_IsSet_fromEnv(t *testing.T) { ) clearenv() - os.Setenv("APP_TIMEOUT_SECONDS", "15.5") - os.Setenv("APP_PASSWORD", "") + _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + _ = os.Setenv("APP_PASSWORD", "") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, @@ -214,7 +214,7 @@ func TestContext_IsSet_fromEnv(t *testing.T) { return nil }, } - a.Run([]string{"run"}) + _ = a.Run([]string{"run"}) expect(t, timeoutIsSet, true) expect(t, tIsSet, true) expect(t, passwordIsSet, true) @@ -222,8 +222,8 @@ func TestContext_IsSet_fromEnv(t *testing.T) { expect(t, noEnvVarIsSet, false) expect(t, nIsSet, false) - os.Setenv("APP_UNPARSABLE", "foobar") - a.Run([]string{"run"}) + _ = os.Setenv("APP_UNPARSABLE", "foobar") + _ = a.Run([]string{"run"}) expect(t, unparsableIsSet, false) expect(t, uIsSet, false) } @@ -237,8 +237,8 @@ func TestContext_GlobalIsSet(t *testing.T) { globalSet.Bool("myflagGlobalUnset", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) - set.Parse([]string{"--myflag", "bat", "baz"}) - globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) + _ = set.Parse([]string{"--myflag", "bat", "baz"}) + _ = globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) expect(t, c.GlobalIsSet("myflag"), false) expect(t, c.GlobalIsSet("otherflag"), false) expect(t, c.GlobalIsSet("bogusflag"), false) @@ -261,9 +261,9 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) { ) clearenv() - os.Setenv("APP_TIMEOUT_SECONDS", "15.5") - os.Setenv("APP_PASSWORD", "badpass") - os.Setenv("APP_OVERRIDE", "overridden") + _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + _ = os.Setenv("APP_PASSWORD", "badpass") + _ = os.Setenv("APP_OVERRIDE", "overridden") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, @@ -308,7 +308,7 @@ func TestContext_GlobalIsSet_fromEnv(t *testing.T) { expect(t, oIsSet, true) expect(t, overrideValue, "overridden") - os.Setenv("APP_UNPARSABLE", "foobar") + _ = os.Setenv("APP_UNPARSABLE", "foobar") if err := a.Run([]string{"run"}); err != nil { t.Logf("error running Run(): %+v", err) } @@ -324,8 +324,8 @@ func TestContext_NumFlags(t *testing.T) { globalSet.Bool("myflagGlobal", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) - set.Parse([]string{"--myflag", "--otherflag=foo"}) - globalSet.Parse([]string{"--myflagGlobal"}) + _ = set.Parse([]string{"--myflag", "--otherflag=foo"}) + _ = globalSet.Parse([]string{"--myflagGlobal"}) expect(t, c.NumFlags(), 2) } @@ -341,7 +341,7 @@ func TestContext_GlobalFlag(t *testing.T) { globalFlagSet = c.GlobalIsSet("global") return nil } - app.Run([]string{"command", "-g", "foo"}) + _ = app.Run([]string{"command", "-g", "foo"}) expect(t, globalFlag, "foo") expect(t, globalFlagSet, true) @@ -379,7 +379,7 @@ func TestContext_GlobalFlagsInSubcommands(t *testing.T) { }, } - app.Run([]string{"command", "-d", "foo", "-p", "bar"}) + _ = app.Run([]string{"command", "-d", "foo", "-p", "bar"}) expect(t, subcommandRun, true) expect(t, parentFlag, true) @@ -391,7 +391,7 @@ func TestContext_Set(t *testing.T) { c := NewContext(nil, set, nil) expect(t, c.IsSet("int"), false) - c.Set("int", "1") + _ = c.Set("int", "1") expect(t, c.Int("int"), 1) expect(t, c.IsSet("int"), true) } @@ -406,12 +406,12 @@ func TestContext_GlobalSet(t *testing.T) { pc := NewContext(nil, gSet, nil) c := NewContext(nil, set, pc) - c.Set("int", "1") + _ = c.Set("int", "1") expect(t, c.Int("int"), 1) expect(t, c.GlobalInt("int"), 5) expect(t, c.GlobalIsSet("int"), false) - c.GlobalSet("int", "1") + _ = c.GlobalSet("int", "1") expect(t, c.Int("int"), 1) expect(t, c.GlobalInt("int"), 1) expect(t, c.GlobalIsSet("int"), true) @@ -525,10 +525,10 @@ func TestCheckRequiredFlags(t *testing.T) { for _, flags := range test.flags { flags.Apply(set) } - set.Parse(test.parseInput) + _ = set.Parse(test.parseInput) if test.envVarInput[0] != "" { os.Clearenv() - os.Setenv(test.envVarInput[0], test.envVarInput[1]) + _ = os.Setenv(test.envVarInput[0], test.envVarInput[1]) } ctx := &Context{} context := NewContext(ctx.App, set, ctx) diff --git a/flag.go b/flag.go index d98c808..dfa84be 100644 --- a/flag.go +++ b/flag.go @@ -126,7 +126,7 @@ type Generic interface { // provided by the user for parsing by the flag // Ignores parsing errors func (f GenericFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError takes the flagset and calls Set on the generic flag with the value @@ -173,7 +173,7 @@ func (f *StringSlice) Get() interface{} { // Apply populates the flag given the flag set and environment // Ignores errors func (f StringSliceFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -234,7 +234,7 @@ func (f *IntSlice) Get() interface{} { // Apply populates the flag given the flag set and environment // Ignores errors func (f IntSliceFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -295,7 +295,7 @@ func (f *Int64Slice) Get() interface{} { // Apply populates the flag given the flag set and environment // Ignores errors func (f Int64SliceFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -327,7 +327,7 @@ func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f BoolFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -359,7 +359,7 @@ func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f BoolTFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -392,7 +392,7 @@ func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f StringFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -415,7 +415,7 @@ func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f IntFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -442,7 +442,7 @@ func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f Int64Flag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -470,7 +470,7 @@ func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f UintFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -498,7 +498,7 @@ func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f Uint64Flag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -509,7 +509,7 @@ func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) } - f.Value = uint64(envValInt) + f.Value = envValInt } eachName(f.Name, func(name string) { @@ -526,7 +526,7 @@ func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f DurationFlag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -554,7 +554,7 @@ func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { // Apply populates the flag given the flag set and environment // Ignores errors func (f Float64Flag) Apply(set *flag.FlagSet) { - f.ApplyWithError(set) + _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment @@ -565,7 +565,7 @@ func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) } - f.Value = float64(envValFloat) + f.Value = envValFloat } eachName(f.Name, func(name string) { @@ -580,11 +580,11 @@ func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { } func visibleFlags(fl []Flag) []Flag { - visible := []Flag{} - for _, flag := range fl { - field := flagValue(flag).FieldByName("Hidden") + var visible []Flag + for _, f := range fl { + field := flagValue(f).FieldByName("Hidden") if !field.IsValid() || !field.Bool() { - visible = append(visible, flag) + visible = append(visible, f) } } return visible @@ -729,7 +729,7 @@ func stringifyFlag(f Flag) string { } func stringifyIntSliceFlag(f IntSliceFlag) string { - defaultVals := []string{} + var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.Itoa(i)) @@ -740,7 +740,7 @@ func stringifyIntSliceFlag(f IntSliceFlag) string { } func stringifyInt64SliceFlag(f Int64SliceFlag) string { - defaultVals := []string{} + var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) @@ -751,7 +751,7 @@ func stringifyInt64SliceFlag(f Int64SliceFlag) string { } func stringifyStringSliceFlag(f StringSliceFlag) string { - defaultVals := []string{} + var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, s := range f.Value.Value() { if len(s) > 0 { diff --git a/flag_test.go b/flag_test.go index da9fd73..f4a24c2 100644 --- a/flag_test.go +++ b/flag_test.go @@ -89,7 +89,7 @@ func TestFlagsFromEnv(t *testing.T) { for _, test := range flagTests { os.Clearenv() - os.Setenv(reflect.ValueOf(test.flag).FieldByName("EnvVar").String(), test.input) + _ = os.Setenv(reflect.ValueOf(test.flag).FieldByName("EnvVar").String(), test.input) a := App{ Flags: []Flag{test.flag}, Action: func(ctx *Context) error { @@ -145,7 +145,7 @@ func TestStringFlagHelpOutput(t *testing.T) { func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_FOO", "derp") + _ = os.Setenv("APP_FOO", "derp") for _, test := range stringFlagTests { flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} output := flag.String() @@ -244,22 +244,22 @@ var stringSliceFlagTests = []struct { }{ {"foo", func() *StringSlice { s := &StringSlice{} - s.Set("") + _ = s.Set("") return s }(), "--foo value\t"}, {"f", func() *StringSlice { s := &StringSlice{} - s.Set("") + _ = s.Set("") return s }(), "-f value\t"}, {"f", func() *StringSlice { s := &StringSlice{} - s.Set("Lipstick") + _ = s.Set("Lipstick") return s }(), "-f value\t(default: \"Lipstick\")"}, {"test", func() *StringSlice { s := &StringSlice{} - s.Set("Something") + _ = s.Set("Something") return s }(), "--test value\t(default: \"Something\")"}, } @@ -277,7 +277,7 @@ func TestStringSliceFlagHelpOutput(t *testing.T) { func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_QWWX", "11,4") + _ = os.Setenv("APP_QWWX", "11,4") for _, test := range stringSliceFlagTests { flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} output := flag.String() @@ -313,7 +313,7 @@ func TestIntFlagHelpOutput(t *testing.T) { func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_BAR", "2") + _ = os.Setenv("APP_BAR", "2") for _, test := range intFlagTests { flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() @@ -349,7 +349,7 @@ func TestInt64FlagHelpOutput(t *testing.T) { func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_BAR", "2") + _ = os.Setenv("APP_BAR", "2") for _, test := range int64FlagTests { flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() @@ -385,7 +385,7 @@ func TestUintFlagHelpOutput(t *testing.T) { func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_BAR", "2") + _ = os.Setenv("APP_BAR", "2") for _, test := range uintFlagTests { flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() @@ -421,7 +421,7 @@ func TestUint64FlagHelpOutput(t *testing.T) { func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_BAR", "2") + _ = os.Setenv("APP_BAR", "2") for _, test := range uint64FlagTests { flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() @@ -457,7 +457,7 @@ func TestDurationFlagHelpOutput(t *testing.T) { func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_BAR", "2h3m6s") + _ = os.Setenv("APP_BAR", "2h3m6s") for _, test := range durationFlagTests { flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() @@ -481,8 +481,8 @@ var intSliceFlagTests = []struct { {"H", &IntSlice{}, "-H value\t"}, {"H, heads", func() *IntSlice { i := &IntSlice{} - i.Set("9") - i.Set("3") + _ = i.Set("9") + _ = i.Set("3") return i }(), "-H value, --heads value\t(default: 9, 3)"}, } @@ -500,7 +500,7 @@ func TestIntSliceFlagHelpOutput(t *testing.T) { func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_SMURF", "42,3") + _ = os.Setenv("APP_SMURF", "42,3") for _, test := range intSliceFlagTests { flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} output := flag.String() @@ -524,8 +524,8 @@ var int64SliceFlagTests = []struct { {"H", &Int64Slice{}, "-H value\t"}, {"H, heads", func() *Int64Slice { i := &Int64Slice{} - i.Set("2") - i.Set("17179869184") + _ = i.Set("2") + _ = i.Set("17179869184") return i }(), "-H value, --heads value\t(default: 2, 17179869184)"}, } @@ -543,7 +543,7 @@ func TestInt64SliceFlagHelpOutput(t *testing.T) { func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_SMURF", "42,17179869184") + _ = os.Setenv("APP_SMURF", "42,17179869184") for _, test := range int64SliceFlagTests { flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} output := flag.String() @@ -568,7 +568,7 @@ var float64FlagTests = []struct { func TestFloat64FlagHelpOutput(t *testing.T) { for _, test := range float64FlagTests { - flag := Float64Flag{Name: test.name, Value: float64(0.1)} + flag := Float64Flag{Name: test.name, Value: 0.1} output := flag.String() if output != test.expected { @@ -579,7 +579,7 @@ func TestFloat64FlagHelpOutput(t *testing.T) { func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_BAZ", "99.4") + _ = os.Setenv("APP_BAZ", "99.4") for _, test := range float64FlagTests { flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} output := flag.String() @@ -616,7 +616,7 @@ func TestGenericFlagHelpOutput(t *testing.T) { func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() - os.Setenv("APP_ZAP", "3") + _ = os.Setenv("APP_ZAP", "3") for _, test := range genericFlagTests { flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} output := flag.String() @@ -632,7 +632,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { } func TestParseMultiString(t *testing.T) { - (&App{ + _ = (&App{ Flags: []Flag{ StringFlag{Name: "serve, s"}, }, @@ -650,7 +650,7 @@ func TestParseMultiString(t *testing.T) { func TestParseDestinationString(t *testing.T) { var dest string - a := App{ + _ = (&App{ Flags: []Flag{ StringFlag{ Name: "dest", @@ -663,14 +663,13 @@ func TestParseDestinationString(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "--dest", "10"}) + }).Run([]string{"run", "--dest", "10"}) } func TestParseMultiStringFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_COUNT", "20") - (&App{ + _ = os.Setenv("APP_COUNT", "20") + _ = (&App{ Flags: []Flag{ StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, }, @@ -688,8 +687,8 @@ func TestParseMultiStringFromEnv(t *testing.T) { func TestParseMultiStringFromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_COUNT", "20") - (&App{ + _ = os.Setenv("APP_COUNT", "20") + _ = (&App{ Flags: []Flag{ StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, }, @@ -706,7 +705,7 @@ func TestParseMultiStringFromEnvCascade(t *testing.T) { } func TestParseMultiStringSlice(t *testing.T) { - (&App{ + _ = (&App{ Flags: []Flag{ StringSliceFlag{Name: "serve, s", Value: &StringSlice{}}, }, @@ -724,9 +723,9 @@ func TestParseMultiStringSlice(t *testing.T) { func TestParseMultiStringSliceFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_INTERVALS", "20,30,40") + _ = os.Setenv("APP_INTERVALS", "20,30,40") - (&App{ + _ = (&App{ Flags: []Flag{ StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"}, }, @@ -744,9 +743,9 @@ func TestParseMultiStringSliceFromEnv(t *testing.T) { func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_INTERVALS", "20,30,40") + _ = os.Setenv("APP_INTERVALS", "20,30,40") - (&App{ + _ = (&App{ Flags: []Flag{ StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, @@ -763,7 +762,7 @@ func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { } func TestParseMultiInt(t *testing.T) { - a := App{ + _ = (&App{ Flags: []Flag{ IntFlag{Name: "serve, s"}, }, @@ -776,13 +775,12 @@ func TestParseMultiInt(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "-s", "10"}) + }).Run([]string{"run", "-s", "10"}) } func TestParseDestinationInt(t *testing.T) { var dest int - a := App{ + _ = (&App{ Flags: []Flag{ IntFlag{ Name: "dest", @@ -795,14 +793,13 @@ func TestParseDestinationInt(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "--dest", "10"}) + }).Run([]string{"run", "--dest", "10"}) } func TestParseMultiIntFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_TIMEOUT_SECONDS", "10") - a := App{ + _ = os.Setenv("APP_TIMEOUT_SECONDS", "10") + _ = (&App{ Flags: []Flag{ IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, }, @@ -815,14 +812,13 @@ func TestParseMultiIntFromEnv(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseMultiIntFromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_TIMEOUT_SECONDS", "10") - a := App{ + _ = os.Setenv("APP_TIMEOUT_SECONDS", "10") + _ = (&App{ Flags: []Flag{ IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, }, @@ -835,12 +831,11 @@ func TestParseMultiIntFromEnvCascade(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseMultiIntSlice(t *testing.T) { - (&App{ + _ = (&App{ Flags: []Flag{ IntSliceFlag{Name: "serve, s", Value: &IntSlice{}}, }, @@ -858,9 +853,9 @@ func TestParseMultiIntSlice(t *testing.T) { func TestParseMultiIntSliceFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_INTERVALS", "20,30,40") + _ = os.Setenv("APP_INTERVALS", "20,30,40") - (&App{ + _ = (&App{ Flags: []Flag{ IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"}, }, @@ -878,9 +873,9 @@ func TestParseMultiIntSliceFromEnv(t *testing.T) { func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_INTERVALS", "20,30,40") + _ = os.Setenv("APP_INTERVALS", "20,30,40") - (&App{ + _ = (&App{ Flags: []Flag{ IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, @@ -897,7 +892,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { } func TestParseMultiInt64Slice(t *testing.T) { - (&App{ + _ = (&App{ Flags: []Flag{ Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}}, }, @@ -915,9 +910,9 @@ func TestParseMultiInt64Slice(t *testing.T) { func TestParseMultiInt64SliceFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_INTERVALS", "20,30,17179869184") + _ = os.Setenv("APP_INTERVALS", "20,30,17179869184") - (&App{ + _ = (&App{ Flags: []Flag{ Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"}, }, @@ -935,9 +930,9 @@ func TestParseMultiInt64SliceFromEnv(t *testing.T) { func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_INTERVALS", "20,30,17179869184") + _ = os.Setenv("APP_INTERVALS", "20,30,17179869184") - (&App{ + _ = (&App{ Flags: []Flag{ Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, @@ -954,7 +949,7 @@ func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) { } func TestParseMultiFloat64(t *testing.T) { - a := App{ + _ = (&App{ Flags: []Flag{ Float64Flag{Name: "serve, s"}, }, @@ -967,13 +962,12 @@ func TestParseMultiFloat64(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "-s", "10.2"}) + }).Run([]string{"run", "-s", "10.2"}) } func TestParseDestinationFloat64(t *testing.T) { var dest float64 - a := App{ + _ = (&App{ Flags: []Flag{ Float64Flag{ Name: "dest", @@ -986,14 +980,13 @@ func TestParseDestinationFloat64(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "--dest", "10.2"}) + }).Run([]string{"run", "--dest", "10.2"}) } func TestParseMultiFloat64FromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_TIMEOUT_SECONDS", "15.5") - a := App{ + _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + _ = (&App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, }, @@ -1006,14 +999,13 @@ func TestParseMultiFloat64FromEnv(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseMultiFloat64FromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_TIMEOUT_SECONDS", "15.5") - a := App{ + _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + _ = (&App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, }, @@ -1026,12 +1018,11 @@ func TestParseMultiFloat64FromEnvCascade(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseMultiBool(t *testing.T) { - a := App{ + _ = (&App{ Flags: []Flag{ BoolFlag{Name: "serve, s"}, }, @@ -1044,15 +1035,14 @@ func TestParseMultiBool(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "--serve"}) + }).Run([]string{"run", "--serve"}) } func TestParseBoolShortOptionHandle(t *testing.T) { - a := App{ + _ = (&App{ Commands: []Command{ { - Name: "foobar", + Name: "foobar", UseShortOptionHandling: true, Action: func(ctx *Context) error { if ctx.Bool("serve") != true { @@ -1069,13 +1059,12 @@ func TestParseBoolShortOptionHandle(t *testing.T) { }, }, }, - } - a.Run([]string{"run", "foobar", "-so"}) + }).Run([]string{"run", "foobar", "-so"}) } func TestParseDestinationBool(t *testing.T) { var dest bool - a := App{ + _ = (&App{ Flags: []Flag{ BoolFlag{ Name: "dest", @@ -1088,14 +1077,13 @@ func TestParseDestinationBool(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "--dest"}) + }).Run([]string{"run", "--dest"}) } func TestParseMultiBoolFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_DEBUG", "1") - a := App{ + _ = os.Setenv("APP_DEBUG", "1") + _ = (&App{ Flags: []Flag{ BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, }, @@ -1108,14 +1096,13 @@ func TestParseMultiBoolFromEnv(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseMultiBoolFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_DEBUG", "1") - a := App{ + _ = (&App{ Flags: []Flag{ BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, }, @@ -1128,8 +1115,7 @@ func TestParseMultiBoolFromEnvCascade(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseBoolTFromEnv(t *testing.T) { @@ -1145,8 +1131,8 @@ func TestParseBoolTFromEnv(t *testing.T) { for _, test := range boolTFlagTests { os.Clearenv() - os.Setenv("DEBUG", test.input) - a := App{ + _ = os.Setenv("DEBUG", test.input) + _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "DEBUG"}, }, @@ -1159,13 +1145,12 @@ func TestParseBoolTFromEnv(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } } func TestParseMultiBoolT(t *testing.T) { - a := App{ + _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "serve, s"}, }, @@ -1178,13 +1163,12 @@ func TestParseMultiBoolT(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "--serve"}) + }).Run([]string{"run", "--serve"}) } func TestParseDestinationBoolT(t *testing.T) { var dest bool - a := App{ + _ = (&App{ Flags: []Flag{ BoolTFlag{ Name: "dest", @@ -1197,14 +1181,13 @@ func TestParseDestinationBoolT(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "--dest"}) + }).Run([]string{"run", "--dest"}) } func TestParseMultiBoolTFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_DEBUG", "0") - a := App{ + _ = os.Setenv("APP_DEBUG", "0") + _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, }, @@ -1217,14 +1200,13 @@ func TestParseMultiBoolTFromEnv(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseMultiBoolTFromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_DEBUG", "0") - a := App{ + _ = os.Setenv("APP_DEBUG", "0") + _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, }, @@ -1237,8 +1219,7 @@ func TestParseMultiBoolTFromEnvCascade(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } type Parser [2]string @@ -1264,7 +1245,7 @@ func (p *Parser) Get() interface{} { } func TestParseGeneric(t *testing.T) { - a := App{ + _ = (&App{ Flags: []Flag{ GenericFlag{Name: "serve, s", Value: &Parser{}}, }, @@ -1277,14 +1258,13 @@ func TestParseGeneric(t *testing.T) { } return nil }, - } - a.Run([]string{"run", "-s", "10,20"}) + }).Run([]string{"run", "-s", "10,20"}) } func TestParseGenericFromEnv(t *testing.T) { os.Clearenv() - os.Setenv("APP_SERVE", "20,30") - a := App{ + _ = os.Setenv("APP_SERVE", "20,30") + _ = (&App{ Flags: []Flag{ GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, }, @@ -1297,14 +1277,13 @@ func TestParseGenericFromEnv(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestParseGenericFromEnvCascade(t *testing.T) { os.Clearenv() - os.Setenv("APP_FOO", "99,2000") - a := App{ + _ = os.Setenv("APP_FOO", "99,2000") + _ = (&App{ Flags: []Flag{ GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, }, @@ -1314,8 +1293,7 @@ func TestParseGenericFromEnvCascade(t *testing.T) { } return nil }, - } - a.Run([]string{"run"}) + }).Run([]string{"run"}) } func TestFlagFromFile(t *testing.T) { @@ -1327,10 +1305,10 @@ func TestFlagFromFile(t *testing.T) { t.Error(err) return } - io.WriteString(temp, "abc") - temp.Close() + _, _ = io.WriteString(temp, "abc") + _ = temp.Close() defer func() { - os.Remove(temp.Name()) + _ = os.Remove(temp.Name()) }() var filePathTests = []struct { diff --git a/help.go b/help.go index e504fc2..288e864 100644 --- a/help.go +++ b/help.go @@ -94,7 +94,7 @@ var helpCommand = Command{ return ShowCommandHelp(c, args.First()) } - ShowAppHelp(c) + _ = ShowAppHelp(c) return nil }, } @@ -134,7 +134,7 @@ 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) + _ = ShowAppHelp(c) os.Exit(exitCode) } @@ -168,11 +168,11 @@ func printCommandSuggestions(commands []Command, writer io.Writer) { } if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { for _, name := range command.Names() { - fmt.Fprintf(writer, "%s:%s\n", name, command.Usage) + _, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage) } } else { for _, name := range command.Names() { - fmt.Fprintf(writer, "%s\n", name) + _, _ = fmt.Fprintf(writer, "%s\n", name) } } } @@ -217,7 +217,7 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { // match if last argument matches this flag and it is not repeated if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(flag.GetName()) { flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - fmt.Fprintln(writer, flagCompletion) + _, _ = fmt.Fprintln(writer, flagCompletion) } } } @@ -245,7 +245,7 @@ func DefaultCompleteWithFlags(cmd *Command) func(c *Context) { // ShowCommandHelpAndExit - exits with code after showing help func ShowCommandHelpAndExit(c *Context, command string, code int) { - ShowCommandHelp(c, command) + _ = ShowCommandHelp(c, command) os.Exit(code) } @@ -287,7 +287,7 @@ func ShowVersion(c *Context) { } func printVersion(c *Context) { - fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) + _, _ = fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) } // ShowCompletions prints the lists of commands within a given context @@ -326,11 +326,11 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc m // If the writer is closed, t.Execute will fail, and there's nothing // we can do to recover. if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { - fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) + _, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) } return } - w.Flush() + _ = w.Flush() } func printHelp(out io.Writer, templ string, data interface{}) { @@ -363,7 +363,7 @@ func checkHelp(c *Context) bool { func checkCommandHelp(c *Context, name string) bool { if c.Bool("h") || c.Bool("help") { - ShowCommandHelp(c, name) + _ = ShowCommandHelp(c, name) return true } @@ -372,7 +372,7 @@ func checkCommandHelp(c *Context, name string) bool { func checkSubcommandHelp(c *Context) bool { if c.Bool("h") || c.Bool("help") { - ShowSubcommandHelp(c) + _ = ShowSubcommandHelp(c) return true } diff --git a/help_test.go b/help_test.go index 70b6300..b13eef4 100644 --- a/help_test.go +++ b/help_test.go @@ -16,9 +16,9 @@ func Test_ShowAppHelp_NoAuthor(t *testing.T) { c := NewContext(app, nil, nil) - ShowAppHelp(c) + _ = ShowAppHelp(c) - if bytes.Index(output.Bytes(), []byte("AUTHOR(S):")) != -1 { + if bytes.Contains(output.Bytes(), []byte("AUTHOR(S):")) { t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):") } } @@ -32,9 +32,9 @@ func Test_ShowAppHelp_NoVersion(t *testing.T) { c := NewContext(app, nil, nil) - ShowAppHelp(c) + _ = ShowAppHelp(c) - if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 { + if bytes.Contains(output.Bytes(), []byte("VERSION:")) { t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") } } @@ -48,9 +48,9 @@ func Test_ShowAppHelp_HideVersion(t *testing.T) { c := NewContext(app, nil, nil) - ShowAppHelp(c) + _ = ShowAppHelp(c) - if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 { + if bytes.Contains(output.Bytes(), []byte("VERSION:")) { t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") } } @@ -79,7 +79,7 @@ func Test_Help_Custom_Flags(t *testing.T) { } output := new(bytes.Buffer) app.Writer = output - app.Run([]string{"test", "-h"}) + _ = app.Run([]string{"test", "-h"}) if output.Len() > 0 { t.Errorf("unexpected output: %s", output.String()) } @@ -109,7 +109,7 @@ func Test_Version_Custom_Flags(t *testing.T) { } output := new(bytes.Buffer) app.Writer = output - app.Run([]string{"test", "-v"}) + _ = app.Run([]string{"test", "-v"}) if output.Len() > 0 { t.Errorf("unexpected output: %s", output.String()) } @@ -119,7 +119,7 @@ func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) { app := NewApp() set := flag.NewFlagSet("test", 0) - set.Parse([]string{"foo"}) + _ = set.Parse([]string{"foo"}) c := NewContext(app, set, nil) @@ -147,7 +147,7 @@ func Test_helpCommand_InHelpOutput(t *testing.T) { app := NewApp() output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"test", "--help"}) + _ = app.Run([]string{"test", "--help"}) s := output.String() @@ -164,7 +164,7 @@ func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) { app := NewApp() set := flag.NewFlagSet("test", 0) - set.Parse([]string{"foo"}) + _ = set.Parse([]string{"foo"}) c := NewContext(app, set, nil) @@ -203,7 +203,7 @@ func TestShowAppHelp_CommandAliases(t *testing.T) { output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"foo", "--help"}) + _ = app.Run([]string{"foo", "--help"}) if !strings.Contains(output.String(), "frobbly, fr, frob") { t.Errorf("expected output to include all command aliases; got: %q", output.String()) @@ -225,7 +225,7 @@ func TestShowCommandHelp_CommandAliases(t *testing.T) { output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"foo", "help", "fr"}) + _ = app.Run([]string{"foo", "help", "fr"}) if !strings.Contains(output.String(), "frobbly") { t.Errorf("expected output to include command name; got: %q", output.String()) @@ -251,7 +251,7 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) { output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"foo", "help"}) + _ = app.Run([]string{"foo", "help"}) if !strings.Contains(output.String(), "frobbly, fr, frob, bork") { t.Errorf("expected output to include all command aliases; got: %q", output.String()) @@ -285,7 +285,7 @@ EXAMPLES: } output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"foo", "help", "frobbly"}) + _ = app.Run([]string{"foo", "help", "frobbly"}) if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") { t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String()) @@ -313,7 +313,7 @@ func TestShowSubcommandHelp_CommandUsageText(t *testing.T) { output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"foo", "frobbly", "--help"}) + _ = app.Run([]string{"foo", "frobbly", "--help"}) if !strings.Contains(output.String(), "this is usage text") { t.Errorf("expected output to include usage text; got: %q", output.String()) @@ -337,7 +337,7 @@ func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) { output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"foo", "frobbly", "bobbly", "--help"}) + _ = app.Run([]string{"foo", "frobbly", "bobbly", "--help"}) if !strings.Contains(output.String(), "this is usage text") { t.Errorf("expected output to include usage text; got: %q", output.String()) @@ -365,7 +365,7 @@ func TestShowAppHelp_HiddenCommand(t *testing.T) { output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"app", "--help"}) + _ = app.Run([]string{"app", "--help"}) if strings.Contains(output.String(), "secretfrob") { t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String()) @@ -423,7 +423,7 @@ VERSION: output := &bytes.Buffer{} app.Writer = output - app.Run([]string{"app", "--help"}) + _ = app.Run([]string{"app", "--help"}) if strings.Contains(output.String(), "secretfrob") { t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String()) From fd395786a2c77da3f111c6208ba50b3041fe6ee2 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 7 Aug 2019 21:00:07 +0530 Subject: [PATCH 168/215] Update README.md remove quotes around coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cb12505..bd456b1 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ cli [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) -"[![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli)" +[![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli) This is the library formerly known as `github.com/codegangsta/cli` -- Github will automatically redirect requests to this repository, but we recommend From 0d79d1d9d99db9e380e37034f677523b0ca435b5 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Thu, 8 Aug 2019 14:04:21 +0200 Subject: [PATCH 169/215] Remove unused `Date` variable from `cliTemplate` Signed-off-by: Sascha Grunert --- docs.go | 1 - 1 file changed, 1 deletion(-) diff --git a/docs.go b/docs.go index ef5699a..0beadf2 100644 --- a/docs.go +++ b/docs.go @@ -34,7 +34,6 @@ func (a *App) ToMan() (string, error) { type cliTemplate struct { App *App - Date string Commands []string GlobalArgs []string SynopsisArgs []string From 7506b11da746beef287831f805f5b0e49264b400 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Thu, 8 Aug 2019 15:50:36 +0200 Subject: [PATCH 170/215] Add fish shell completion support This commit adds a new method `ToFishCompletion` to the `*App` which can be used to generate a fish completion string for the application. Relates to: #351 Signed-off-by: Sascha Grunert --- fish.go | 171 +++++++++++++++++++++++++++++++ fish_test.go | 17 +++ template.go | 14 +++ testdata/expected-fish-full.fish | 28 +++++ 4 files changed, 230 insertions(+) create mode 100644 fish.go create mode 100644 fish_test.go create mode 100644 testdata/expected-fish-full.fish diff --git a/fish.go b/fish.go new file mode 100644 index 0000000..0f51065 --- /dev/null +++ b/fish.go @@ -0,0 +1,171 @@ +package cli + +import ( + "bytes" + "fmt" + "io" + "strings" + "text/template" +) + +// ToFishCompletion creates a fish completion string for the `*App` +// The function errors if either parsing or writing of the string fails. +func (a *App) ToFishCompletion() (string, error) { + var w bytes.Buffer + if err := a.writeFishCompletionTemplate(&w); err != nil { + return "", err + } + return w.String(), nil +} + +type fishCompletionTemplate struct { + App *App + Completions []string + AllCommands []string +} + +func (a *App) writeFishCompletionTemplate(w io.Writer) error { + const name = "cli" + t, err := template.New(name).Parse(FishCompletionTemplate) + if err != nil { + return err + } + allCommands := []string{} + + // Add global flags + completions := a.prepareFishFlags(a.VisibleFlags(), allCommands) + + // Add help flag + if !a.HideHelp { + completions = append( + completions, + a.prepareFishFlags([]Flag{HelpFlag}, allCommands)..., + ) + } + + // Add version flag + if !a.HideVersion { + completions = append( + completions, + a.prepareFishFlags([]Flag{VersionFlag}, allCommands)..., + ) + } + + // Add commands and their flags + completions = append( + completions, + a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})..., + ) + + return t.ExecuteTemplate(w, name, &fishCompletionTemplate{ + App: a, + Completions: completions, + AllCommands: allCommands, + }) +} + +func (a *App) prepareFishCommands( + commands []Command, + allCommands *[]string, + previousCommands []string, +) []string { + completions := []string{} + for i := range commands { + command := &commands[i] + + var completion strings.Builder + completion.WriteString(fmt.Sprintf( + "complete -c %s -f -n '%s' -a '%s'", + a.Name, + a.fishSubcommandHelper(previousCommands), + strings.Join(command.Names(), " "), + )) + + if command.Usage != "" { + completion.WriteString(fmt.Sprintf(" -d '%s'", command.Usage)) + } + + if !command.HideHelp { + completions = append( + completions, + a.prepareFishFlags([]Flag{HelpFlag}, command.Names())..., + ) + } + + *allCommands = append(*allCommands, command.Names()...) + completions = append(completions, completion.String()) + completions = append( + completions, + a.prepareFishFlags(command.Flags, command.Names())..., + ) + + // recursevly iterate subcommands + if len(command.Subcommands) > 0 { + completions = append( + completions, + a.prepareFishCommands( + command.Subcommands, allCommands, command.Names(), + )..., + ) + } + } + + return completions +} + +func (a *App) prepareFishFlags( + flags []Flag, + previousCommands []string, +) []string { + completions := []string{} + for _, f := range flags { + flag, ok := f.(DocGenerationFlag) + if !ok { + continue + } + + var completion strings.Builder + completion.WriteString(fmt.Sprintf( + "complete -c %s -f -n '%s'", + a.Name, + a.fishSubcommandHelper(previousCommands), + )) + + for idx, opt := range strings.Split(flag.GetName(), ",") { + if idx == 0 { + completion.WriteString(fmt.Sprintf( + " -l %s", strings.TrimSpace(opt), + )) + } else { + completion.WriteString(fmt.Sprintf( + " -s %s", strings.TrimSpace(opt), + )) + + } + } + + if flag.TakesValue() { + completion.WriteString(" -r") + } + + if flag.GetUsage() != "" { + completion.WriteString(fmt.Sprintf(" -d '%s'", flag.GetUsage())) + } + + completions = append(completions, completion.String()) + } + + return completions +} + +func (a *App) fishSubcommandHelper(allCommands []string) string { + fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name) + if len(allCommands) > 0 { + fishHelper = fmt.Sprintf( + "__fish_seen_subcommand_from %s", + strings.Join(allCommands, " "), + ) + } + return fishHelper + +} diff --git a/fish_test.go b/fish_test.go new file mode 100644 index 0000000..a4c1871 --- /dev/null +++ b/fish_test.go @@ -0,0 +1,17 @@ +package cli + +import ( + "testing" +) + +func TestFishCompletion(t *testing.T) { + // Given + app := testApp() + + // When + res, err := app.ToFishCompletion() + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-fish-full.fish", res) +} diff --git a/template.go b/template.go index 24c44c8..c631fb9 100644 --- a/template.go +++ b/template.go @@ -105,3 +105,17 @@ var MarkdownDocTemplate = `% {{ .App.Name }}(8) {{ .App.Description }} # COMMANDS {{ range $v := .Commands }} {{ $v }}{{ end }}{{ end }}` + +var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion + +function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' + for i in (commandline -opc) + if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} + return 1 + end + end + return 0 +end + +{{ range $v := .Completions }}{{ $v }} +{{ end }}` diff --git a/testdata/expected-fish-full.fish b/testdata/expected-fish-full.fish new file mode 100644 index 0000000..3538c19 --- /dev/null +++ b/testdata/expected-fish-full.fish @@ -0,0 +1,28 @@ +# greet fish shell completion + +function __fish_greet_no_subcommand --description 'Test if there has been any subcommand yet' + for i in (commandline -opc) + if contains -- $i config c sub-config s ss info i in some-command + return 1 + end + end + return 0 +end + +complete -c greet -f -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some usage text' +complete -c greet -f -n '__fish_greet_no_subcommand' -l flag -s fl -s f -r +complete -c greet -f -n '__fish_greet_no_subcommand' -l another-flag -s b -d 'another usage text' +complete -c greet -f -n '__fish_greet_no_subcommand' -l help -s h -d 'show help' +complete -c greet -f -n '__fish_greet_no_subcommand' -l version -s v -d 'print the version' +complete -c greet -f -n '__fish_seen_subcommand_from config c' -l help -s h -d 'show help' +complete -c greet -f -n '__fish_greet_no_subcommand' -a 'config c' -d 'another usage test' +complete -c greet -f -n '__fish_seen_subcommand_from config c' -l flag -s fl -s f -r +complete -c greet -f -n '__fish_seen_subcommand_from config c' -l another-flag -s b -d 'another usage text' +complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l help -s h -d 'show help' +complete -c greet -f -n '__fish_seen_subcommand_from config c' -a 'sub-config s ss' -d 'another usage test' +complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l sub-flag -s sub-fl -s s -r +complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l sub-command-flag -s s -d 'some usage text' +complete -c greet -f -n '__fish_seen_subcommand_from info i in' -l help -s h -d 'show help' +complete -c greet -f -n '__fish_greet_no_subcommand' -a 'info i in' -d 'retrieve generic information' +complete -c greet -f -n '__fish_seen_subcommand_from some-command' -l help -s h -d 'show help' +complete -c greet -f -n '__fish_greet_no_subcommand' -a 'some-command' From e9e9e0ac5dfce215fb2207b3bbde2534a67907f6 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Fri, 9 Aug 2019 09:05:55 +0200 Subject: [PATCH 171/215] Add `TakesFile` indicator to flag This new member of `Flag` indicates if the flag expects a file as input. This is especially useful for documentation and shell completion purposes. Signed-off-by: Sascha Grunert --- flag-gen/assets_vfsdata.go | 4 ++-- flag-gen/main.go | 1 + flag-gen/templates/cli_flags_generated.gotpl | 1 + flag_generated.go | 13 +++++++++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index 2e31627..82095df 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -46,9 +46,9 @@ var assets = func() http.FileSystem { "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", modTime: time.Time{}, - uncompressedSize: 2575, + uncompressedSize: 2591, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x8f\xdb\x36\x10\x3d\x4b\xbf\x62\x6a\xe4\x20\x2d\x5c\xfb\xde\x20\xa7\x75\x36\x0d\x50\x24\x8b\xc6\xcd\x9d\x96\x86\x32\xb1\x34\xa9\x90\xa3\xdd\x2e\x0c\xff\xf7\x62\x48\xea\xc3\x1f\x58\xfb\xb2\x3d\x89\xe4\x0c\xdf\x7b\x7c\x33\xa4\x96\x4b\xb8\xb7\x35\x42\x83\x06\x9d\x20\xac\x61\xf3\x0a\xb2\xf9\x08\xab\xef\xf0\xed\xfb\x1a\x3e\xaf\xbe\xae\x17\x79\xde\x8a\xea\x49\x34\x08\xfb\x3d\x2c\x1e\xe3\xf8\x9b\xd8\x21\x1c\x0e\x79\xae\x76\xad\x75\x04\x45\x9e\xcd\xa4\x16\xcd\x8c\xbf\x3b\xe2\x8f\x27\x57\x59\xf3\xcc\x43\x52\x3b\x9c\xe5\x65\xbe\xdf\x83\x13\xa6\x41\xf8\xa0\xe6\xf0\x81\xf3\xe1\x8f\x4f\xb0\x78\xd0\xa2\xf1\x8c\xb6\x5c\x32\x47\x08\x2c\x12\x03\xc7\x40\x79\x10\x10\xd2\x5f\x14\x6d\x81\x5e\x5b\x1c\x13\xd7\x3c\x3b\x1c\x86\xf9\xca\x56\x24\x94\x66\xbc\xe3\xc4\x29\xa2\x27\xd7\x55\x04\xfb\x3c\xe3\xd5\x2c\xf3\xe4\x94\x69\xf2\xec\x1f\x2f\x9a\xc9\xf4\xb3\x79\xfe\x29\xdc\x38\x7f\x50\x1a\x1f\x05\x6d\x87\x85\xbf\xf1\x57\xa7\x1c\xd6\xd9\xc6\x5a\x9d\x67\x7f\xaa\xba\x46\x93\xa5\xd9\x7e\xff\x3b\x28\x09\xf8\x2b\x09\xf8\x29\x74\x87\x40\xae\x0b\xd6\x65\x61\x9a\x9d\x9e\x23\x6e\x43\x53\x0f\xe3\x29\xc4\x0a\x3d\x29\x23\x48\x59\x33\x02\x4d\x16\xb3\xbb\x37\xf1\x0e\x39\x7b\xfc\x23\x88\x07\x87\xd4\x39\xc3\xd6\x3a\x14\xb5\xd8\x68\x04\x87\xad\x43\x8f\x86\x22\x83\x95\x40\x5b\xe5\xe1\x99\x95\xf2\xce\x42\x5a\x07\x1d\x7b\x04\x35\x4a\xd1\x69\xf2\x65\x2e\x3b\x53\x41\x21\x2f\x1a\x5d\x26\xb2\xa2\x84\x68\x19\x5b\x1e\x89\x81\xe3\x31\x8a\xae\x90\x65\x12\xf7\x05\x29\xec\xef\xd5\xd1\x16\xc1\xf0\x42\x10\x83\xa1\x0d\xae\x50\x26\x88\x4b\x9c\x32\x64\x26\xaa\xaf\xbe\x2f\xdf\xc0\xf6\xb2\x45\xda\xa2\x03\xeb\xc0\x58\x1a\x08\xb9\x03\x5d\xca\xbd\x42\x3e\x82\x16\x25\x70\x1f\x1c\xb1\xf7\xb1\xa4\x60\x2d\x9e\xd0\xc7\xb6\x18\xce\xcb\x55\x9d\x9c\x15\x88\x73\x40\xc4\x22\xcc\xc1\xb2\xbe\x17\xe5\x11\xa4\xd0\x1e\xaf\xa8\x19\x09\xce\xd5\x0c\x3b\xa2\x80\xa1\x3d\xbe\x20\x85\x6b\x70\x54\x82\x58\xf4\xe4\x27\x77\xc1\xed\xb5\x08\x60\x97\x8b\x11\x42\x23\xed\x89\x13\x89\x21\xf5\x1f\x08\xdf\x43\x9c\xb4\xa9\x30\x35\x08\x03\xb8\x6b\xe9\x95\x91\x52\x96\x3a\x33\xd1\xd8\x1e\x8a\x40\x68\xbd\xb8\xae\xbd\xb7\x6e\xd4\x7e\xec\x5a\xba\x4a\x83\x77\xa7\x40\xa0\xad\x7d\xf2\xd0\xb5\x41\x4a\x24\xb7\x12\x04\x68\x5b\x09\x7d\x91\x77\xde\x1b\x70\x84\x77\x6f\x0d\xe1\xbf\xb4\x8a\xb7\x8e\x91\x95\x0c\x2d\x2a\x6d\x67\xfa\x9e\xac\xe0\x2e\xe5\x95\x67\xd0\x45\xb8\x44\xf1\x1c\x21\xca\xfb\x11\x7a\xe0\xf0\x5a\xcc\x66\xd3\x87\x74\x1a\x09\xcb\xa8\xfd\xd1\x4b\x3b\x44\xfa\xf7\x65\x52\x5b\x3e\x77\xd7\x5e\x14\x31\x87\x6a\xc1\x8b\x3f\x90\x86\x4b\xaf\xed\x46\xe8\x9b\xcd\x6b\x42\xfa\x7b\xba\x77\x59\xd0\xff\xe5\xa1\x92\x20\x3d\xff\x1a\xa3\x8b\x51\xcc\x43\xb4\xac\xb7\xb0\xfc\xc8\x39\xbf\x7d\x02\xa3\xc2\xa5\xbe\xc9\x78\xe9\xcb\x3c\x3b\x9c\xbf\x00\x67\xfe\x70\x5d\x82\x2b\x6f\xc0\x25\x23\xe6\xe0\x91\xe0\x2e\x84\x93\xc6\xf7\xf4\x46\xb2\x2f\x1e\x69\xf1\x57\x50\x16\x94\x94\xd1\xb2\xa9\x1b\xa3\x80\x47\xe1\x3c\xba\xc8\xdd\xf2\xb8\x9e\x03\x3a\xc7\x30\x03\x53\xca\x99\xaa\x38\x49\x95\xf1\xbe\x87\xb9\x75\x85\x51\xba\xe4\xdc\xf4\x9b\x66\x7a\x4e\x9c\x08\xb8\xc1\xe3\x8c\x4b\x71\xa6\xf4\x5e\x78\x8a\x6a\x4f\x11\x26\xf1\xa9\xd2\x94\x16\x05\x4f\x44\xdd\x58\xe7\x71\xc7\x7f\x01\x00\x00\xff\xff\xa4\x4d\x6e\xce\x0f\x0a\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x8f\xdb\x36\x10\x3d\x4b\xbf\x62\x6a\xe4\x20\x2d\x5c\xfb\xde\x20\xa7\x75\x36\x0d\x50\x24\x8b\xc6\xcd\x9d\x96\x86\x32\xb1\x34\xa9\x90\xa3\xdd\x2e\x0c\xff\xf7\x62\x48\xea\xc3\x1f\x58\xfb\xb2\x3d\x59\xc3\x19\xbe\xf7\xf8\x66\x48\x2f\x97\x70\x6f\x6b\x84\x06\x0d\x3a\x41\x58\xc3\xe6\x15\x64\xf3\x11\x56\xdf\xe1\xdb\xf7\x35\x7c\x5e\x7d\x5d\x2f\xf2\xbc\x15\xd5\x93\x68\x10\xf6\x7b\x58\x3c\xc6\xef\x6f\x62\x87\x70\x38\xe4\xb9\xda\xb5\xd6\x11\x14\x79\x36\x93\x5a\x34\x33\xfe\xdd\x11\xff\x78\x72\x95\x35\xcf\xfc\x49\x6a\x87\xb3\xbc\xcc\xf7\x7b\x70\xc2\x34\x08\x1f\xd4\x1c\x3e\x70\x3d\xfc\xf1\x09\x16\x0f\x5a\x34\x9e\xd1\x96\x4b\xe6\x08\x89\x45\x62\xe0\x1c\x28\x0f\x02\x42\xf9\x8b\xa2\x2d\xd0\x6b\x8b\x63\xe1\x9a\xa3\xc3\x61\x88\x57\xb6\x22\xa1\x34\xe3\x1d\x17\x4e\x11\x3d\xb9\xae\x22\xd8\xe7\x19\xaf\x66\x99\x27\xa7\x4c\x93\x67\xff\x78\xd1\x4c\xc2\xcf\xe6\xf9\xa7\x70\x63\xfc\xa0\x34\x3e\x0a\xda\x0e\x0b\x7f\xe3\xaf\x4e\x39\xac\xb3\x8d\xb5\x3a\xcf\xfe\x54\x75\x8d\x26\x4b\xd1\x5a\x3c\xa1\xe7\x2d\x29\xde\xef\x7f\x07\x25\x01\x7f\x25\x41\x3f\x85\xee\x10\xc8\x75\xc1\xca\x2c\x84\xd9\xe9\xb9\xe2\x36\x34\xf5\xf0\x3d\x85\x58\xa1\x27\x65\x04\x29\x6b\x46\xa0\xc9\x62\x76\xf7\x26\xde\x21\x67\xcf\x7f\x84\xc3\x80\x43\xea\x9c\x61\xab\x1d\x8a\x5a\x6c\x34\x82\xc3\xd6\xa1\x47\x43\x91\xc1\x4a\xa0\xad\xf2\xf0\xcc\x4a\x79\x67\x21\xad\x83\x8e\x3d\x83\x1a\xa5\xe8\x34\xf9\x32\x97\x9d\xa9\xa0\x90\x17\x8d\x2f\x13\x59\x51\x42\xb4\x90\x5b\x10\x89\x81\xf3\x31\x8b\xae\x90\x65\x12\xf7\x05\x29\xec\xef\xd5\xd1\x16\xc1\xf0\x42\x10\x83\x61\x2c\xae\x50\x26\x88\x4b\x9c\x32\x54\x26\xaa\xaf\xbe\x6f\xe7\xc0\xf6\xb2\x45\xda\xa2\x03\xeb\xc0\x58\x1a\x08\x79\x22\x5d\xaa\xbd\x42\x3e\x82\x16\x25\xf0\x1c\x1c\xb1\xf7\xb9\xa4\x20\x4c\x4c\x1c\x8b\xe1\xbc\xdc\xd5\xc9\x59\x81\xb8\x06\x44\x6c\xc2\x1c\x2c\xeb\x7b\x51\x1e\x41\x0a\xed\xf1\x8a\x9a\x91\xe0\x5c\xcd\xb0\x23\x0a\x18\xc6\xe3\x0b\x52\xb8\x16\x47\x2d\x88\x4d\x4f\x7e\xf2\x14\xdc\xde\x8b\x00\x76\xb9\x19\x21\x35\xd2\x9e\x38\x91\x18\xd2\xfc\x81\xf0\x3d\xc4\xc9\x98\x0a\x53\x83\x30\x80\xbb\x96\x5e\x19\x29\x55\xa9\x33\x13\x8d\xed\xa1\x08\x84\xd6\x8b\xeb\xda\x7b\xeb\x46\xed\xc7\xae\xa5\xab\x34\x78\x77\x0a\x04\xda\xda\x27\x0f\x5d\x1b\xa4\x44\x72\x2b\x41\x80\xb6\x95\xd0\x17\x79\xe7\xbd\x01\x47\x78\xf7\xd6\x10\xfe\x4b\xab\x78\xeb\x18\x59\xc9\x30\xa2\xd2\x76\xa6\x9f\xc9\x0a\xee\x52\x5d\x79\x06\x5d\x84\x4b\x14\xcf\x11\xb2\xbc\x1f\xa1\x07\x0e\xaf\xc5\x6c\x36\x7d\x58\xa7\x99\xb0\x8c\xda\x1f\xbd\xbc\x43\xa6\x7f\x5f\x26\xbd\xe5\x73\x77\xed\x45\x11\x73\xa8\x16\xbc\xf8\x03\x69\xb8\xf4\xda\x6e\x84\xbe\xd9\xbc\x26\x94\xbf\xa7\x7b\x97\x05\xfd\x5f\x1e\x2a\x09\xd2\xf3\x5f\x65\x74\x31\x8a\x79\x88\x96\xf5\x16\x96\x1f\xb9\xe6\xb7\x4f\x60\x54\xb8\xd4\x37\x19\x2f\x7d\x99\x67\x87\xf3\x17\xe0\xcc\x1f\xee\x4b\x70\xe5\x0d\xb8\x64\xc4\x1c\x3c\x12\xdc\x85\x74\xd2\xf8\x9e\xde\x48\xf6\xc5\x23\x2d\xfe\x0a\xca\x82\x92\x32\x5a\x36\x75\x63\x14\xf0\x28\x9c\x47\x17\xb9\x5b\xfe\xae\xe7\x80\xce\x31\xcc\xc0\x94\x6a\xa6\x2a\x4e\x4a\x65\xbc\xef\x21\xb6\xae\x30\x4a\x97\x5c\x9b\xfe\xa6\x99\x9e\x0b\x27\x02\x6e\xf0\x38\xe3\x56\x9c\x29\xbd\x17\x9e\xa2\xda\x53\x84\x49\x7e\xaa\x34\x95\x45\xc1\x13\x51\x37\xf6\x79\xdc\xf1\x5f\x00\x00\x00\xff\xff\xdb\x2f\x5d\x87\x1f\x0a\x00\x00"), }, } fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ diff --git a/flag-gen/main.go b/flag-gen/main.go index 579a7dd..52cc3b1 100644 --- a/flag-gen/main.go +++ b/flag-gen/main.go @@ -28,6 +28,7 @@ type FlagType struct { Parser string `json:"parser"` ParserCast string `json:"parser_cast"` ValueString string `json:"valueString"` + TakesFile bool `json:"takes_file"` } func main() { diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index ffd18ae..08988d3 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -17,6 +17,7 @@ type {{ $flag.Name }}Flag struct { FilePath string Required bool Hidden bool + TakesFile bool {{- if eq $flag.Value true }} Value {{ $flag.Type }} {{- end }} diff --git a/flag_generated.go b/flag_generated.go index af68f95..bae7d12 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -17,6 +17,7 @@ type BoolFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Destination *bool } @@ -87,6 +88,7 @@ type BoolTFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Destination *bool } @@ -157,6 +159,7 @@ type DurationFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value time.Duration Destination *time.Duration } @@ -228,6 +231,7 @@ type Float64Flag struct { FilePath string Required bool Hidden bool + TakesFile bool Value float64 Destination *float64 } @@ -299,6 +303,7 @@ type GenericFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value Generic } @@ -372,6 +377,7 @@ type Int64Flag struct { FilePath string Required bool Hidden bool + TakesFile bool Value int64 Destination *int64 } @@ -443,6 +449,7 @@ type IntFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value int Destination *int } @@ -514,6 +521,7 @@ type IntSliceFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value *IntSlice } @@ -587,6 +595,7 @@ type Int64SliceFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value *Int64Slice } @@ -660,6 +669,7 @@ type StringFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value string Destination *string } @@ -731,6 +741,7 @@ type StringSliceFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value *StringSlice } @@ -804,6 +815,7 @@ type Uint64Flag struct { FilePath string Required bool Hidden bool + TakesFile bool Value uint64 Destination *uint64 } @@ -875,6 +887,7 @@ type UintFlag struct { FilePath string Required bool Hidden bool + TakesFile bool Value uint Destination *uint } From 08c24e22ed2c4bebb348a738caf92c40bb63133c Mon Sep 17 00:00:00 2001 From: FaranIdo Date: Mon, 12 Aug 2019 00:29:46 +0300 Subject: [PATCH 172/215] add missing ExitErrHandler in command + matching test, fixing #816 --- app_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ command.go | 1 + 2 files changed, 55 insertions(+) diff --git a/app_test.go b/app_test.go index 1295467..bccede4 100644 --- a/app_test.go +++ b/app_test.go @@ -2166,3 +2166,57 @@ func TestHandleActionActuallyWorksWithActions(t *testing.T) { t.Errorf("Function was not called") } } + +func TestWhenExitSubCommandWithCodeThenAppQuitUnexpectedly(t *testing.T) { + testCode := 104 + + app := NewApp() + app.Commands = []Command{ + Command{ + Name: "cmd", + Subcommands: []Command{ + Command{ + Name: "subcmd", + Action: func(c *Context) error { + return NewExitError("exit error", testCode) + }, + }, + }, + }, + } + + // set user function as ExitErrHandler + var exitCodeFromExitErrHandler int + app.ExitErrHandler = func(c *Context, err error) { + if exitErr, ok := err.(ExitCoder); ok { + t.Log(exitErr) + exitCodeFromExitErrHandler = exitErr.ExitCode() + } + } + + // keep and restore original OsExiter + origExiter := OsExiter + defer func() { + OsExiter = origExiter + }() + + // set user function as OsExiter + var exitCodeFromOsExiter int + OsExiter = func(exitCode int) { + exitCodeFromOsExiter = exitCode + } + + app.Run([]string{ + "myapp", + "cmd", + "subcmd", + }) + + if exitCodeFromOsExiter != 0 { + t.Errorf("exitCodeFromOsExiter should not change, but its value is %v", exitCodeFromOsExiter) + } + + if exitCodeFromExitErrHandler != testCode { + t.Errorf("exitCodeFromOsExiter valeu should be %v, but its value is %v", testCode, exitCodeFromExitErrHandler) + } +} diff --git a/command.go b/command.go index f55043f..44a90de 100644 --- a/command.go +++ b/command.go @@ -268,6 +268,7 @@ func (c Command) HasName(name string) bool { func (c Command) startApp(ctx *Context) error { app := NewApp() app.Metadata = ctx.App.Metadata + app.ExitErrHandler = ctx.App.ExitErrHandler // set the name and usage app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) if c.HelpName == "" { From 3681b057c5df7f380e75974674a8282cf5632dc3 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Tue, 13 Aug 2019 09:43:57 +0200 Subject: [PATCH 173/215] Update `TakesFile` flag to apply only to supported flags Signed-off-by: Sascha Grunert --- flag-gen/assets_vfsdata.go | 8 ++-- flag-gen/source/flag-types.json | 39 +++++++++++++------- flag-gen/templates/cli_flags_generated.gotpl | 2 + flag_generated.go | 10 ----- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index 82095df..ab600d9 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -28,9 +28,9 @@ var assets = func() http.FileSystem { "/source/flag-types.json": &vfsgenÛ°CompressedFileInfo{ name: "flag-types.json", modTime: time.Time{}, - uncompressedSize: 3364, + uncompressedSize: 3686, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x95\x41\x6f\x9b\x40\x10\x85\xef\xfc\x8a\xe9\x56\x95\xa0\xb2\x20\x87\x28\x07\x4b\xbd\x54\x55\x2b\xdf\x2a\xb9\xe9\x25\x44\xd1\x06\x16\xb2\xea\x7a\x16\x2d\x43\xd4\xc8\xf2\x7f\xaf\x16\x9b\x04\x16\x70\x48\x5b\x2c\x5f\x7c\x78\xe3\xe5\xed\xfb\x66\x47\x73\xe3\x01\x6c\x3d\x00\x00\x86\x7c\x23\xd8\x12\xd8\x67\xad\x15\x5b\xec\x35\x7a\x2a\x6a\xed\xbe\xa5\x3d\x72\x55\x59\x31\xe3\xaa\x14\x07\x2d\x15\x25\xb1\x25\x90\xa9\x1a\x25\xd1\x48\xe2\x37\xdd\xa5\x22\xe3\x95\xb2\x45\x56\x1f\x68\xbe\x52\x70\x53\x0a\x63\xe5\x92\x4c\xa2\xf1\x31\xfc\x6e\x15\x6b\xee\x67\xe1\x4f\xeb\x11\xae\xc9\x48\xcc\xfd\x20\xe8\x58\xef\x55\x7b\xd2\x08\xaa\x0c\x42\xcc\x62\xc6\x3c\x80\xdd\x62\x38\xcc\x8f\x7f\x4f\x93\xea\x84\xb8\x54\xf6\x30\xd0\x03\x27\x90\x65\x5d\x86\xfb\x27\x68\x12\x9e\x57\xf0\x2f\x95\xe1\x24\x35\xba\xd9\x49\x6e\x44\xe8\x16\x1b\x08\xed\xc4\x47\x19\xf8\xa5\x10\xf0\x40\x54\x94\xcb\x28\xca\xb5\xe2\x98\x87\xda\xe4\x51\xf1\x2b\x8f\xac\x43\xf4\xbe\x0e\xd5\xf8\x04\x47\xe0\x5c\xf4\xc1\xd4\x77\xec\x7c\xe0\xad\x64\xdc\xbf\x8f\x51\xfa\xaa\x34\xa7\xab\x4b\x17\x52\xd6\x95\x27\xe1\x99\x16\xad\xd3\xf3\xda\xbd\x17\x6d\x01\x57\x97\xaf\xc5\xdb\x50\xb8\x2e\x8c\x44\xca\xfc\x98\x7d\xc8\x62\xb6\x68\x22\x8f\x46\xfd\x26\x50\x18\x99\xb8\x51\x1d\x79\x3c\x6a\x7b\x40\x06\xb2\xa2\x54\x6e\x93\x1b\x0b\x89\x24\x4c\xc6\x13\xb1\xdd\x8d\xc4\x92\x59\x73\x7d\x78\xf7\x09\x50\x2a\xd8\xc6\x18\x53\x4c\x23\xed\xb4\xc5\x9d\xfd\x99\x30\x08\x2b\x1c\x68\xb0\xc4\x13\xb4\x77\x85\x43\xcd\xbd\x78\x7b\x7f\xd3\x29\xfd\x5d\x21\x0d\xa4\x3c\x87\x8c\xfb\xa3\x77\x09\xaf\xfd\xec\xad\xfc\x5a\x4a\xe7\xa1\xb0\x56\x32\x11\x2e\x8a\x8f\x6e\x61\x8e\x87\x7e\x73\xdb\x22\xfe\x02\xec\x19\x90\xff\x7c\x8b\x20\xd8\x4b\x16\x96\x30\x46\x1b\x1f\xa5\x1a\xc3\x31\xfb\x74\x8c\x12\x73\x4a\xb3\x31\x7b\x99\xc5\x31\x6a\x87\x9b\x9c\x0d\xb7\x83\x8b\xc3\xac\xec\xa8\x7f\x3b\x73\xb5\x6f\x8f\x47\x7f\xca\x5e\x05\xd0\xcd\x79\x3c\xca\xf0\x1b\x18\xa8\xcd\xf3\x08\xba\xe4\x06\x5f\x41\xeb\x2e\x67\xf3\x0c\xae\xe5\xd0\x76\xa9\x4e\xb2\x5e\xac\xf7\x09\xf7\xcb\xb5\xec\x2f\x98\xea\x04\x1b\x66\x52\x4c\x67\xc5\x54\xff\x6f\xc7\x78\xb7\xde\x9f\x00\x00\x00\xff\xff\x61\x79\x63\x47\x24\x0d\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x95\x41\x6f\x9b\x40\x10\x85\xef\xfc\x8a\xe9\x56\x95\xa0\xb2\x20\x07\xcb\x07\x4b\xbd\x54\x55\x2b\xdf\x2a\xb9\xe9\x25\x44\xd6\x06\x16\xb2\xca\x7a\x17\x2d\x43\xd4\xc8\xf2\x7f\xaf\x16\x8c\x0d\x6b\xb0\x4d\x23\x23\xf7\x92\xc3\x9b\x2c\x33\xef\xcb\x9b\xcc\x83\x03\xb0\x71\x00\x00\x88\xa4\x6b\x46\xe6\x40\xbe\x2a\x25\xc8\xa4\xd2\xf0\x2d\x2b\xb5\xa7\x86\xf6\x4a\x45\x61\xc4\x84\x8a\x9c\xed\xb4\x98\xe5\x48\xe6\x80\xba\xa8\x95\x48\x49\x64\x7f\x70\x15\xb3\x84\x16\xc2\x14\x49\xf9\xa0\xfe\x4a\x46\x75\xce\xb4\x91\x73\xd4\x91\x92\xaf\xfe\x4f\xa3\x98\xe6\x6e\xe2\xff\x36\x3d\xfc\x25\x6a\x2e\x53\xd7\xf3\x5a\xad\x2b\xd5\xbc\xd4\x0c\x0b\x2d\x21\x24\x21\xd9\x0f\x4c\x5f\x58\xbe\x4a\xb8\xd8\x4f\xe8\x00\x6c\x27\xdd\x2e\x7f\xbd\xdf\x66\xac\x22\xa4\x5c\x98\xc7\x80\xcf\x14\x81\xe7\x65\x19\x9e\xde\xa0\xb6\xfe\x9f\x10\xf9\x56\x68\x8a\x5c\x49\x1b\x0a\xf2\x35\xf3\xed\x62\x4d\xa7\x89\xe2\x24\x1c\x37\x67\x0c\x9e\x11\xb3\x7c\x1e\x04\xa9\x12\x54\xa6\xbe\xd2\x69\x90\xbd\xa4\x81\xe9\x10\x7c\x2c\xdd\xd6\x7d\xbc\x13\xd4\xee\x8e\x89\x95\x33\xb6\x3e\x30\x14\x99\xfd\xeb\x83\xf1\x7d\x17\x8a\xe2\x6c\x6a\xd3\x4b\xda\xf2\x45\xdc\x2e\xf3\xdc\x4a\x49\xd9\xfd\xc8\xf3\x04\x66\xd3\x73\xbe\xd7\xe8\x2f\x33\xcd\x25\x26\x6e\x48\x3e\x25\x21\x99\xd4\x2c\x86\x33\xf8\xc1\x24\xd3\x3c\xb2\x19\x58\x72\x3f\x83\xe6\xae\x75\x40\x90\x5c\xd8\xb1\xa8\x5b\x70\x89\x4c\x27\x34\x62\x9b\x6d\x8f\x5f\x9e\xd4\xbe\xe0\xc3\x17\x90\x5c\xc0\x26\x94\x21\x86\xd8\x13\x00\x53\xdc\x9a\x1f\xe7\x76\xca\xd8\xe8\xe1\xb1\x90\x1d\x89\xe0\x72\x84\x3c\x2c\x64\x57\x1a\xee\x86\x07\x22\x7e\x57\x20\x16\x12\x3b\xec\xdf\x82\xf9\xea\xe9\x2a\xa2\x65\x3f\x33\x95\x5b\x4a\xf1\xc8\x78\x96\x82\x47\xcc\x66\xf4\xd9\x2e\x5c\x63\x65\x1e\x1e\x1b\x7f\x8a\x03\xc9\x3d\x39\x77\x3f\x85\xe7\x55\x92\xa1\xc8\xb4\x56\xda\x95\x5c\xf4\x71\xba\xda\x9e\x9d\x01\x39\x9b\xf6\xa2\xb4\x4a\x57\x83\x79\x58\xeb\x3e\x9c\xbb\x49\x6e\x1f\xe8\xae\xbd\x05\x33\x6f\xa9\xff\xba\xbe\xcd\x81\x0e\xa0\x8e\x17\xf6\x2c\x99\x36\x80\xa1\xff\x9c\xab\xef\x74\x87\xa6\xa3\x76\x9d\xd4\xb4\x89\x76\xc6\xa6\x31\xcb\x6d\xe4\xe6\x04\xd3\x7b\xde\x75\xf1\x8a\x51\x4e\x9e\xe9\x7d\x0b\x37\xef\x9e\x1f\x1f\xbd\x62\x84\xab\x77\x91\x7f\xeb\xec\x15\x23\xdc\x3d\xe7\xd1\xf9\x1b\x00\x00\xff\xff\xc7\xe4\x19\x16\x66\x0e\x00\x00"), }, "/templates": &vfsgenÛ°DirInfo{ name: "templates", @@ -46,9 +46,9 @@ var assets = func() http.FileSystem { "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", modTime: time.Time{}, - uncompressedSize: 2591, + uncompressedSize: 2638, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x8f\xdb\x36\x10\x3d\x4b\xbf\x62\x6a\xe4\x20\x2d\x5c\xfb\xde\x20\xa7\x75\x36\x0d\x50\x24\x8b\xc6\xcd\x9d\x96\x86\x32\xb1\x34\xa9\x90\xa3\xdd\x2e\x0c\xff\xf7\x62\x48\xea\xc3\x1f\x58\xfb\xb2\x3d\x59\xc3\x19\xbe\xf7\xf8\x66\x48\x2f\x97\x70\x6f\x6b\x84\x06\x0d\x3a\x41\x58\xc3\xe6\x15\x64\xf3\x11\x56\xdf\xe1\xdb\xf7\x35\x7c\x5e\x7d\x5d\x2f\xf2\xbc\x15\xd5\x93\x68\x10\xf6\x7b\x58\x3c\xc6\xef\x6f\x62\x87\x70\x38\xe4\xb9\xda\xb5\xd6\x11\x14\x79\x36\x93\x5a\x34\x33\xfe\xdd\x11\xff\x78\x72\x95\x35\xcf\xfc\x49\x6a\x87\xb3\xbc\xcc\xf7\x7b\x70\xc2\x34\x08\x1f\xd4\x1c\x3e\x70\x3d\xfc\xf1\x09\x16\x0f\x5a\x34\x9e\xd1\x96\x4b\xe6\x08\x89\x45\x62\xe0\x1c\x28\x0f\x02\x42\xf9\x8b\xa2\x2d\xd0\x6b\x8b\x63\xe1\x9a\xa3\xc3\x61\x88\x57\xb6\x22\xa1\x34\xe3\x1d\x17\x4e\x11\x3d\xb9\xae\x22\xd8\xe7\x19\xaf\x66\x99\x27\xa7\x4c\x93\x67\xff\x78\xd1\x4c\xc2\xcf\xe6\xf9\xa7\x70\x63\xfc\xa0\x34\x3e\x0a\xda\x0e\x0b\x7f\xe3\xaf\x4e\x39\xac\xb3\x8d\xb5\x3a\xcf\xfe\x54\x75\x8d\x26\x4b\xd1\x5a\x3c\xa1\xe7\x2d\x29\xde\xef\x7f\x07\x25\x01\x7f\x25\x41\x3f\x85\xee\x10\xc8\x75\xc1\xca\x2c\x84\xd9\xe9\xb9\xe2\x36\x34\xf5\xf0\x3d\x85\x58\xa1\x27\x65\x04\x29\x6b\x46\xa0\xc9\x62\x76\xf7\x26\xde\x21\x67\xcf\x7f\x84\xc3\x80\x43\xea\x9c\x61\xab\x1d\x8a\x5a\x6c\x34\x82\xc3\xd6\xa1\x47\x43\x91\xc1\x4a\xa0\xad\xf2\xf0\xcc\x4a\x79\x67\x21\xad\x83\x8e\x3d\x83\x1a\xa5\xe8\x34\xf9\x32\x97\x9d\xa9\xa0\x90\x17\x8d\x2f\x13\x59\x51\x42\xb4\x90\x5b\x10\x89\x81\xf3\x31\x8b\xae\x90\x65\x12\xf7\x05\x29\xec\xef\xd5\xd1\x16\xc1\xf0\x42\x10\x83\x61\x2c\xae\x50\x26\x88\x4b\x9c\x32\x54\x26\xaa\xaf\xbe\x6f\xe7\xc0\xf6\xb2\x45\xda\xa2\x03\xeb\xc0\x58\x1a\x08\x79\x22\x5d\xaa\xbd\x42\x3e\x82\x16\x25\xf0\x1c\x1c\xb1\xf7\xb9\xa4\x20\x4c\x4c\x1c\x8b\xe1\xbc\xdc\xd5\xc9\x59\x81\xb8\x06\x44\x6c\xc2\x1c\x2c\xeb\x7b\x51\x1e\x41\x0a\xed\xf1\x8a\x9a\x91\xe0\x5c\xcd\xb0\x23\x0a\x18\xc6\xe3\x0b\x52\xb8\x16\x47\x2d\x88\x4d\x4f\x7e\xf2\x14\xdc\xde\x8b\x00\x76\xb9\x19\x21\x35\xd2\x9e\x38\x91\x18\xd2\xfc\x81\xf0\x3d\xc4\xc9\x98\x0a\x53\x83\x30\x80\xbb\x96\x5e\x19\x29\x55\xa9\x33\x13\x8d\xed\xa1\x08\x84\xd6\x8b\xeb\xda\x7b\xeb\x46\xed\xc7\xae\xa5\xab\x34\x78\x77\x0a\x04\xda\xda\x27\x0f\x5d\x1b\xa4\x44\x72\x2b\x41\x80\xb6\x95\xd0\x17\x79\xe7\xbd\x01\x47\x78\xf7\xd6\x10\xfe\x4b\xab\x78\xeb\x18\x59\xc9\x30\xa2\xd2\x76\xa6\x9f\xc9\x0a\xee\x52\x5d\x79\x06\x5d\x84\x4b\x14\xcf\x11\xb2\xbc\x1f\xa1\x07\x0e\xaf\xc5\x6c\x36\x7d\x58\xa7\x99\xb0\x8c\xda\x1f\xbd\xbc\x43\xa6\x7f\x5f\x26\xbd\xe5\x73\x77\xed\x45\x11\x73\xa8\x16\xbc\xf8\x03\x69\xb8\xf4\xda\x6e\x84\xbe\xd9\xbc\x26\x94\xbf\xa7\x7b\x97\x05\xfd\x5f\x1e\x2a\x09\xd2\xf3\x5f\x65\x74\x31\x8a\x79\x88\x96\xf5\x16\x96\x1f\xb9\xe6\xb7\x4f\x60\x54\xb8\xd4\x37\x19\x2f\x7d\x99\x67\x87\xf3\x17\xe0\xcc\x1f\xee\x4b\x70\xe5\x0d\xb8\x64\xc4\x1c\x3c\x12\xdc\x85\x74\xd2\xf8\x9e\xde\x48\xf6\xc5\x23\x2d\xfe\x0a\xca\x82\x92\x32\x5a\x36\x75\x63\x14\xf0\x28\x9c\x47\x17\xb9\x5b\xfe\xae\xe7\x80\xce\x31\xcc\xc0\x94\x6a\xa6\x2a\x4e\x4a\x65\xbc\xef\x21\xb6\xae\x30\x4a\x97\x5c\x9b\xfe\xa6\x99\x9e\x0b\x27\x02\x6e\xf0\x38\xe3\x56\x9c\x29\xbd\x17\x9e\xa2\xda\x53\x84\x49\x7e\xaa\x34\x95\x45\xc1\x13\x51\x37\xf6\x79\xdc\xf1\x5f\x00\x00\x00\xff\xff\xdb\x2f\x5d\x87\x1f\x0a\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x6f\xe3\x36\x10\x3d\x4b\xbf\x62\x6a\xec\x41\x0a\x5c\xfb\xde\xc5\x9e\xe2\xcd\x76\x81\x62\x37\xe8\xa6\xb9\xd3\xd2\x50\x26\x42\x93\x0a\x39\x4a\x1a\x18\xfe\xef\xc5\x90\xd4\x87\x3f\x10\xfb\x92\x9e\x2c\x72\x66\xde\x7b\x7c\x33\xa4\x97\x4b\xb8\xb5\x35\x42\x83\x06\x9d\x20\xac\x61\xfd\x06\xb2\xf9\x0c\xab\x9f\xf0\xe3\xe7\x03\x7c\x5d\x7d\x7f\x58\xe4\x79\x2b\xaa\x27\xd1\x20\xec\x76\xb0\xb8\x8f\xdf\x3f\xc4\x16\x61\xbf\xcf\x73\xb5\x6d\xad\x23\x28\xf2\x6c\x26\xb5\x68\x66\xfc\xbb\x25\xfe\xf1\xe4\x2a\x6b\x5e\xf8\x93\xd4\x16\x67\x79\x99\xef\x76\xe0\x84\x69\x10\x3e\xa9\x39\x7c\xe2\x7c\xf8\xe3\x0b\x2c\xee\xb4\x68\x3c\xa3\x2d\x97\xcc\x11\x02\x8b\xc4\xc0\x31\x50\x1e\x04\x84\xf4\x57\x45\x1b\xa0\xb7\x16\xc7\xc4\x07\x5e\xed\xf7\xc3\x7a\x65\x2b\x12\x4a\x33\xde\x61\xe2\x14\xd1\x93\xeb\x2a\x82\x5d\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\x8f\x17\xcd\x64\xf9\xd5\xbc\x3c\x0a\x37\xae\xef\x94\xc6\x7b\x41\x9b\x61\xe3\x6f\x7c\xee\x94\xc3\x3a\x5b\x5b\xab\xf3\xec\x4f\x55\xd7\x68\xb2\xb4\xda\xed\x7e\x07\x25\x01\x9f\x7b\xa5\xe2\x09\x3d\x43\x00\xb9\x2e\xd8\x97\x0d\x5b\x93\x12\x34\x75\x88\x1d\x97\x3f\x0a\xdd\x4d\x4a\xc3\x32\x3b\xb6\xe1\x7d\x88\x15\x7a\x52\x46\x90\xb2\x66\x04\x9a\x6c\x66\x37\xef\xe2\xed\x73\x6e\xd1\xaf\x70\x76\x70\x48\x9d\x33\xdc\x19\x87\xa2\x16\x6b\x8d\xe0\xb0\x75\xe8\xd1\x50\x64\xb0\x12\x68\xa3\x3c\xbc\xb0\x52\xae\x2c\xa4\x75\xd0\xb1\xc5\x50\xa3\x14\x9d\x26\x5f\xe6\xb2\x33\x15\x14\xf2\x6c\x9f\xca\x44\x56\x94\x10\x1d\xe7\x8e\x45\x62\xe0\x78\x8c\xa2\x2b\x64\x99\xc4\x7d\x43\x0a\xf5\xbd\x3a\xda\x20\x18\xde\x08\x62\x30\x4c\xd1\x05\xca\x04\x71\x8e\x53\x86\xcc\x44\xf5\xdd\xf7\xdd\x1f\xd8\x5e\x37\x48\x1b\x74\x60\x1d\x18\x4b\x03\x21\x0f\xb0\x4b\xb9\x17\xc8\x47\xd0\xa2\x04\x9e\x89\x03\xf6\x3e\x96\x14\x84\xe9\x89\x63\x31\x9c\x97\xbb\x3a\x39\x2b\x10\xe7\x80\x88\x4d\x98\x83\x65\x7d\xaf\xca\x23\x48\xa1\x3d\x5e\x50\x33\x12\x9c\xaa\x19\x2a\xa2\x80\x61\x3c\xbe\x21\x85\x5b\x74\xd0\x82\xd8\xf4\xe4\x27\x4f\xc1\xf5\xbd\x08\x60\xe7\x9b\x11\x42\x23\xed\x91\x13\x89\x21\xcd\x1f\x08\xdf\x43\x1c\x8d\xa9\x30\x35\x08\x03\xb8\x6d\xe9\x8d\x91\x52\x96\x3a\x31\xd1\xd8\x1e\x8a\x40\x68\xbd\xb8\xac\xbd\xb7\x6e\xd4\x7e\xe8\x5a\xba\x4a\x83\x77\xc7\x40\xa0\xad\x7d\xf2\xd0\xb5\x41\x4a\x24\xb7\x12\x04\x68\x5b\x09\x7d\x96\x77\xde\x1b\x70\x80\x77\x6b\x0d\xe1\xbf\xb4\x8a\xb7\x8e\x91\x95\x0c\x23\x2a\x6d\x67\xfa\x99\xac\xe0\x26\xe5\x95\x27\xd0\x45\xb8\x44\xf1\x1c\x21\xca\xf5\x08\x3d\x70\x78\x2d\x66\xb3\xe9\x3b\x3c\x8d\x84\x6d\xd4\xfe\xe0\xa1\x1e\x22\xfd\xfb\x32\xe9\x2d\x9f\xbb\x6b\xcf\x8a\x98\x43\xb5\xe0\xcd\x5f\x48\xc3\xa5\xd7\x76\x2d\xf4\xd5\xe6\x35\x21\xfd\x23\xdd\x3b\x2f\xe8\xff\xf2\x50\x49\x90\x9e\xff\x59\xa3\x8b\x51\xcc\x5d\xb4\xac\xb7\xb0\xfc\xcc\x39\xbf\x7d\x01\xa3\xc2\xa5\xbe\xca\x78\xe9\xcb\x3c\xdb\x9f\xbe\x00\x27\xfe\x70\x5f\x82\x2b\xef\xc0\x25\x23\xe6\xe0\x91\xe0\x26\x84\x93\xc6\x8f\xf4\x46\xb2\x2f\x1e\x69\xf1\x57\x50\x16\x94\x94\xd1\xb2\xa9\x1b\xa3\x80\x7b\xe1\x3c\xba\xc8\xdd\xf2\x77\x3d\x07\x74\x8e\x61\x06\xa6\x94\x33\x55\x71\x94\x2a\xe3\x7d\x0f\x6b\xeb\x0a\xa3\x74\xc9\xb9\xe9\x6f\x9a\xe9\x39\x71\x22\xe0\x0a\x8f\x33\x6e\xc5\x89\xd2\x5b\xe1\x29\xaa\x3d\x46\x98\xc4\xa7\x4a\x53\x5a\x14\x3c\x11\x75\x65\x9f\xc7\x8a\xff\x02\x00\x00\xff\xff\x73\xc9\x52\x5f\x4e\x0a\x00\x00"), }, } fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ diff --git a/flag-gen/source/flag-types.json b/flag-gen/source/flag-types.json index 7ec5ddd..472e572 100644 --- a/flag-gen/source/flag-types.json +++ b/flag-gen/source/flag-types.json @@ -6,7 +6,8 @@ "dest": true, "context_default": "false", "parser": "strconv.ParseBool(f.Value.String())", - "valueString": "return \"\"" + "valueString": "return \"\"", + "takes_file": false }, { "name": "BoolT", @@ -16,7 +17,8 @@ "doctail": " that is true by default", "context_default": "false", "parser": "strconv.ParseBool(f.Value.String())", - "valueString": "return \"\"" + "valueString": "return \"\"", + "takes_file": false }, { "name": "Duration", @@ -26,7 +28,8 @@ "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", "context_default": "0", "parser": "time.ParseDuration(f.Value.String())", - "valueString": "return f.Value.String()" + "valueString": "return f.Value.String()", + "takes_file": false }, { "name": "Float64", @@ -35,7 +38,8 @@ "dest": true, "context_default": "0", "parser": "strconv.ParseFloat(f.Value.String(), 64)", - "valueString": "return fmt.Sprintf(\"%f\", f.Value)" + "valueString": "return fmt.Sprintf(\"%f\", f.Value)", + "takes_file": false }, { "name": "Generic", @@ -44,7 +48,8 @@ "dest": false, "context_default": "nil", "context_type": "interface{}", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", + "takes_file": true }, { "name": "Int64", @@ -53,7 +58,8 @@ "dest": true, "context_default": "0", "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)" + "valueString": "return fmt.Sprintf(\"%d\", f.Value)", + "takes_file": false }, { "name": "Int", @@ -63,7 +69,8 @@ "context_default": "0", "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", "parser_cast": "int(parsed)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)" + "valueString": "return fmt.Sprintf(\"%d\", f.Value)", + "takes_file": false }, { "name": "IntSlice", @@ -73,7 +80,8 @@ "context_default": "nil", "context_type": "[]int", "parser": "(f.Value.(*IntSlice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", + "takes_file": false }, { "name": "Int64Slice", @@ -83,7 +91,8 @@ "context_default": "nil", "context_type": "[]int64", "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", + "takes_file": false }, { "name": "String", @@ -92,7 +101,8 @@ "dest": true, "context_default": "\"\"", "parser": "f.Value.String(), error(nil)", - "valueString": "return f.Value" + "valueString": "return f.Value", + "takes_file": true }, { "name": "StringSlice", @@ -102,7 +112,8 @@ "context_default": "nil", "context_type": "[]string", "parser": "(f.Value.(*StringSlice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"" + "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", + "takes_file": true }, { "name": "Uint64", @@ -111,7 +122,8 @@ "dest": true, "context_default": "0", "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)" + "valueString": "return fmt.Sprintf(\"%d\", f.Value)", + "takes_file": false }, { "name": "Uint", @@ -121,6 +133,7 @@ "context_default": "0", "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", "parser_cast": "uint(parsed)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)" + "valueString": "return fmt.Sprintf(\"%d\", f.Value)", + "takes_file": false } ] diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index 08988d3..61d06f0 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -17,7 +17,9 @@ type {{ $flag.Name }}Flag struct { FilePath string Required bool Hidden bool + {{- if eq $flag.TakesFile true }} TakesFile bool + {{- end }} {{- if eq $flag.Value true }} Value {{ $flag.Type }} {{- end }} diff --git a/flag_generated.go b/flag_generated.go index bae7d12..9dcdfd6 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -17,7 +17,6 @@ type BoolFlag struct { FilePath string Required bool Hidden bool - TakesFile bool Destination *bool } @@ -88,7 +87,6 @@ type BoolTFlag struct { FilePath string Required bool Hidden bool - TakesFile bool Destination *bool } @@ -159,7 +157,6 @@ type DurationFlag struct { FilePath string Required bool Hidden bool - TakesFile bool Value time.Duration Destination *time.Duration } @@ -231,7 +228,6 @@ type Float64Flag struct { FilePath string Required bool Hidden bool - TakesFile bool Value float64 Destination *float64 } @@ -377,7 +373,6 @@ type Int64Flag struct { FilePath string Required bool Hidden bool - TakesFile bool Value int64 Destination *int64 } @@ -449,7 +444,6 @@ type IntFlag struct { FilePath string Required bool Hidden bool - TakesFile bool Value int Destination *int } @@ -521,7 +515,6 @@ type IntSliceFlag struct { FilePath string Required bool Hidden bool - TakesFile bool Value *IntSlice } @@ -595,7 +588,6 @@ type Int64SliceFlag struct { FilePath string Required bool Hidden bool - TakesFile bool Value *Int64Slice } @@ -815,7 +807,6 @@ type Uint64Flag struct { FilePath string Required bool Hidden bool - TakesFile bool Value uint64 Destination *uint64 } @@ -887,7 +878,6 @@ type UintFlag struct { FilePath string Required bool Hidden bool - TakesFile bool Value uint Destination *uint } From f2c26bab772e6b69a9fca945534728678578eb2b Mon Sep 17 00:00:00 2001 From: russoj88 Date: Tue, 13 Aug 2019 21:10:38 -0700 Subject: [PATCH 174/215] Rewrite the "pinning to v1" section. --- README.md | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 29ed171..6748476 100644 --- a/README.md +++ b/README.md @@ -108,25 +108,12 @@ import ( ... ``` -### Pinning to the `v1` releases +### Using `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 `v1` is an acceptable option, e.g.: +Use the github URL to use the v1 branch. This is illustrated in the Getting Started section below. -``` -$ go get gopkg.in/urfave/cli.v1 -``` - -``` go -... -import ( - "gopkg.in/urfave/cli.v1" // imports as package "cli" -) -... -``` +Do not use the gopkg.in URL as it does not work for versions >= 1.21.0. -This will pull the latest tagged `v1` release (e.g. `v1.18.1` at the time of writing). ## Getting Started From f529dad70caa6e307f95eecb2db6f16efc0f964d Mon Sep 17 00:00:00 2001 From: russoj88 Date: Wed, 14 Aug 2019 11:20:58 -0700 Subject: [PATCH 175/215] Update README.md Include suggestion to put example in. Co-Authored-By: Lynn Cyrin (they/them) --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6748476..9ecef10 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,13 @@ import ( ### Using `v1` releases -Use the github URL to use the v1 branch. This is illustrated in the Getting Started section below. +$ go get github.com/urfave/cli + +package main + +import ( + "github.com/urfave/cli" +) Do not use the gopkg.in URL as it does not work for versions >= 1.21.0. From cc091db561b137c49cbf370766a94b47cfdae182 Mon Sep 17 00:00:00 2001 From: russoj88 Date: Wed, 14 Aug 2019 11:21:40 -0700 Subject: [PATCH 176/215] Update README.md Only instruct on right way to use library. Co-Authored-By: Lynn Cyrin (they/them) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9ecef10..f611bfa 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,6 @@ import ( "github.com/urfave/cli" ) -Do not use the gopkg.in URL as it does not work for versions >= 1.21.0. ## Getting Started From 62b8a7cc2cb05b1a454908087b35e5780a1d12ad Mon Sep 17 00:00:00 2001 From: russoj88 Date: Wed, 14 Aug 2019 11:20:09 -0700 Subject: [PATCH 177/215] Add "Using v1 releases" to table of contents. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f611bfa..224224c 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` releases](#pinning-to-the-v1-releases) + * [Using `v1` releases](#using-v1-releases) - [Getting Started](#getting-started) - [Examples](#examples) * [Arguments](#arguments) From 3bc62c4fde03e107cad02f8828780470258b8fc0 Mon Sep 17 00:00:00 2001 From: russoj88 Date: Thu, 15 Aug 2019 12:30:29 -0700 Subject: [PATCH 178/215] Mimic v2 example code from above. --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 224224c..cbb0b37 100644 --- a/README.md +++ b/README.md @@ -110,14 +110,17 @@ import ( ### Using `v1` releases +``` $ go get github.com/urfave/cli +``` -package main - +```go +... import ( "github.com/urfave/cli" ) - +... +``` ## Getting Started From e11183fe50e3b3d75e481b1262e3222c565ba8bf Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 17 Aug 2019 02:44:49 -0700 Subject: [PATCH 179/215] Modernize readme I assume that people no longer care about what the package was named many years ago --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 29ed171..70137ec 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,6 @@ cli [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) [![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli) -This is the library formerly known as `github.com/codegangsta/cli` -- Github -will automatically redirect requests to this repository, but we recommend -updating your references for clarity. - cli is a simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. From 8469a9de07c45435b61cbfd4aed7167fb9e59cca Mon Sep 17 00:00:00 2001 From: "[[ BOT ]] Lynn Cyrin" Date: Thu, 22 Aug 2019 21:42:07 -0700 Subject: [PATCH 180/215] show test failures --- build.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/build.go b/build.go index 2a38f8b..125d25f 100644 --- a/build.go +++ b/build.go @@ -6,12 +6,13 @@ import ( "bufio" "bytes" "fmt" - "github.com/urfave/cli" "io/ioutil" "log" "os" "os/exec" "strings" + + "github.com/urfave/cli" ) var packages = []string{"cli", "altsrc"} @@ -52,7 +53,13 @@ func main() { } func VetActionFunc(_ *cli.Context) error { - return exec.Command("go", "vet").Run() + cmd := exec.Command("go", "vet") + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() } func TestActionFunc(c *cli.Context) error { @@ -67,10 +74,13 @@ func TestActionFunc(c *cli.Context) error { coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg) - err := exec.Command( - "go", "test", "-v", coverProfile, packageName, - ).Run() + cmd := exec.Command("go", "test", "-v", coverProfile, packageName) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() if err != nil { return err } @@ -142,16 +152,34 @@ func GfmrunActionFunc(_ *cli.Context) error { return err } - return exec.Command("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md").Run() + cmd := exec.Command("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md") + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() } func TocActionFunc(_ *cli.Context) error { - err := exec.Command("node_modules/.bin/markdown-toc", "-i", "README.md").Run() + cmd := exec.Command("node_modules/.bin/markdown-toc", "-i", "README.md") + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() if err != nil { return err } - err = exec.Command("git", "diff", "--exit-code").Run() + cmd = exec.Command("git", "diff", "--exit-code") + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = cmd.Run() if err != nil { return err } @@ -160,17 +188,35 @@ func TocActionFunc(_ *cli.Context) error { } func GenActionFunc(_ *cli.Context) error { - err := exec.Command("go", "generate", "flag-gen/main.go").Run() + cmd := exec.Command("go", "generate", "flag-gen/main.go") + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err := cmd.Run() if err != nil { return err } - err = exec.Command("go", "generate", "cli.go").Run() + cmd = exec.Command("go", "generate", "cli.go") + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = cmd.Run() if err != nil { return err } - err = exec.Command("git", "diff", "--exit-code").Run() + cmd = exec.Command("git", "diff", "--exit-code") + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = cmd.Run() if err != nil { return err } From 487c7236736db7d5d2e46633d09453d0e149a0bd Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Fri, 23 Aug 2019 10:28:32 +0200 Subject: [PATCH 181/215] Cleanup go modules These two dependencies are not really needed, which can be reproduced via: ``` > export GO111MODULE=on && go mod tidy ``` Signed-off-by: Sascha Grunert --- go.mod | 2 -- go.sum | 4 ---- 2 files changed, 6 deletions(-) diff --git a/go.mod b/go.mod index 91106bc..1b94cfe 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,5 @@ go 1.10 require ( github.com/BurntSushi/toml v0.3.1 github.com/cpuguy83/go-md2man v1.0.10 - github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect - github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd // indirect gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index 2b8b863..a8e436a 100644 --- a/go.sum +++ b/go.sum @@ -4,10 +4,6 @@ github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= From 29ad6ee6ad7e02dbec3334e8843bb6711c011b55 Mon Sep 17 00:00:00 2001 From: "[[ BOT ]] Lynn Cyrin" Date: Fri, 23 Aug 2019 20:09:08 -0700 Subject: [PATCH 182/215] DRY --- build.go | 64 ++++++++++++-------------------------------------------- 1 file changed, 13 insertions(+), 51 deletions(-) diff --git a/build.go b/build.go index 125d25f..4572d28 100644 --- a/build.go +++ b/build.go @@ -52,8 +52,8 @@ func main() { } } -func VetActionFunc(_ *cli.Context) error { - cmd := exec.Command("go", "vet") +func runCmd(args ...string) error { + cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout @@ -62,6 +62,10 @@ func VetActionFunc(_ *cli.Context) error { return cmd.Run() } +func VetActionFunc(_ *cli.Context) error { + return runCmd("go", "vet") +} + func TestActionFunc(c *cli.Context) error { for _, pkg := range packages { var packageName string @@ -74,13 +78,7 @@ func TestActionFunc(c *cli.Context) error { coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg) - cmd := exec.Command("go", "test", "-v", coverProfile, packageName) - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := cmd.Run() + err := runCmd("go", "test", "-v", coverProfile, packageName) if err != nil { return err } @@ -152,34 +150,16 @@ func GfmrunActionFunc(_ *cli.Context) error { return err } - cmd := exec.Command("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md") - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - return cmd.Run() + return runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md") } func TocActionFunc(_ *cli.Context) error { - cmd := exec.Command("node_modules/.bin/markdown-toc", "-i", "README.md") - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := cmd.Run() + err := runCmd("node_modules/.bin/markdown-toc", "-i", "README.md") if err != nil { return err } - cmd = exec.Command("git", "diff", "--exit-code") - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err = cmd.Run() + err = runCmd("git", "diff", "--exit-code") if err != nil { return err } @@ -188,35 +168,17 @@ func TocActionFunc(_ *cli.Context) error { } func GenActionFunc(_ *cli.Context) error { - cmd := exec.Command("go", "generate", "flag-gen/main.go") - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err := cmd.Run() + err := runCmd("go", "generate", "flag-gen/main.go") if err != nil { return err } - cmd = exec.Command("go", "generate", "cli.go") - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err = cmd.Run() + err = runCmd("go", "generate", "cli.go") if err != nil { return err } - cmd = exec.Command("git", "diff", "--exit-code") - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err = cmd.Run() + err = runCmd("git", "diff", "--exit-code") if err != nil { return err } From 1095838cca9d596e55cff88bcb35b67cf83bf4e4 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 24 Aug 2019 00:49:29 -0700 Subject: [PATCH 183/215] check length --- build.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.go b/build.go index 4572d28..57d8432 100644 --- a/build.go +++ b/build.go @@ -53,7 +53,12 @@ func main() { } func runCmd(args ...string) error { - cmd := exec.Command(args[0], args[1:]...) + var cmd *exec.Cmd + if len(args) > 1 { + cmd = exec.Command(args[0], args[1:]...) + } else { + cmd = exec.Command(args[0]) + } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout From c2d1a132082e3b02a219e61eeef49da364d2c315 Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Sat, 24 Aug 2019 03:05:45 -0700 Subject: [PATCH 184/215] Revert "check length" This reverts commit 1095838cca9d596e55cff88bcb35b67cf83bf4e4. --- build.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/build.go b/build.go index 57d8432..4572d28 100644 --- a/build.go +++ b/build.go @@ -53,12 +53,7 @@ func main() { } func runCmd(args ...string) error { - var cmd *exec.Cmd - if len(args) > 1 { - cmd = exec.Command(args[0], args[1:]...) - } else { - cmd = exec.Command(args[0]) - } + cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout From d3edef887a2fc39830216cd41b16955ef60d0d3c Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Sat, 24 Aug 2019 11:34:03 +0100 Subject: [PATCH 185/215] Update build.go --- build.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.go b/build.go index 4572d28..bd1b3cf 100644 --- a/build.go +++ b/build.go @@ -52,8 +52,8 @@ func main() { } } -func runCmd(args ...string) error { - cmd := exec.Command(args[0], args[1:]...) +func runCmd(arg string, args ...string) error { + cmd := exec.Command(arg, args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout From edd8cb2068b6501d6b631299038cb42194926a8e Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 24 Aug 2019 14:44:56 -0700 Subject: [PATCH 186/215] Bump go version to 1.10 in readme Closes https://github.com/urfave/cli/issues/875 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a4ce0f3..96720b6 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ organized, and expressive! ## Installation -Make sure you have a working Go environment. Go version 1.2+ is supported. [See +Make sure you have a working Go environment. Go version 1.10+ is supported. [See the install instructions for Go](http://golang.org/doc/install.html). To install cli, simply run: From a1cf7f44b6cf65c0c10c282c71524cc37442b798 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Mon, 12 Aug 2019 09:42:12 +0200 Subject: [PATCH 187/215] Add `TakesFile` to fish shell completion The new `TakesFile` flag will be now consumed by the fish shell completion generator. Signed-off-by: Sascha Grunert --- docs_test.go | 12 ++- fish.go | 8 +- flag-gen/assets_vfsdata.go | 4 +- flag-gen/templates/cli_flags_generated.gotpl | 6 ++ flag.go | 4 + flag_generated.go | 78 ++++++++++++++++++++ testdata/expected-fish-full.fish | 34 ++++----- 7 files changed, 121 insertions(+), 25 deletions(-) diff --git a/docs_test.go b/docs_test.go index 177eb19..25afd23 100644 --- a/docs_test.go +++ b/docs_test.go @@ -10,9 +10,10 @@ func testApp() *App { app.Name = "greet" app.Flags = []Flag{ StringFlag{ - Name: "socket, s", - Usage: "some usage text", - Value: "value", + Name: "socket, s", + Usage: "some usage text", + Value: "value", + TakesFile: true, }, StringFlag{Name: "flag, fl, f"}, BoolFlag{ @@ -23,7 +24,10 @@ func testApp() *App { app.Commands = []Command{{ Aliases: []string{"c"}, Flags: []Flag{ - StringFlag{Name: "flag, fl, f"}, + StringFlag{ + Name: "flag, fl, f", + TakesFile: true, + }, BoolFlag{ Name: "another-flag, b", Usage: "another usage text", diff --git a/fish.go b/fish.go index 0f51065..d286881 100644 --- a/fish.go +++ b/fish.go @@ -75,7 +75,7 @@ func (a *App) prepareFishCommands( var completion strings.Builder completion.WriteString(fmt.Sprintf( - "complete -c %s -f -n '%s' -a '%s'", + "complete -r -c %s -n '%s' -a '%s'", a.Name, a.fishSubcommandHelper(previousCommands), strings.Join(command.Names(), " "), @@ -126,11 +126,15 @@ func (a *App) prepareFishFlags( var completion strings.Builder completion.WriteString(fmt.Sprintf( - "complete -c %s -f -n '%s'", + "complete -c %s -n '%s'", a.Name, a.fishSubcommandHelper(previousCommands), )) + if !flag.GetTakesFile() { + completion.WriteString(" -f") + } + for idx, opt := range strings.Split(flag.GetName(), ",") { if idx == 0 { completion.WriteString(fmt.Sprintf( diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index ab600d9..7e7d01b 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -46,9 +46,9 @@ var assets = func() http.FileSystem { "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", modTime: time.Time{}, - uncompressedSize: 2638, + uncompressedSize: 2895, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x6f\xe3\x36\x10\x3d\x4b\xbf\x62\x6a\xec\x41\x0a\x5c\xfb\xde\xc5\x9e\xe2\xcd\x76\x81\x62\x37\xe8\xa6\xb9\xd3\xd2\x50\x26\x42\x93\x0a\x39\x4a\x1a\x18\xfe\xef\xc5\x90\xd4\x87\x3f\x10\xfb\x92\x9e\x2c\x72\x66\xde\x7b\x7c\x33\xa4\x97\x4b\xb8\xb5\x35\x42\x83\x06\x9d\x20\xac\x61\xfd\x06\xb2\xf9\x0c\xab\x9f\xf0\xe3\xe7\x03\x7c\x5d\x7d\x7f\x58\xe4\x79\x2b\xaa\x27\xd1\x20\xec\x76\xb0\xb8\x8f\xdf\x3f\xc4\x16\x61\xbf\xcf\x73\xb5\x6d\xad\x23\x28\xf2\x6c\x26\xb5\x68\x66\xfc\xbb\x25\xfe\xf1\xe4\x2a\x6b\x5e\xf8\x93\xd4\x16\x67\x79\x99\xef\x76\xe0\x84\x69\x10\x3e\xa9\x39\x7c\xe2\x7c\xf8\xe3\x0b\x2c\xee\xb4\x68\x3c\xa3\x2d\x97\xcc\x11\x02\x8b\xc4\xc0\x31\x50\x1e\x04\x84\xf4\x57\x45\x1b\xa0\xb7\x16\xc7\xc4\x07\x5e\xed\xf7\xc3\x7a\x65\x2b\x12\x4a\x33\xde\x61\xe2\x14\xd1\x93\xeb\x2a\x82\x5d\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\x8f\x17\xcd\x64\xf9\xd5\xbc\x3c\x0a\x37\xae\xef\x94\xc6\x7b\x41\x9b\x61\xe3\x6f\x7c\xee\x94\xc3\x3a\x5b\x5b\xab\xf3\xec\x4f\x55\xd7\x68\xb2\xb4\xda\xed\x7e\x07\x25\x01\x9f\x7b\xa5\xe2\x09\x3d\x43\x00\xb9\x2e\xd8\x97\x0d\x5b\x93\x12\x34\x75\x88\x1d\x97\x3f\x0a\xdd\x4d\x4a\xc3\x32\x3b\xb6\xe1\x7d\x88\x15\x7a\x52\x46\x90\xb2\x66\x04\x9a\x6c\x66\x37\xef\xe2\xed\x73\x6e\xd1\xaf\x70\x76\x70\x48\x9d\x33\xdc\x19\x87\xa2\x16\x6b\x8d\xe0\xb0\x75\xe8\xd1\x50\x64\xb0\x12\x68\xa3\x3c\xbc\xb0\x52\xae\x2c\xa4\x75\xd0\xb1\xc5\x50\xa3\x14\x9d\x26\x5f\xe6\xb2\x33\x15\x14\xf2\x6c\x9f\xca\x44\x56\x94\x10\x1d\xe7\x8e\x45\x62\xe0\x78\x8c\xa2\x2b\x64\x99\xc4\x7d\x43\x0a\xf5\xbd\x3a\xda\x20\x18\xde\x08\x62\x30\x4c\xd1\x05\xca\x04\x71\x8e\x53\x86\xcc\x44\xf5\xdd\xf7\xdd\x1f\xd8\x5e\x37\x48\x1b\x74\x60\x1d\x18\x4b\x03\x21\x0f\xb0\x4b\xb9\x17\xc8\x47\xd0\xa2\x04\x9e\x89\x03\xf6\x3e\x96\x14\x84\xe9\x89\x63\x31\x9c\x97\xbb\x3a\x39\x2b\x10\xe7\x80\x88\x4d\x98\x83\x65\x7d\xaf\xca\x23\x48\xa1\x3d\x5e\x50\x33\x12\x9c\xaa\x19\x2a\xa2\x80\x61\x3c\xbe\x21\x85\x5b\x74\xd0\x82\xd8\xf4\xe4\x27\x4f\xc1\xf5\xbd\x08\x60\xe7\x9b\x11\x42\x23\xed\x91\x13\x89\x21\xcd\x1f\x08\xdf\x43\x1c\x8d\xa9\x30\x35\x08\x03\xb8\x6d\xe9\x8d\x91\x52\x96\x3a\x31\xd1\xd8\x1e\x8a\x40\x68\xbd\xb8\xac\xbd\xb7\x6e\xd4\x7e\xe8\x5a\xba\x4a\x83\x77\xc7\x40\xa0\xad\x7d\xf2\xd0\xb5\x41\x4a\x24\xb7\x12\x04\x68\x5b\x09\x7d\x96\x77\xde\x1b\x70\x80\x77\x6b\x0d\xe1\xbf\xb4\x8a\xb7\x8e\x91\x95\x0c\x23\x2a\x6d\x67\xfa\x99\xac\xe0\x26\xe5\x95\x27\xd0\x45\xb8\x44\xf1\x1c\x21\xca\xf5\x08\x3d\x70\x78\x2d\x66\xb3\xe9\x3b\x3c\x8d\x84\x6d\xd4\xfe\xe0\xa1\x1e\x22\xfd\xfb\x32\xe9\x2d\x9f\xbb\x6b\xcf\x8a\x98\x43\xb5\xe0\xcd\x5f\x48\xc3\xa5\xd7\x76\x2d\xf4\xd5\xe6\x35\x21\xfd\x23\xdd\x3b\x2f\xe8\xff\xf2\x50\x49\x90\x9e\xff\x59\xa3\x8b\x51\xcc\x5d\xb4\xac\xb7\xb0\xfc\xcc\x39\xbf\x7d\x01\xa3\xc2\xa5\xbe\xca\x78\xe9\xcb\x3c\xdb\x9f\xbe\x00\x27\xfe\x70\x5f\x82\x2b\xef\xc0\x25\x23\xe6\xe0\x91\xe0\x26\x84\x93\xc6\x8f\xf4\x46\xb2\x2f\x1e\x69\xf1\x57\x50\x16\x94\x94\xd1\xb2\xa9\x1b\xa3\x80\x7b\xe1\x3c\xba\xc8\xdd\xf2\x77\x3d\x07\x74\x8e\x61\x06\xa6\x94\x33\x55\x71\x94\x2a\xe3\x7d\x0f\x6b\xeb\x0a\xa3\x74\xc9\xb9\xe9\x6f\x9a\xe9\x39\x71\x22\xe0\x0a\x8f\x33\x6e\xc5\x89\xd2\x5b\xe1\x29\xaa\x3d\x46\x98\xc4\xa7\x4a\x53\x5a\x14\x3c\x11\x75\x65\x9f\xc7\x8a\xff\x02\x00\x00\xff\xff\x73\xc9\x52\x5f\x4e\x0a\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x56\x4d\x6f\xdb\x38\x10\x3d\x4b\xbf\x62\xd6\xe8\xc1\x0a\xbc\xce\x7d\x8b\x9e\xe2\xa6\x5b\x60\xd1\x06\xdb\x6c\xef\x8c\x34\x94\x89\xd0\xa4\x4a\x8e\x92\x0d\x04\xff\xf7\xc5\x90\x94\x44\x7f\x20\xf1\xa5\x8b\x9e\x22\x72\x86\xef\x3d\xbe\x19\x8e\x73\x7d\x0d\x37\xb6\x41\x68\xd1\xa0\x13\x84\x0d\x3c\xbc\x80\x6c\xdf\xc3\xe6\x2b\x7c\xf9\x7a\x0f\x1f\x37\x9f\xef\xd7\x65\xd9\x89\xfa\x51\xb4\x08\xc3\x00\xeb\xbb\xf8\xfd\x45\xec\x10\xf6\xfb\xb2\x54\xbb\xce\x3a\x82\x65\x59\x2c\xa4\x16\xed\x82\xff\xee\x88\xff\x78\x72\xb5\x35\x4f\xfc\x49\x6a\x87\x8b\xb2\x2a\x87\x01\x9c\x30\x2d\xc2\x3b\xb5\x82\x77\x9c\x0f\x7f\x7c\x80\xf5\xad\x16\xad\x67\xb4\xeb\x6b\xe6\x08\x81\x75\x62\xe0\x18\x28\x0f\x02\x42\xfa\xb3\xa2\x2d\xd0\x4b\x87\x73\xe2\x3d\xaf\xf6\xfb\x69\xbd\xb1\x35\x09\xa5\x19\xef\x30\x31\x47\xf4\xe4\xfa\x9a\x60\x28\x0b\xde\x2d\x0a\x4f\x4e\x99\xb6\x2c\xfe\xf1\xa2\xcd\x96\x1f\xcd\xd3\x77\xe1\xe6\xf5\xad\xd2\x78\x27\x68\x3b\x6d\xfc\x8d\x3f\x7a\xe5\xb0\x29\x1e\xac\xd5\x65\xf1\xa7\x6a\x1a\x34\x45\x5a\x0d\xc3\xef\xa0\x24\xe0\x8f\x51\xa9\x78\x44\xcf\x10\x40\xae\x0f\xf6\x15\xd3\x56\x76\x04\x4d\x13\x62\xc7\xc7\xbf\x0b\xdd\x67\x47\xc3\xb2\x38\xb6\xe1\x75\x88\x0d\x7a\x52\x46\x90\xb2\x66\x06\xca\x36\x8b\xab\x57\xf1\xf6\x25\x97\xe8\x5b\xb8\x3b\x38\xa4\xde\x19\xae\x8c\x43\xd1\x88\x07\x8d\xe0\xb0\x73\xe8\xd1\x50\x64\xb0\x12\x68\xab\x3c\x3c\xb1\x52\x3e\xb9\x94\xd6\x41\xcf\x16\x43\x83\x52\xf4\x9a\x7c\x55\xca\xde\xd4\xb0\x94\x67\xeb\x54\x25\xb2\x65\x05\xd1\x71\xae\x58\x24\x06\x8e\xc7\x28\xba\xa5\xac\x92\xb8\x4f\x48\xe1\xfc\xa8\x8e\xb6\x08\x86\x37\x82\x18\x0c\x5d\xf4\x06\x65\x82\x38\xc7\x29\x43\x66\xa2\xfa\xec\xc7\xea\x4f\x6c\xcf\x5b\xa4\x2d\x3a\xb0\x0e\x8c\xa5\x89\x90\x1b\xd8\xa5\xdc\x37\xc8\x67\xd0\x65\x05\xdc\x13\x07\xec\x63\x2c\x29\x08\xdd\x13\xdb\x62\xba\x2f\x57\x35\xbb\x2b\x10\xe7\x80\x88\x45\x58\x81\x65\x7d\xcf\xca\x23\x48\xa1\x3d\xbe\xa1\x66\x26\x38\x55\x33\x9d\x88\x02\xa6\xf6\xf8\x84\x14\x5e\xd1\x41\x09\x62\xd1\x93\x9f\xdc\x05\x97\xd7\x22\x80\x9d\x2f\x46\x08\xcd\xb4\x47\x4e\x24\x86\xd4\x7f\x20\xfc\x08\x71\xd4\xa6\xc2\x34\x20\x0c\xe0\xae\xa3\x17\x46\x4a\x59\xea\xc4\x44\x63\x47\x28\x02\xa1\xf5\xfa\x6d\xed\xa3\x75\xb3\xf6\x43\xd7\xd2\x53\xca\xbd\xfb\xf5\x2e\x31\xcd\xa8\xac\x07\x86\xe1\xf5\xc9\x36\x95\x68\x8a\x0c\x03\xa0\xf6\x79\x8c\xfb\x8f\x77\xf3\xd9\x72\x2c\x02\xb4\xb5\x8f\x1e\xfa\x2e\x5c\x23\x0a\xb7\x12\x04\x68\x5b\x0b\x7d\x56\xf3\x6a\x34\xef\x00\xef\xc6\x1a\xc2\x7f\x69\x13\xc7\x0e\x23\x2b\x19\xde\xa8\xb4\xbd\x19\x1f\x65\x0d\x57\x29\xaf\x3a\x81\x5e\x86\x29\x12\x6d\x0d\x51\x3e\x8f\x30\x02\x87\x71\xb9\x58\xe4\x3f\x44\x79\x24\x6c\xa7\xfb\x9f\xfe\x72\x8d\x03\x36\x6b\x6e\xbe\x77\xdf\x9d\x15\xb1\x82\x7a\xcd\x9b\xdf\x90\xa6\xa9\xa7\xed\x83\xd0\x17\x9b\xd7\x86\xf4\x9f\xe9\xde\x79\x41\xff\x97\x87\x4a\x82\xf4\xfc\xaf\x45\x74\x31\x8a\xb9\x8d\x96\x8d\x16\x56\xef\x39\xe7\xb7\x0f\x60\x54\xe8\xe8\x8b\x8c\x97\xbe\x2a\x8b\xfd\xe9\x08\x3c\xf1\x87\xeb\x12\x5c\x79\x05\x2e\x19\xb1\x02\x8f\x04\x57\x21\x9c\x34\xfe\x4c\x6f\x24\xfb\xe2\x91\xd6\x7f\x05\x65\x41\x49\x15\x2d\xcb\xdd\x98\x05\xdc\x09\xe7\xd1\x45\xee\x8e\xbf\x9b\x15\xa0\x73\x0c\x33\x31\xa5\x9c\x5c\xc5\x51\xaa\x8c\x03\x2f\xac\xad\x5b\x1a\xa5\xab\xf9\xed\x17\x4c\xcf\x89\x99\x80\x0b\x3c\x2e\xb8\x14\x27\x4a\x6f\x84\xa7\xa8\xf6\x18\x21\x8b\xe7\x4a\x53\x5a\x14\x9c\x89\xba\xb0\xce\xf3\x89\xff\x02\x00\x00\xff\xff\x05\x8b\x54\x74\x4f\x0b\x00\x00"), }, } fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index 61d06f0..1a95416 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -60,6 +60,12 @@ func (f {{ $flag.Name }}Flag) GetValue() string { {{ $flag.ValueString }} } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f {{ $flag.Name }}Flag) GetTakesFile() bool { + {{ if eq $flag.TakesFile true }}return f.TakesFile{{ else }}return false{{ end }} +} + // {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns // {{ $flag.ContextDefault }} if not found func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { diff --git a/flag.go b/flag.go index be55a33..4aa2fa8 100644 --- a/flag.go +++ b/flag.go @@ -96,6 +96,10 @@ type DocGenerationFlag interface { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. GetValue() string + + // GetTakesFile indicates that the command takes a file as input, which + // will be used for documentation and completion generation purposes. + GetTakesFile() bool } // errorableFlag is an interface that allows us to return errors during apply diff --git a/flag_generated.go b/flag_generated.go index 9dcdfd6..f847835 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -52,6 +52,12 @@ func (f BoolFlag) GetValue() string { return "" } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolFlag) GetTakesFile() bool { + return false +} + // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { @@ -122,6 +128,12 @@ func (f BoolTFlag) GetValue() string { return "" } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolTFlag) GetTakesFile() bool { + return false +} + // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { @@ -193,6 +205,12 @@ func (f DurationFlag) GetValue() string { return f.Value.String() } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f DurationFlag) GetTakesFile() bool { + return false +} + // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { @@ -264,6 +282,12 @@ func (f Float64Flag) GetValue() string { return fmt.Sprintf("%f", f.Value) } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Float64Flag) GetTakesFile() bool { + return false +} + // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { @@ -338,6 +362,12 @@ func (f GenericFlag) GetValue() string { return "" } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f GenericFlag) GetTakesFile() bool { + return f.TakesFile +} + // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { @@ -409,6 +439,12 @@ func (f Int64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64Flag) GetTakesFile() bool { + return false +} + // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { @@ -480,6 +516,12 @@ func (f IntFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntFlag) GetTakesFile() bool { + return false +} + // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { @@ -553,6 +595,12 @@ func (f IntSliceFlag) GetValue() string { return "" } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntSliceFlag) GetTakesFile() bool { + return false +} + // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { @@ -626,6 +674,12 @@ func (f Int64SliceFlag) GetValue() string { return "" } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64SliceFlag) GetTakesFile() bool { + return false +} + // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { @@ -698,6 +752,12 @@ func (f StringFlag) GetValue() string { return f.Value } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringFlag) GetTakesFile() bool { + return f.TakesFile +} + // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { @@ -772,6 +832,12 @@ func (f StringSliceFlag) GetValue() string { return "" } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringSliceFlag) GetTakesFile() bool { + return f.TakesFile +} + // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { @@ -843,6 +909,12 @@ func (f Uint64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Uint64Flag) GetTakesFile() bool { + return false +} + // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { @@ -914,6 +986,12 @@ func (f UintFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f UintFlag) GetTakesFile() bool { + return false +} + // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { diff --git a/testdata/expected-fish-full.fish b/testdata/expected-fish-full.fish index 3538c19..bda0322 100644 --- a/testdata/expected-fish-full.fish +++ b/testdata/expected-fish-full.fish @@ -9,20 +9,20 @@ function __fish_greet_no_subcommand --description 'Test if there has been any su return 0 end -complete -c greet -f -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some usage text' -complete -c greet -f -n '__fish_greet_no_subcommand' -l flag -s fl -s f -r -complete -c greet -f -n '__fish_greet_no_subcommand' -l another-flag -s b -d 'another usage text' -complete -c greet -f -n '__fish_greet_no_subcommand' -l help -s h -d 'show help' -complete -c greet -f -n '__fish_greet_no_subcommand' -l version -s v -d 'print the version' -complete -c greet -f -n '__fish_seen_subcommand_from config c' -l help -s h -d 'show help' -complete -c greet -f -n '__fish_greet_no_subcommand' -a 'config c' -d 'another usage test' -complete -c greet -f -n '__fish_seen_subcommand_from config c' -l flag -s fl -s f -r -complete -c greet -f -n '__fish_seen_subcommand_from config c' -l another-flag -s b -d 'another usage text' -complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l help -s h -d 'show help' -complete -c greet -f -n '__fish_seen_subcommand_from config c' -a 'sub-config s ss' -d 'another usage test' -complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l sub-flag -s sub-fl -s s -r -complete -c greet -f -n '__fish_seen_subcommand_from sub-config s ss' -l sub-command-flag -s s -d 'some usage text' -complete -c greet -f -n '__fish_seen_subcommand_from info i in' -l help -s h -d 'show help' -complete -c greet -f -n '__fish_greet_no_subcommand' -a 'info i in' -d 'retrieve generic information' -complete -c greet -f -n '__fish_seen_subcommand_from some-command' -l help -s h -d 'show help' -complete -c greet -f -n '__fish_greet_no_subcommand' -a 'some-command' +complete -c greet -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some usage text' +complete -c greet -n '__fish_greet_no_subcommand' -f -l flag -s fl -s f -r +complete -c greet -n '__fish_greet_no_subcommand' -f -l another-flag -s b -d 'another usage text' +complete -c greet -n '__fish_greet_no_subcommand' -f -l help -s h -d 'show help' +complete -c greet -n '__fish_greet_no_subcommand' -f -l version -s v -d 'print the version' +complete -c greet -n '__fish_seen_subcommand_from config c' -f -l help -s h -d 'show help' +complete -r -c greet -n '__fish_greet_no_subcommand' -a 'config c' -d 'another usage test' +complete -c greet -n '__fish_seen_subcommand_from config c' -l flag -s fl -s f -r +complete -c greet -n '__fish_seen_subcommand_from config c' -f -l another-flag -s b -d 'another usage text' +complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l help -s h -d 'show help' +complete -r -c greet -n '__fish_seen_subcommand_from config c' -a 'sub-config s ss' -d 'another usage test' +complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-flag -s sub-fl -s s -r +complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-command-flag -s s -d 'some usage text' +complete -c greet -n '__fish_seen_subcommand_from info i in' -f -l help -s h -d 'show help' +complete -r -c greet -n '__fish_greet_no_subcommand' -a 'info i in' -d 'retrieve generic information' +complete -c greet -n '__fish_seen_subcommand_from some-command' -f -l help -s h -d 'show help' +complete -r -c greet -n '__fish_greet_no_subcommand' -a 'some-command' From 38d0ac629677a7fbf08a52e17fec73894bb31263 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Sun, 25 Aug 2019 17:50:18 +0200 Subject: [PATCH 188/215] Removed GetTakesFile and stick to type assertions Signed-off-by: Sascha Grunert --- fish.go | 25 ++++++- flag-gen/assets_vfsdata.go | 4 +- flag-gen/templates/cli_flags_generated.gotpl | 6 -- flag.go | 4 - flag_generated.go | 78 -------------------- 5 files changed, 23 insertions(+), 94 deletions(-) diff --git a/fish.go b/fish.go index d286881..922b924 100644 --- a/fish.go +++ b/fish.go @@ -124,16 +124,14 @@ func (a *App) prepareFishFlags( continue } - var completion strings.Builder + completion := &strings.Builder{} completion.WriteString(fmt.Sprintf( "complete -c %s -n '%s'", a.Name, a.fishSubcommandHelper(previousCommands), )) - if !flag.GetTakesFile() { - completion.WriteString(" -f") - } + fishAddFileFlag(f, completion) for idx, opt := range strings.Split(flag.GetName(), ",") { if idx == 0 { @@ -162,6 +160,25 @@ func (a *App) prepareFishFlags( return completions } +func fishAddFileFlag( + flag Flag, + completion *strings.Builder, +) { + addFileExclusionFlag := true + if f, ok := flag.(GenericFlag); ok && f.TakesFile { + addFileExclusionFlag = false + } + if f, ok := flag.(StringFlag); ok && f.TakesFile { + addFileExclusionFlag = false + } + if f, ok := flag.(StringSliceFlag); ok && f.TakesFile { + addFileExclusionFlag = false + } + if addFileExclusionFlag { + completion.WriteString(" -f") + } +} + func (a *App) fishSubcommandHelper(allCommands []string) string { fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name) if len(allCommands) > 0 { diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go index 7e7d01b..ab600d9 100644 --- a/flag-gen/assets_vfsdata.go +++ b/flag-gen/assets_vfsdata.go @@ -46,9 +46,9 @@ var assets = func() http.FileSystem { "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ name: "cli_flags_generated.gotpl", modTime: time.Time{}, - uncompressedSize: 2895, + uncompressedSize: 2638, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x56\x4d\x6f\xdb\x38\x10\x3d\x4b\xbf\x62\xd6\xe8\xc1\x0a\xbc\xce\x7d\x8b\x9e\xe2\xa6\x5b\x60\xd1\x06\xdb\x6c\xef\x8c\x34\x94\x89\xd0\xa4\x4a\x8e\x92\x0d\x04\xff\xf7\xc5\x90\x94\x44\x7f\x20\xf1\xa5\x8b\x9e\x22\x72\x86\xef\x3d\xbe\x19\x8e\x73\x7d\x0d\x37\xb6\x41\x68\xd1\xa0\x13\x84\x0d\x3c\xbc\x80\x6c\xdf\xc3\xe6\x2b\x7c\xf9\x7a\x0f\x1f\x37\x9f\xef\xd7\x65\xd9\x89\xfa\x51\xb4\x08\xc3\x00\xeb\xbb\xf8\xfd\x45\xec\x10\xf6\xfb\xb2\x54\xbb\xce\x3a\x82\x65\x59\x2c\xa4\x16\xed\x82\xff\xee\x88\xff\x78\x72\xb5\x35\x4f\xfc\x49\x6a\x87\x8b\xb2\x2a\x87\x01\x9c\x30\x2d\xc2\x3b\xb5\x82\x77\x9c\x0f\x7f\x7c\x80\xf5\xad\x16\xad\x67\xb4\xeb\x6b\xe6\x08\x81\x75\x62\xe0\x18\x28\x0f\x02\x42\xfa\xb3\xa2\x2d\xd0\x4b\x87\x73\xe2\x3d\xaf\xf6\xfb\x69\xbd\xb1\x35\x09\xa5\x19\xef\x30\x31\x47\xf4\xe4\xfa\x9a\x60\x28\x0b\xde\x2d\x0a\x4f\x4e\x99\xb6\x2c\xfe\xf1\xa2\xcd\x96\x1f\xcd\xd3\x77\xe1\xe6\xf5\xad\xd2\x78\x27\x68\x3b\x6d\xfc\x8d\x3f\x7a\xe5\xb0\x29\x1e\xac\xd5\x65\xf1\xa7\x6a\x1a\x34\x45\x5a\x0d\xc3\xef\xa0\x24\xe0\x8f\x51\xa9\x78\x44\xcf\x10\x40\xae\x0f\xf6\x15\xd3\x56\x76\x04\x4d\x13\x62\xc7\xc7\xbf\x0b\xdd\x67\x47\xc3\xb2\x38\xb6\xe1\x75\x88\x0d\x7a\x52\x46\x90\xb2\x66\x06\xca\x36\x8b\xab\x57\xf1\xf6\x25\x97\xe8\x5b\xb8\x3b\x38\xa4\xde\x19\xae\x8c\x43\xd1\x88\x07\x8d\xe0\xb0\x73\xe8\xd1\x50\x64\xb0\x12\x68\xab\x3c\x3c\xb1\x52\x3e\xb9\x94\xd6\x41\xcf\x16\x43\x83\x52\xf4\x9a\x7c\x55\xca\xde\xd4\xb0\x94\x67\xeb\x54\x25\xb2\x65\x05\xd1\x71\xae\x58\x24\x06\x8e\xc7\x28\xba\xa5\xac\x92\xb8\x4f\x48\xe1\xfc\xa8\x8e\xb6\x08\x86\x37\x82\x18\x0c\x5d\xf4\x06\x65\x82\x38\xc7\x29\x43\x66\xa2\xfa\xec\xc7\xea\x4f\x6c\xcf\x5b\xa4\x2d\x3a\xb0\x0e\x8c\xa5\x89\x90\x1b\xd8\xa5\xdc\x37\xc8\x67\xd0\x65\x05\xdc\x13\x07\xec\x63\x2c\x29\x08\xdd\x13\xdb\x62\xba\x2f\x57\x35\xbb\x2b\x10\xe7\x80\x88\x45\x58\x81\x65\x7d\xcf\xca\x23\x48\xa1\x3d\xbe\xa1\x66\x26\x38\x55\x33\x9d\x88\x02\xa6\xf6\xf8\x84\x14\x5e\xd1\x41\x09\x62\xd1\x93\x9f\xdc\x05\x97\xd7\x22\x80\x9d\x2f\x46\x08\xcd\xb4\x47\x4e\x24\x86\xd4\x7f\x20\xfc\x08\x71\xd4\xa6\xc2\x34\x20\x0c\xe0\xae\xa3\x17\x46\x4a\x59\xea\xc4\x44\x63\x47\x28\x02\xa1\xf5\xfa\x6d\xed\xa3\x75\xb3\xf6\x43\xd7\xd2\x53\xca\xbd\xfb\xf5\x2e\x31\xcd\xa8\xac\x07\x86\xe1\xf5\xc9\x36\x95\x68\x8a\x0c\x03\xa0\xf6\x79\x8c\xfb\x8f\x77\xf3\xd9\x72\x2c\x02\xb4\xb5\x8f\x1e\xfa\x2e\x5c\x23\x0a\xb7\x12\x04\x68\x5b\x0b\x7d\x56\xf3\x6a\x34\xef\x00\xef\xc6\x1a\xc2\x7f\x69\x13\xc7\x0e\x23\x2b\x19\xde\xa8\xb4\xbd\x19\x1f\x65\x0d\x57\x29\xaf\x3a\x81\x5e\x86\x29\x12\x6d\x0d\x51\x3e\x8f\x30\x02\x87\x71\xb9\x58\xe4\x3f\x44\x79\x24\x6c\xa7\xfb\x9f\xfe\x72\x8d\x03\x36\x6b\x6e\xbe\x77\xdf\x9d\x15\xb1\x82\x7a\xcd\x9b\xdf\x90\xa6\xa9\xa7\xed\x83\xd0\x17\x9b\xd7\x86\xf4\x9f\xe9\xde\x79\x41\xff\x97\x87\x4a\x82\xf4\xfc\xaf\x45\x74\x31\x8a\xb9\x8d\x96\x8d\x16\x56\xef\x39\xe7\xb7\x0f\x60\x54\xe8\xe8\x8b\x8c\x97\xbe\x2a\x8b\xfd\xe9\x08\x3c\xf1\x87\xeb\x12\x5c\x79\x05\x2e\x19\xb1\x02\x8f\x04\x57\x21\x9c\x34\xfe\x4c\x6f\x24\xfb\xe2\x91\xd6\x7f\x05\x65\x41\x49\x15\x2d\xcb\xdd\x98\x05\xdc\x09\xe7\xd1\x45\xee\x8e\xbf\x9b\x15\xa0\x73\x0c\x33\x31\xa5\x9c\x5c\xc5\x51\xaa\x8c\x03\x2f\xac\xad\x5b\x1a\xa5\xab\xf9\xed\x17\x4c\xcf\x89\x99\x80\x0b\x3c\x2e\xb8\x14\x27\x4a\x6f\x84\xa7\xa8\xf6\x18\x21\x8b\xe7\x4a\x53\x5a\x14\x9c\x89\xba\xb0\xce\xf3\x89\xff\x02\x00\x00\xff\xff\x05\x8b\x54\x74\x4f\x0b\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x6f\xe3\x36\x10\x3d\x4b\xbf\x62\x6a\xec\x41\x0a\x5c\xfb\xde\xc5\x9e\xe2\xcd\x76\x81\x62\x37\xe8\xa6\xb9\xd3\xd2\x50\x26\x42\x93\x0a\x39\x4a\x1a\x18\xfe\xef\xc5\x90\xd4\x87\x3f\x10\xfb\x92\x9e\x2c\x72\x66\xde\x7b\x7c\x33\xa4\x97\x4b\xb8\xb5\x35\x42\x83\x06\x9d\x20\xac\x61\xfd\x06\xb2\xf9\x0c\xab\x9f\xf0\xe3\xe7\x03\x7c\x5d\x7d\x7f\x58\xe4\x79\x2b\xaa\x27\xd1\x20\xec\x76\xb0\xb8\x8f\xdf\x3f\xc4\x16\x61\xbf\xcf\x73\xb5\x6d\xad\x23\x28\xf2\x6c\x26\xb5\x68\x66\xfc\xbb\x25\xfe\xf1\xe4\x2a\x6b\x5e\xf8\x93\xd4\x16\x67\x79\x99\xef\x76\xe0\x84\x69\x10\x3e\xa9\x39\x7c\xe2\x7c\xf8\xe3\x0b\x2c\xee\xb4\x68\x3c\xa3\x2d\x97\xcc\x11\x02\x8b\xc4\xc0\x31\x50\x1e\x04\x84\xf4\x57\x45\x1b\xa0\xb7\x16\xc7\xc4\x07\x5e\xed\xf7\xc3\x7a\x65\x2b\x12\x4a\x33\xde\x61\xe2\x14\xd1\x93\xeb\x2a\x82\x5d\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\x8f\x17\xcd\x64\xf9\xd5\xbc\x3c\x0a\x37\xae\xef\x94\xc6\x7b\x41\x9b\x61\xe3\x6f\x7c\xee\x94\xc3\x3a\x5b\x5b\xab\xf3\xec\x4f\x55\xd7\x68\xb2\xb4\xda\xed\x7e\x07\x25\x01\x9f\x7b\xa5\xe2\x09\x3d\x43\x00\xb9\x2e\xd8\x97\x0d\x5b\x93\x12\x34\x75\x88\x1d\x97\x3f\x0a\xdd\x4d\x4a\xc3\x32\x3b\xb6\xe1\x7d\x88\x15\x7a\x52\x46\x90\xb2\x66\x04\x9a\x6c\x66\x37\xef\xe2\xed\x73\x6e\xd1\xaf\x70\x76\x70\x48\x9d\x33\xdc\x19\x87\xa2\x16\x6b\x8d\xe0\xb0\x75\xe8\xd1\x50\x64\xb0\x12\x68\xa3\x3c\xbc\xb0\x52\xae\x2c\xa4\x75\xd0\xb1\xc5\x50\xa3\x14\x9d\x26\x5f\xe6\xb2\x33\x15\x14\xf2\x6c\x9f\xca\x44\x56\x94\x10\x1d\xe7\x8e\x45\x62\xe0\x78\x8c\xa2\x2b\x64\x99\xc4\x7d\x43\x0a\xf5\xbd\x3a\xda\x20\x18\xde\x08\x62\x30\x4c\xd1\x05\xca\x04\x71\x8e\x53\x86\xcc\x44\xf5\xdd\xf7\xdd\x1f\xd8\x5e\x37\x48\x1b\x74\x60\x1d\x18\x4b\x03\x21\x0f\xb0\x4b\xb9\x17\xc8\x47\xd0\xa2\x04\x9e\x89\x03\xf6\x3e\x96\x14\x84\xe9\x89\x63\x31\x9c\x97\xbb\x3a\x39\x2b\x10\xe7\x80\x88\x4d\x98\x83\x65\x7d\xaf\xca\x23\x48\xa1\x3d\x5e\x50\x33\x12\x9c\xaa\x19\x2a\xa2\x80\x61\x3c\xbe\x21\x85\x5b\x74\xd0\x82\xd8\xf4\xe4\x27\x4f\xc1\xf5\xbd\x08\x60\xe7\x9b\x11\x42\x23\xed\x91\x13\x89\x21\xcd\x1f\x08\xdf\x43\x1c\x8d\xa9\x30\x35\x08\x03\xb8\x6d\xe9\x8d\x91\x52\x96\x3a\x31\xd1\xd8\x1e\x8a\x40\x68\xbd\xb8\xac\xbd\xb7\x6e\xd4\x7e\xe8\x5a\xba\x4a\x83\x77\xc7\x40\xa0\xad\x7d\xf2\xd0\xb5\x41\x4a\x24\xb7\x12\x04\x68\x5b\x09\x7d\x96\x77\xde\x1b\x70\x80\x77\x6b\x0d\xe1\xbf\xb4\x8a\xb7\x8e\x91\x95\x0c\x23\x2a\x6d\x67\xfa\x99\xac\xe0\x26\xe5\x95\x27\xd0\x45\xb8\x44\xf1\x1c\x21\xca\xf5\x08\x3d\x70\x78\x2d\x66\xb3\xe9\x3b\x3c\x8d\x84\x6d\xd4\xfe\xe0\xa1\x1e\x22\xfd\xfb\x32\xe9\x2d\x9f\xbb\x6b\xcf\x8a\x98\x43\xb5\xe0\xcd\x5f\x48\xc3\xa5\xd7\x76\x2d\xf4\xd5\xe6\x35\x21\xfd\x23\xdd\x3b\x2f\xe8\xff\xf2\x50\x49\x90\x9e\xff\x59\xa3\x8b\x51\xcc\x5d\xb4\xac\xb7\xb0\xfc\xcc\x39\xbf\x7d\x01\xa3\xc2\xa5\xbe\xca\x78\xe9\xcb\x3c\xdb\x9f\xbe\x00\x27\xfe\x70\x5f\x82\x2b\xef\xc0\x25\x23\xe6\xe0\x91\xe0\x26\x84\x93\xc6\x8f\xf4\x46\xb2\x2f\x1e\x69\xf1\x57\x50\x16\x94\x94\xd1\xb2\xa9\x1b\xa3\x80\x7b\xe1\x3c\xba\xc8\xdd\xf2\x77\x3d\x07\x74\x8e\x61\x06\xa6\x94\x33\x55\x71\x94\x2a\xe3\x7d\x0f\x6b\xeb\x0a\xa3\x74\xc9\xb9\xe9\x6f\x9a\xe9\x39\x71\x22\xe0\x0a\x8f\x33\x6e\xc5\x89\xd2\x5b\xe1\x29\xaa\x3d\x46\x98\xc4\xa7\x4a\x53\x5a\x14\x3c\x11\x75\x65\x9f\xc7\x8a\xff\x02\x00\x00\xff\xff\x73\xc9\x52\x5f\x4e\x0a\x00\x00"), }, } fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl index 1a95416..61d06f0 100644 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ b/flag-gen/templates/cli_flags_generated.gotpl @@ -60,12 +60,6 @@ func (f {{ $flag.Name }}Flag) GetValue() string { {{ $flag.ValueString }} } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f {{ $flag.Name }}Flag) GetTakesFile() bool { - {{ if eq $flag.TakesFile true }}return f.TakesFile{{ else }}return false{{ end }} -} - // {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns // {{ $flag.ContextDefault }} if not found func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { diff --git a/flag.go b/flag.go index 4aa2fa8..be55a33 100644 --- a/flag.go +++ b/flag.go @@ -96,10 +96,6 @@ type DocGenerationFlag interface { // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. GetValue() string - - // GetTakesFile indicates that the command takes a file as input, which - // will be used for documentation and completion generation purposes. - GetTakesFile() bool } // errorableFlag is an interface that allows us to return errors during apply diff --git a/flag_generated.go b/flag_generated.go index f847835..9dcdfd6 100644 --- a/flag_generated.go +++ b/flag_generated.go @@ -52,12 +52,6 @@ func (f BoolFlag) GetValue() string { return "" } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f BoolFlag) GetTakesFile() bool { - return false -} - // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { @@ -128,12 +122,6 @@ func (f BoolTFlag) GetValue() string { return "" } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f BoolTFlag) GetTakesFile() bool { - return false -} - // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { @@ -205,12 +193,6 @@ func (f DurationFlag) GetValue() string { return f.Value.String() } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f DurationFlag) GetTakesFile() bool { - return false -} - // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { @@ -282,12 +264,6 @@ func (f Float64Flag) GetValue() string { return fmt.Sprintf("%f", f.Value) } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Float64Flag) GetTakesFile() bool { - return false -} - // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { @@ -362,12 +338,6 @@ func (f GenericFlag) GetValue() string { return "" } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f GenericFlag) GetTakesFile() bool { - return f.TakesFile -} - // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { @@ -439,12 +409,6 @@ func (f Int64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Int64Flag) GetTakesFile() bool { - return false -} - // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { @@ -516,12 +480,6 @@ func (f IntFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f IntFlag) GetTakesFile() bool { - return false -} - // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { @@ -595,12 +553,6 @@ func (f IntSliceFlag) GetValue() string { return "" } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f IntSliceFlag) GetTakesFile() bool { - return false -} - // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { @@ -674,12 +626,6 @@ func (f Int64SliceFlag) GetValue() string { return "" } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Int64SliceFlag) GetTakesFile() bool { - return false -} - // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { @@ -752,12 +698,6 @@ func (f StringFlag) GetValue() string { return f.Value } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f StringFlag) GetTakesFile() bool { - return f.TakesFile -} - // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { @@ -832,12 +772,6 @@ func (f StringSliceFlag) GetValue() string { return "" } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f StringSliceFlag) GetTakesFile() bool { - return f.TakesFile -} - // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { @@ -909,12 +843,6 @@ func (f Uint64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Uint64Flag) GetTakesFile() bool { - return false -} - // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { @@ -986,12 +914,6 @@ func (f UintFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f UintFlag) GetTakesFile() bool { - return false -} - // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { From 0c01922a12c501867cad200bc4b36a25f9a073e0 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Mon, 26 Aug 2019 08:46:55 +0200 Subject: [PATCH 189/215] Add type switch Signed-off-by: Sascha Grunert --- fish.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/fish.go b/fish.go index 922b924..e6c6d84 100644 --- a/fish.go +++ b/fish.go @@ -164,19 +164,21 @@ func fishAddFileFlag( flag Flag, completion *strings.Builder, ) { - addFileExclusionFlag := true - if f, ok := flag.(GenericFlag); ok && f.TakesFile { - addFileExclusionFlag = false - } - if f, ok := flag.(StringFlag); ok && f.TakesFile { - addFileExclusionFlag = false - } - if f, ok := flag.(StringSliceFlag); ok && f.TakesFile { - addFileExclusionFlag = false - } - if addFileExclusionFlag { - completion.WriteString(" -f") + switch f := flag.(type) { + case GenericFlag: + if f.TakesFile { + return + } + case StringFlag: + if f.TakesFile { + return + } + case StringSliceFlag: + if f.TakesFile { + return + } } + completion.WriteString(" -f") } func (a *App) fishSubcommandHelper(allCommands []string) string { From 687f721eaa40859950820b37b9ad1fcd85b7da9f Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Mon, 26 Aug 2019 10:07:50 +0200 Subject: [PATCH 190/215] Update function alignment Signed-off-by: Sascha Grunert --- fish.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/fish.go b/fish.go index e6c6d84..166435c 100644 --- a/fish.go +++ b/fish.go @@ -64,11 +64,7 @@ func (a *App) writeFishCompletionTemplate(w io.Writer) error { }) } -func (a *App) prepareFishCommands( - commands []Command, - allCommands *[]string, - previousCommands []string, -) []string { +func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, previousCommands []string) []string { completions := []string{} for i := range commands { command := &commands[i] @@ -113,10 +109,7 @@ func (a *App) prepareFishCommands( return completions } -func (a *App) prepareFishFlags( - flags []Flag, - previousCommands []string, -) []string { +func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string { completions := []string{} for _, f := range flags { flag, ok := f.(DocGenerationFlag) @@ -160,10 +153,7 @@ func (a *App) prepareFishFlags( return completions } -func fishAddFileFlag( - flag Flag, - completion *strings.Builder, -) { +func fishAddFileFlag(flag Flag, completion *strings.Builder) { switch f := flag.(type) { case GenericFlag: if f.TakesFile { From 4bbff841690954ed86c147147151c0144dcf1765 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Thu, 29 Aug 2019 14:45:32 +0200 Subject: [PATCH 191/215] Escape single quotes in fish shell completion Single quotes can break the generated fish shell completion and should be escaped correctly. Signed-off-by: Sascha Grunert --- docs_test.go | 2 +- fish.go | 10 ++++++++-- testdata/expected-doc-full.man | 2 +- testdata/expected-doc-full.md | 2 +- testdata/expected-doc-no-commands.md | 2 +- testdata/expected-fish-full.fish | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs_test.go b/docs_test.go index 25afd23..6ffc995 100644 --- a/docs_test.go +++ b/docs_test.go @@ -11,7 +11,7 @@ func testApp() *App { app.Flags = []Flag{ StringFlag{ Name: "socket, s", - Usage: "some usage text", + Usage: "some 'usage' text", Value: "value", TakesFile: true, }, diff --git a/fish.go b/fish.go index 166435c..1121a20 100644 --- a/fish.go +++ b/fish.go @@ -78,7 +78,8 @@ func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, pre )) if command.Usage != "" { - completion.WriteString(fmt.Sprintf(" -d '%s'", command.Usage)) + completion.WriteString(fmt.Sprintf(" -d '%s'", + escapeSingleQuotes(command.Usage))) } if !command.HideHelp { @@ -144,7 +145,8 @@ func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string } if flag.GetUsage() != "" { - completion.WriteString(fmt.Sprintf(" -d '%s'", flag.GetUsage())) + completion.WriteString(fmt.Sprintf(" -d '%s'", + escapeSingleQuotes(flag.GetUsage()))) } completions = append(completions, completion.String()) @@ -182,3 +184,7 @@ func (a *App) fishSubcommandHelper(allCommands []string) string { return fishHelper } + +func escapeSingleQuotes(input string) string { + return strings.Replace(input, `'`, `\'`, -1) +} diff --git a/testdata/expected-doc-full.man b/testdata/expected-doc-full.man index a2d0e6d..5190698 100644 --- a/testdata/expected-doc-full.man +++ b/testdata/expected-doc-full.man @@ -49,7 +49,7 @@ greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] \fB\-\-flag, \-\-fl, \-f\fP="": .PP -\fB\-\-socket, \-s\fP="": some usage text (default: value) +\fB\-\-socket, \-s\fP="": some 'usage' text (default: value) .SH COMMANDS diff --git a/testdata/expected-doc-full.md b/testdata/expected-doc-full.md index 8fb9f58..23d7c23 100644 --- a/testdata/expected-doc-full.md +++ b/testdata/expected-doc-full.md @@ -32,7 +32,7 @@ greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] **--flag, --fl, -f**="": -**--socket, -s**="": some usage text (default: value) +**--socket, -s**="": some 'usage' text (default: value) # COMMANDS diff --git a/testdata/expected-doc-no-commands.md b/testdata/expected-doc-no-commands.md index dba7bb0..18d8e35 100644 --- a/testdata/expected-doc-no-commands.md +++ b/testdata/expected-doc-no-commands.md @@ -32,5 +32,5 @@ greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] **--flag, --fl, -f**="": -**--socket, -s**="": some usage text (default: value) +**--socket, -s**="": some 'usage' text (default: value) diff --git a/testdata/expected-fish-full.fish b/testdata/expected-fish-full.fish index bda0322..b18d51e 100644 --- a/testdata/expected-fish-full.fish +++ b/testdata/expected-fish-full.fish @@ -9,7 +9,7 @@ function __fish_greet_no_subcommand --description 'Test if there has been any su return 0 end -complete -c greet -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some usage text' +complete -c greet -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some \'usage\' text' complete -c greet -n '__fish_greet_no_subcommand' -f -l flag -s fl -s f -r complete -c greet -n '__fish_greet_no_subcommand' -f -l another-flag -s b -d 'another usage text' complete -c greet -n '__fish_greet_no_subcommand' -f -l help -s h -d 'show help' From 890d49ca7a15a3fb7f44d33e5cdb499f0e5c2331 Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 7 Sep 2019 20:41:44 -0700 Subject: [PATCH 192/215] Release 1.22.0 - adds the changelog for 1.22.0 - updates the changelog for 1.21.0. some PRs were missed, as was mentioned here https://github.com/urfave/cli/pull/829#issuecomment-517968795 - closes https://github.com/urfave/cli/issues/867 --- CHANGELOG.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e49d54..ae95be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ ## [Unreleased] +## [1.22.0] - 2019-09-07 + +### Fixed + +* Fix Subcommands not falling back to `app.ExitEventHandler` in [urfave/cli/pull/856](https://github.com/urfave/cli/pull/856) via [@FaranIdo](https://github.com/FaranIdo) + +### Changed + +* Clarify that altsrc supports both TOML and JSON in [urfave/cli/pull/774](https://github.com/urfave/cli/pull/774) via [@whereswaldon](https://github.com/whereswaldon) +* Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0) +* Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) +* Changed the supported go versions to `1.10`, `1.11`, `1.12` in [https://github.com/urfave/cli/pull/843](urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) +* Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) +* Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert) + +### Added + +* Added `UseShortOptionHandling` for combining short flags in [urfave/cli/pull/735](https://github.com/urfave/cli/pull/735) via [@rliebz](https://github.com/rliebz) +* Added support for flags bash completion in [urfave/cli/pull/808](https://github.com/urfave/cli/pull/808) via [@yogeshlonkar](https://github.com/yogeshlonkar) +* Added the `TakesFile` indicator to flag in [urfave/cli/pull/851](https://github.com/urfave/cli/pull/851) via [@saschagrunert](https://github.com/saschagrunert) +* Added fish shell completion support in [urfave/cli/pull/848](https://github.com/urfave/cli/pull/848) via [@saschagrunert](https://github.com/saschagrunert) + ## [1.21.0] - 2019-08-02 ### Fixed @@ -13,17 +35,24 @@ * Fix handling `ShortOptions` and `SkipArgReorder` in [urfave/cli/pull/686](https://github.com/urfave/cli/pull/686) via [@baude](https://github.com/baude) * Fix args reordering when bool flags are present in [urfave/cli/pull/712](https://github.com/urfave/cli/pull/712) via [@windler](https://github.com/windler) * Fix parsing of short options in [urfave/cli/pull/758](https://github.com/urfave/cli/pull/758) via [@vrothberg](https://github.com/vrothberg) +* Fix unaligned indents for the command help messages in [urfave/cli/pull/806](https://github.com/urfave/cli/pull/806) via [@mingrammer](https://github.com/mingrammer) -### Added / Changed +### Changed -* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/) * Cleaned up help output in [urfave/cli/pull/664](https://github.com/urfave/cli/pull/664) via [@maguro](https://github.com/maguro) +* Remove redundant nil checks in [urfave/cli/pull/773](https://github.com/urfave/cli/pull/773) via [@teresy](https://github.com/teresy) * Case is now considered when sorting strings in [urfave/cli/pull/676](https://github.com/urfave/cli/pull/676) via [@rliebz](https://github.com/rliebz) + +### Added + +* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/) * Backport JSON `InputSource` to v1 in [urfave/cli/pull/598](https://github.com/urfave/cli/pull/598) via [@jszwedko](https://github.com/jszwedko) * Allow more customization of flag help strings in [urfave/cli/pull/661](https://github.com/urfave/cli/pull/661) via [@rliebz](https://github.com/rliebz) * Allow custom `ExitError` handler function in [urfave/cli/pull/628](https://github.com/urfave/cli/pull/628) via [@phinnaeus](https://github.com/phinnaeus) * Allow loading a variable from a file in [urfave/cli/pull/675](https://github.com/urfave/cli/pull/675) via [@jmccann](https://github.com/jmccann) * Allow combining short bool names in [urfave/cli/pull/684](https://github.com/urfave/cli/pull/684) via [@baude](https://github.com/baude) +* Added test coverage to context in [urfave/cli/pull/788](https://github.com/urfave/cli/pull/788) via [@benzvan](https://github.com/benzvan) +* Added go module support in [urfave/cli/pull/831](https://github.com/urfave/cli/pull/831) via [@saschagrunert](https://github.com/saschagrunert) ## [1.20.0] - 2017-08-10 @@ -428,7 +457,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[Unreleased]: https://github.com/urfave/cli/compare/v1.21.0...HEAD +[Unreleased]: https://github.com/urfave/cli/compare/v1.22.0...HEAD +[1.22.0]: https://github.com/urfave/cli/compare/v1.21.0...v1.22.0 [1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 [1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0 [1.19.1]: https://github.com/urfave/cli/compare/v1.19.0...v1.19.1 From 980742b7cc56e8f7b14b4668fb8ddcf89025c9ea Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 7 Sep 2019 20:43:56 -0700 Subject: [PATCH 193/215] typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae95be2..b118580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ * Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0) * Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) * Changed the supported go versions to `1.10`, `1.11`, `1.12` in [https://github.com/urfave/cli/pull/843](urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) -* Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) +* Changed the v1 releases section in the readme in [https://github.com/urfave/cli/pull/862](urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) * Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert) ### Added From 238c80f9b5e54a834f25423fd0887f68b9737cbb Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Sat, 7 Sep 2019 20:44:48 -0700 Subject: [PATCH 194/215] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b118580..2a30fcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ * Clarify that altsrc supports both TOML and JSON in [urfave/cli/pull/774](https://github.com/urfave/cli/pull/774) via [@whereswaldon](https://github.com/whereswaldon) * Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0) * Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) -* Changed the supported go versions to `1.10`, `1.11`, `1.12` in [https://github.com/urfave/cli/pull/843](urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) -* Changed the v1 releases section in the readme in [https://github.com/urfave/cli/pull/862](urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) +* Changed the supported go versions to `1.10`, `1.11`, `1.12` in [urfave/cli/pull/843](https://github.com/urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) +* Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) * Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert) ### Added From b6c5d17a835d17e8dd8f2e34c02e96af7f43c9e4 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 07:44:07 +0530 Subject: [PATCH 195/215] Remove Flag Generation Remove Legacy Python Scripts --- build.go | 23 - flag-gen/.gitignore | 1 - flag-gen/assets_generate.go | 55 - flag-gen/assets_vfsdata.go | 214 ---- flag-gen/go.mod | 9 - flag-gen/go.sum | 9 - flag-gen/main.go | 115 --- flag-gen/source/flag-types.json | 139 --- .../templates/altsrc_flags_generated.gotpl | 35 - flag-gen/templates/cli_flags_generated.gotpl | 89 -- flag.go | 464 --------- flag_bool.go | 109 ++ flag_bool_t.go | 110 ++ flag_duration.go | 106 ++ flag_float64.go | 106 ++ flag_generated.go | 943 ------------------ flag_generic.go | 110 ++ flag_int.go | 105 ++ flag_int64.go | 106 ++ flag_int64_slice.go | 141 +++ flag_int_slice.go | 142 +++ flag_string.go | 98 ++ flag_string_slice.go | 138 +++ flag_uint.go | 106 ++ flag_uint64.go | 106 ++ runtests | 109 -- 26 files changed, 1483 insertions(+), 2205 deletions(-) delete mode 100644 flag-gen/.gitignore delete mode 100644 flag-gen/assets_generate.go delete mode 100644 flag-gen/assets_vfsdata.go delete mode 100644 flag-gen/go.mod delete mode 100644 flag-gen/go.sum delete mode 100644 flag-gen/main.go delete mode 100644 flag-gen/source/flag-types.json delete mode 100644 flag-gen/templates/altsrc_flags_generated.gotpl delete mode 100644 flag-gen/templates/cli_flags_generated.gotpl create mode 100644 flag_bool.go create mode 100644 flag_bool_t.go create mode 100644 flag_duration.go create mode 100644 flag_float64.go delete mode 100644 flag_generated.go create mode 100644 flag_generic.go create mode 100644 flag_int.go create mode 100644 flag_int64.go create mode 100644 flag_int64_slice.go create mode 100644 flag_int_slice.go create mode 100644 flag_string.go create mode 100644 flag_string_slice.go create mode 100644 flag_uint.go create mode 100644 flag_uint64.go delete mode 100755 runtests diff --git a/build.go b/build.go index bd1b3cf..a828ae4 100644 --- a/build.go +++ b/build.go @@ -40,10 +40,6 @@ func main() { Name: "toc", Action: TocActionFunc, }, - cli.Command{ - Name: "generate", - Action: GenActionFunc, - }, } err := app.Run(os.Args) @@ -166,22 +162,3 @@ func TocActionFunc(_ *cli.Context) error { return nil } - -func GenActionFunc(_ *cli.Context) error { - err := runCmd("go", "generate", "flag-gen/main.go") - if err != nil { - return err - } - - err = runCmd("go", "generate", "cli.go") - if err != nil { - return err - } - - err = runCmd("git", "diff", "--exit-code") - if err != nil { - return err - } - - return nil -} diff --git a/flag-gen/.gitignore b/flag-gen/.gitignore deleted file mode 100644 index 5657f6e..0000000 --- a/flag-gen/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor \ No newline at end of file diff --git a/flag-gen/assets_generate.go b/flag-gen/assets_generate.go deleted file mode 100644 index 021da71..0000000 --- a/flag-gen/assets_generate.go +++ /dev/null @@ -1,55 +0,0 @@ -// +build ignore - -package main - -import ( - "github.com/shurcooL/httpfs/union" - "github.com/shurcooL/vfsgen" - "log" - "net/http" - "os" - "time" -) - -// zeroModTimeFileSystem is an http.FileSystem wrapper. -// It exposes a filesystem exactly like Source, except -// all file modification times are changed to zero. -// See https://github.com/shurcooL/vfsgen/pull/40#issuecomment-355416103 -type zeroModTimeFileSystem struct { - Source http.FileSystem -} - -func (fs zeroModTimeFileSystem) Open(name string) (http.File, error) { - f, err := fs.Source.Open(name) - return file{f}, err -} - -type file struct { - http.File -} - -func (f file) Stat() (os.FileInfo, error) { - fi, err := f.File.Stat() - return fileInfo{fi}, err -} - -type fileInfo struct { - os.FileInfo -} - -func (fi fileInfo) ModTime() time.Time { return time.Time{} } - -func main() { - fs := zeroModTimeFileSystem{ - Source: union.New(map[string]http.FileSystem{ - "/templates": http.Dir("templates"), - "/source": http.Dir("source"), - }), - } - - err := vfsgen.Generate(fs, vfsgen.Options{}) - - if err != nil { - log.Fatal(err) - } -} diff --git a/flag-gen/assets_vfsdata.go b/flag-gen/assets_vfsdata.go deleted file mode 100644 index ab600d9..0000000 --- a/flag-gen/assets_vfsdata.go +++ /dev/null @@ -1,214 +0,0 @@ -// Code generated by vfsgen; DO NOT EDIT. - -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - pathpkg "path" - "time" -) - -// assets statically implements the virtual filesystem provided to vfsgen. -var assets = func() http.FileSystem { - fs := vfsgenÛ°FS{ - "/": &vfsgenÛ°DirInfo{ - name: "/", - modTime: time.Time{}, - }, - "/source": &vfsgenÛ°DirInfo{ - name: "source", - modTime: time.Time{}, - }, - "/source/flag-types.json": &vfsgenÛ°CompressedFileInfo{ - name: "flag-types.json", - modTime: time.Time{}, - uncompressedSize: 3686, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x95\x41\x6f\x9b\x40\x10\x85\xef\xfc\x8a\xe9\x56\x95\xa0\xb2\x20\x07\xcb\x07\x4b\xbd\x54\x55\x2b\xdf\x2a\xb9\xe9\x25\x44\xd6\x06\x16\xb2\xca\x7a\x17\x2d\x43\xd4\xc8\xf2\x7f\xaf\x16\x8c\x0d\x6b\xb0\x4d\x23\x23\xf7\x92\xc3\x9b\x2c\x33\xef\xcb\x9b\xcc\x83\x03\xb0\x71\x00\x00\x88\xa4\x6b\x46\xe6\x40\xbe\x2a\x25\xc8\xa4\xd2\xf0\x2d\x2b\xb5\xa7\x86\xf6\x4a\x45\x61\xc4\x84\x8a\x9c\xed\xb4\x98\xe5\x48\xe6\x80\xba\xa8\x95\x48\x49\x64\x7f\x70\x15\xb3\x84\x16\xc2\x14\x49\xf9\xa0\xfe\x4a\x46\x75\xce\xb4\x91\x73\xd4\x91\x92\xaf\xfe\x4f\xa3\x98\xe6\x6e\xe2\xff\x36\x3d\xfc\x25\x6a\x2e\x53\xd7\xf3\x5a\xad\x2b\xd5\xbc\xd4\x0c\x0b\x2d\x21\x24\x21\xd9\x0f\x4c\x5f\x58\xbe\x4a\xb8\xd8\x4f\xe8\x00\x6c\x27\xdd\x2e\x7f\xbd\xdf\x66\xac\x22\xa4\x5c\x98\xc7\x80\xcf\x14\x81\xe7\x65\x19\x9e\xde\xa0\xb6\xfe\x9f\x10\xf9\x56\x68\x8a\x5c\x49\x1b\x0a\xf2\x35\xf3\xed\x62\x4d\xa7\x89\xe2\x24\x1c\x37\x67\x0c\x9e\x11\xb3\x7c\x1e\x04\xa9\x12\x54\xa6\xbe\xd2\x69\x90\xbd\xa4\x81\xe9\x10\x7c\x2c\xdd\xd6\x7d\xbc\x13\xd4\xee\x8e\x89\x95\x33\xb6\x3e\x30\x14\x99\xfd\xeb\x83\xf1\x7d\x17\x8a\xe2\x6c\x6a\xd3\x4b\xda\xf2\x45\xdc\x2e\xf3\xdc\x4a\x49\xd9\xfd\xc8\xf3\x04\x66\xd3\x73\xbe\xd7\xe8\x2f\x33\xcd\x25\x26\x6e\x48\x3e\x25\x21\x99\xd4\x2c\x86\x33\xf8\xc1\x24\xd3\x3c\xb2\x19\x58\x72\x3f\x83\xe6\xae\x75\x40\x90\x5c\xd8\xb1\xa8\x5b\x70\x89\x4c\x27\x34\x62\x9b\x6d\x8f\x5f\x9e\xd4\xbe\xe0\xc3\x17\x90\x5c\xc0\x26\x94\x21\x86\xd8\x13\x00\x53\xdc\x9a\x1f\xe7\x76\xca\xd8\xe8\xe1\xb1\x90\x1d\x89\xe0\x72\x84\x3c\x2c\x64\x57\x1a\xee\x86\x07\x22\x7e\x57\x20\x16\x12\x3b\xec\xdf\x82\xf9\xea\xe9\x2a\xa2\x65\x3f\x33\x95\x5b\x4a\xf1\xc8\x78\x96\x82\x47\xcc\x66\xf4\xd9\x2e\x5c\x63\x65\x1e\x1e\x1b\x7f\x8a\x03\xc9\x3d\x39\x77\x3f\x85\xe7\x55\x92\xa1\xc8\xb4\x56\xda\x95\x5c\xf4\x71\xba\xda\x9e\x9d\x01\x39\x9b\xf6\xa2\xb4\x4a\x57\x83\x79\x58\xeb\x3e\x9c\xbb\x49\x6e\x1f\xe8\xae\xbd\x05\x33\x6f\xa9\xff\xba\xbe\xcd\x81\x0e\xa0\x8e\x17\xf6\x2c\x99\x36\x80\xa1\xff\x9c\xab\xef\x74\x87\xa6\xa3\x76\x9d\xd4\xb4\x89\x76\xc6\xa6\x31\xcb\x6d\xe4\xe6\x04\xd3\x7b\xde\x75\xf1\x8a\x51\x4e\x9e\xe9\x7d\x0b\x37\xef\x9e\x1f\x1f\xbd\x62\x84\xab\x77\x91\x7f\xeb\xec\x15\x23\xdc\x3d\xe7\xd1\xf9\x1b\x00\x00\xff\xff\xc7\xe4\x19\x16\x66\x0e\x00\x00"), - }, - "/templates": &vfsgenÛ°DirInfo{ - name: "templates", - modTime: time.Time{}, - }, - "/templates/altsrc_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ - name: "altsrc_flags_generated.gotpl", - modTime: time.Time{}, - uncompressedSize: 1044, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x92\x4f\x8b\xdb\x30\x10\xc5\xcf\xd6\xa7\x78\x84\xa5\xc4\x4b\xb0\xef\x29\x7b\x28\xdd\x2d\xf4\x92\x2d\xec\x42\xcf\x8a\x3c\xb2\x45\x15\xdb\x48\xe3\x84\x20\xfc\xdd\xcb\x38\x61\xfb\x4f\x29\xbd\xf4\x66\x69\xf4\xde\x9b\xdf\x8c\xeb\x1a\x1f\x87\x86\xd0\x52\x4f\x41\x33\x35\xd8\x9f\x61\xdb\xf7\x78\x7c\xc6\xee\xf9\x15\x4f\x8f\x9f\x5f\x2b\xa5\x46\x6d\xbe\xe9\x96\x90\x12\xaa\x2f\x97\xef\x9d\x3e\x10\xe6\x59\x29\x77\x18\x87\xc0\x58\xab\x62\x65\xbd\x6e\x57\xaa\x58\xb5\x8e\xbb\x69\x5f\x99\xe1\x50\x4f\xc1\xea\x23\xd5\xc6\xbb\x95\x2a\x55\x4a\x08\xba\x6f\x09\x77\x6e\x83\x3b\x79\x8e\xed\x03\xaa\x4f\x5e\xb7\x51\xcc\xea\x5a\x22\x96\x42\x75\x0d\x90\x1a\x5c\x04\x77\x84\x45\xc0\xe7\x91\xc0\x9d\x66\x9c\x82\x1e\x23\x8c\x77\x55\x56\xc4\x03\xb4\xf7\xc3\x49\x5c\xed\x10\x30\x70\x47\x01\x47\xed\x27\x8a\x52\xdc\x13\xe2\x48\xc6\x59\x47\x8d\x5a\x5c\xb3\x36\x91\xc3\x64\x18\x49\x15\xb7\x92\x54\x11\x89\x71\xbf\x5c\xcb\xf9\x85\x58\xcd\x4a\x62\x77\x74\xca\x7a\x9a\x40\x9a\x29\x42\xa3\xa7\x53\x36\x56\xd9\xa9\x37\xb7\xf4\x6b\xeb\x6f\x62\x97\xb8\xcf\x46\x26\x55\x04\xe2\x29\xf4\x78\x97\xab\xa7\x6c\x17\x5b\x58\xbf\x41\x24\xde\xa2\x77\x1e\xf3\x15\xeb\xc3\x38\xfa\x33\xa2\x3e\xd2\x8f\xbd\xbc\x10\x2f\x53\xf6\x9a\x29\x60\x8a\xf2\xbb\x18\xed\x7d\xdc\xc8\x93\xfe\xf2\x2d\x62\x11\xc8\xe6\x46\x6a\xb2\x99\xd5\xe2\x7e\xe1\x5f\xdb\x3c\x4d\x79\x69\x61\xfd\xc7\xdc\x4b\xe1\xb4\x95\xdc\x3f\x48\xdf\x72\xb8\x1d\x22\xfa\xf2\x67\xa6\xaf\x8e\xbb\xa7\x10\x86\xf0\x7f\xe1\xde\x62\xfe\x89\xf2\xed\x75\x0e\x97\x96\x6e\x7f\x87\xbe\x6e\xfa\x6f\xec\xbf\xb8\xca\x10\x52\x02\xf5\x0d\xe6\xf9\x7b\x00\x00\x00\xff\xff\x6d\x3d\x9e\xe0\x14\x04\x00\x00"), - }, - "/templates/cli_flags_generated.gotpl": &vfsgenÛ°CompressedFileInfo{ - name: "cli_flags_generated.gotpl", - modTime: time.Time{}, - uncompressedSize: 2638, - - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x55\x4d\x6f\xe3\x36\x10\x3d\x4b\xbf\x62\x6a\xec\x41\x0a\x5c\xfb\xde\xc5\x9e\xe2\xcd\x76\x81\x62\x37\xe8\xa6\xb9\xd3\xd2\x50\x26\x42\x93\x0a\x39\x4a\x1a\x18\xfe\xef\xc5\x90\xd4\x87\x3f\x10\xfb\x92\x9e\x2c\x72\x66\xde\x7b\x7c\x33\xa4\x97\x4b\xb8\xb5\x35\x42\x83\x06\x9d\x20\xac\x61\xfd\x06\xb2\xf9\x0c\xab\x9f\xf0\xe3\xe7\x03\x7c\x5d\x7d\x7f\x58\xe4\x79\x2b\xaa\x27\xd1\x20\xec\x76\xb0\xb8\x8f\xdf\x3f\xc4\x16\x61\xbf\xcf\x73\xb5\x6d\xad\x23\x28\xf2\x6c\x26\xb5\x68\x66\xfc\xbb\x25\xfe\xf1\xe4\x2a\x6b\x5e\xf8\x93\xd4\x16\x67\x79\x99\xef\x76\xe0\x84\x69\x10\x3e\xa9\x39\x7c\xe2\x7c\xf8\xe3\x0b\x2c\xee\xb4\x68\x3c\xa3\x2d\x97\xcc\x11\x02\x8b\xc4\xc0\x31\x50\x1e\x04\x84\xf4\x57\x45\x1b\xa0\xb7\x16\xc7\xc4\x07\x5e\xed\xf7\xc3\x7a\x65\x2b\x12\x4a\x33\xde\x61\xe2\x14\xd1\x93\xeb\x2a\x82\x5d\x9e\xf1\x6e\x96\x79\x72\xca\x34\x79\xf6\x8f\x17\xcd\x64\xf9\xd5\xbc\x3c\x0a\x37\xae\xef\x94\xc6\x7b\x41\x9b\x61\xe3\x6f\x7c\xee\x94\xc3\x3a\x5b\x5b\xab\xf3\xec\x4f\x55\xd7\x68\xb2\xb4\xda\xed\x7e\x07\x25\x01\x9f\x7b\xa5\xe2\x09\x3d\x43\x00\xb9\x2e\xd8\x97\x0d\x5b\x93\x12\x34\x75\x88\x1d\x97\x3f\x0a\xdd\x4d\x4a\xc3\x32\x3b\xb6\xe1\x7d\x88\x15\x7a\x52\x46\x90\xb2\x66\x04\x9a\x6c\x66\x37\xef\xe2\xed\x73\x6e\xd1\xaf\x70\x76\x70\x48\x9d\x33\xdc\x19\x87\xa2\x16\x6b\x8d\xe0\xb0\x75\xe8\xd1\x50\x64\xb0\x12\x68\xa3\x3c\xbc\xb0\x52\xae\x2c\xa4\x75\xd0\xb1\xc5\x50\xa3\x14\x9d\x26\x5f\xe6\xb2\x33\x15\x14\xf2\x6c\x9f\xca\x44\x56\x94\x10\x1d\xe7\x8e\x45\x62\xe0\x78\x8c\xa2\x2b\x64\x99\xc4\x7d\x43\x0a\xf5\xbd\x3a\xda\x20\x18\xde\x08\x62\x30\x4c\xd1\x05\xca\x04\x71\x8e\x53\x86\xcc\x44\xf5\xdd\xf7\xdd\x1f\xd8\x5e\x37\x48\x1b\x74\x60\x1d\x18\x4b\x03\x21\x0f\xb0\x4b\xb9\x17\xc8\x47\xd0\xa2\x04\x9e\x89\x03\xf6\x3e\x96\x14\x84\xe9\x89\x63\x31\x9c\x97\xbb\x3a\x39\x2b\x10\xe7\x80\x88\x4d\x98\x83\x65\x7d\xaf\xca\x23\x48\xa1\x3d\x5e\x50\x33\x12\x9c\xaa\x19\x2a\xa2\x80\x61\x3c\xbe\x21\x85\x5b\x74\xd0\x82\xd8\xf4\xe4\x27\x4f\xc1\xf5\xbd\x08\x60\xe7\x9b\x11\x42\x23\xed\x91\x13\x89\x21\xcd\x1f\x08\xdf\x43\x1c\x8d\xa9\x30\x35\x08\x03\xb8\x6d\xe9\x8d\x91\x52\x96\x3a\x31\xd1\xd8\x1e\x8a\x40\x68\xbd\xb8\xac\xbd\xb7\x6e\xd4\x7e\xe8\x5a\xba\x4a\x83\x77\xc7\x40\xa0\xad\x7d\xf2\xd0\xb5\x41\x4a\x24\xb7\x12\x04\x68\x5b\x09\x7d\x96\x77\xde\x1b\x70\x80\x77\x6b\x0d\xe1\xbf\xb4\x8a\xb7\x8e\x91\x95\x0c\x23\x2a\x6d\x67\xfa\x99\xac\xe0\x26\xe5\x95\x27\xd0\x45\xb8\x44\xf1\x1c\x21\xca\xf5\x08\x3d\x70\x78\x2d\x66\xb3\xe9\x3b\x3c\x8d\x84\x6d\xd4\xfe\xe0\xa1\x1e\x22\xfd\xfb\x32\xe9\x2d\x9f\xbb\x6b\xcf\x8a\x98\x43\xb5\xe0\xcd\x5f\x48\xc3\xa5\xd7\x76\x2d\xf4\xd5\xe6\x35\x21\xfd\x23\xdd\x3b\x2f\xe8\xff\xf2\x50\x49\x90\x9e\xff\x59\xa3\x8b\x51\xcc\x5d\xb4\xac\xb7\xb0\xfc\xcc\x39\xbf\x7d\x01\xa3\xc2\xa5\xbe\xca\x78\xe9\xcb\x3c\xdb\x9f\xbe\x00\x27\xfe\x70\x5f\x82\x2b\xef\xc0\x25\x23\xe6\xe0\x91\xe0\x26\x84\x93\xc6\x8f\xf4\x46\xb2\x2f\x1e\x69\xf1\x57\x50\x16\x94\x94\xd1\xb2\xa9\x1b\xa3\x80\x7b\xe1\x3c\xba\xc8\xdd\xf2\x77\x3d\x07\x74\x8e\x61\x06\xa6\x94\x33\x55\x71\x94\x2a\xe3\x7d\x0f\x6b\xeb\x0a\xa3\x74\xc9\xb9\xe9\x6f\x9a\xe9\x39\x71\x22\xe0\x0a\x8f\x33\x6e\xc5\x89\xd2\x5b\xe1\x29\xaa\x3d\x46\x98\xc4\xa7\x4a\x53\x5a\x14\x3c\x11\x75\x65\x9f\xc7\x8a\xff\x02\x00\x00\xff\xff\x73\xc9\x52\x5f\x4e\x0a\x00\x00"), - }, - } - fs["/"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ - fs["/source"].(os.FileInfo), - fs["/templates"].(os.FileInfo), - } - fs["/source"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ - fs["/source/flag-types.json"].(os.FileInfo), - } - fs["/templates"].(*vfsgenÛ°DirInfo).entries = []os.FileInfo{ - fs["/templates/altsrc_flags_generated.gotpl"].(os.FileInfo), - fs["/templates/cli_flags_generated.gotpl"].(os.FileInfo), - } - - return fs -}() - -type vfsgenÛ°FS map[string]interface{} - -func (fs vfsgenÛ°FS) Open(path string) (http.File, error) { - path = pathpkg.Clean("/" + path) - f, ok := fs[path] - if !ok { - return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist} - } - - switch f := f.(type) { - case *vfsgenÛ°CompressedFileInfo: - gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent)) - if err != nil { - // This should never happen because we generate the gzip bytes such that they are always valid. - panic("unexpected error reading own gzip compressed bytes: " + err.Error()) - } - return &vfsgenÛ°CompressedFile{ - vfsgenÛ°CompressedFileInfo: f, - gr: gr, - }, nil - case *vfsgenÛ°DirInfo: - return &vfsgenÛ°Dir{ - vfsgenÛ°DirInfo: f, - }, nil - default: - // This should never happen because we generate only the above types. - panic(fmt.Sprintf("unexpected type %T", f)) - } -} - -// vfsgenÛ°CompressedFileInfo is a static definition of a gzip compressed file. -type vfsgenÛ°CompressedFileInfo struct { - name string - modTime time.Time - compressedContent []byte - uncompressedSize int64 -} - -func (f *vfsgenÛ°CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) { - return nil, fmt.Errorf("cannot Readdir from file %s", f.name) -} -func (f *vfsgenÛ°CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil } - -func (f *vfsgenÛ°CompressedFileInfo) GzipBytes() []byte { - return f.compressedContent -} - -func (f *vfsgenÛ°CompressedFileInfo) Name() string { return f.name } -func (f *vfsgenÛ°CompressedFileInfo) Size() int64 { return f.uncompressedSize } -func (f *vfsgenÛ°CompressedFileInfo) Mode() os.FileMode { return 0444 } -func (f *vfsgenÛ°CompressedFileInfo) ModTime() time.Time { return f.modTime } -func (f *vfsgenÛ°CompressedFileInfo) IsDir() bool { return false } -func (f *vfsgenÛ°CompressedFileInfo) Sys() interface{} { return nil } - -// vfsgenÛ°CompressedFile is an opened compressedFile instance. -type vfsgenÛ°CompressedFile struct { - *vfsgenÛ°CompressedFileInfo - gr *gzip.Reader - grPos int64 // Actual gr uncompressed position. - seekPos int64 // Seek uncompressed position. -} - -func (f *vfsgenÛ°CompressedFile) Read(p []byte) (n int, err error) { - if f.grPos > f.seekPos { - // Rewind to beginning. - err = f.gr.Reset(bytes.NewReader(f.compressedContent)) - if err != nil { - return 0, err - } - f.grPos = 0 - } - if f.grPos < f.seekPos { - // Fast-forward. - _, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos) - if err != nil { - return 0, err - } - f.grPos = f.seekPos - } - n, err = f.gr.Read(p) - f.grPos += int64(n) - f.seekPos = f.grPos - return n, err -} -func (f *vfsgenÛ°CompressedFile) Seek(offset int64, whence int) (int64, error) { - switch whence { - case io.SeekStart: - f.seekPos = 0 + offset - case io.SeekCurrent: - f.seekPos += offset - case io.SeekEnd: - f.seekPos = f.uncompressedSize + offset - default: - panic(fmt.Errorf("invalid whence value: %v", whence)) - } - return f.seekPos, nil -} -func (f *vfsgenÛ°CompressedFile) Close() error { - return f.gr.Close() -} - -// vfsgenÛ°DirInfo is a static definition of a directory. -type vfsgenÛ°DirInfo struct { - name string - modTime time.Time - entries []os.FileInfo -} - -func (d *vfsgenÛ°DirInfo) Read([]byte) (int, error) { - return 0, fmt.Errorf("cannot Read from directory %s", d.name) -} -func (d *vfsgenÛ°DirInfo) Close() error { return nil } -func (d *vfsgenÛ°DirInfo) Stat() (os.FileInfo, error) { return d, nil } - -func (d *vfsgenÛ°DirInfo) Name() string { return d.name } -func (d *vfsgenÛ°DirInfo) Size() int64 { return 0 } -func (d *vfsgenÛ°DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir } -func (d *vfsgenÛ°DirInfo) ModTime() time.Time { return d.modTime } -func (d *vfsgenÛ°DirInfo) IsDir() bool { return true } -func (d *vfsgenÛ°DirInfo) Sys() interface{} { return nil } - -// vfsgenÛ°Dir is an opened dir instance. -type vfsgenÛ°Dir struct { - *vfsgenÛ°DirInfo - pos int // Position within entries for Seek and Readdir. -} - -func (d *vfsgenÛ°Dir) Seek(offset int64, whence int) (int64, error) { - if offset == 0 && whence == io.SeekStart { - d.pos = 0 - return 0, nil - } - return 0, fmt.Errorf("unsupported Seek in directory %s", d.name) -} - -func (d *vfsgenÛ°Dir) Readdir(count int) ([]os.FileInfo, error) { - if d.pos >= len(d.entries) && count > 0 { - return nil, io.EOF - } - if count <= 0 || count > len(d.entries)-d.pos { - count = len(d.entries) - d.pos - } - e := d.entries[d.pos : d.pos+count] - d.pos += count - return e, nil -} diff --git a/flag-gen/go.mod b/flag-gen/go.mod deleted file mode 100644 index c93d180..0000000 --- a/flag-gen/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/urfave/cli/flag-generator - -go 1.12 - -require ( - github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 - github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd - github.com/urfave/cli v1.21.0 -) diff --git a/flag-gen/go.sum b/flag-gen/go.sum deleted file mode 100644 index e68bf88..0000000 --- a/flag-gen/go.sum +++ /dev/null @@ -1,9 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE= -github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/flag-gen/main.go b/flag-gen/main.go deleted file mode 100644 index 52cc3b1..0000000 --- a/flag-gen/main.go +++ /dev/null @@ -1,115 +0,0 @@ -package main - -//go:generate go run assets_generate.go - -import ( - "encoding/json" - "fmt" - "github.com/urfave/cli" - "io/ioutil" - "log" - "os" - "text/template" -) - -type CliFlagInfo struct { - PackageName string - Flags []FlagType -} - -type FlagType struct { - Name string `json:"name"` - Type string `json:"type"` - Value bool `json:"value"` - Destination bool `json:"dest"` - Doctail string `json:"doctail"` - ContextDefault string `json:"context_default"` - ContextType string `json:"context_type"` - Parser string `json:"parser"` - ParserCast string `json:"parser_cast"` - ValueString string `json:"valueString"` - TakesFile bool `json:"takes_file"` -} - -func main() { - app := cli.NewApp() - - app.Name = "flag-generator" - app.Usage = "Generate flag type code!" - - app.Action = ActionFunc - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } -} - -func ActionFunc(_ *cli.Context) error { - var info CliFlagInfo - var tpl *template.Template - - inFile, err := assets.Open("/source/flag-types.json") - if err != nil { - log.Fatal(err) - } - - decoder := json.NewDecoder(inFile) - err = decoder.Decode(&info.Flags) - if err != nil { - log.Fatal(err) - } - - err = inFile.Close() - if err != nil { - log.Fatal(err) - } - - for _, packageName := range []string{"cli", "altsrc"} { - info.PackageName = packageName - - bytes, err := ReadTemplate(packageName) - if err != nil { - log.Fatal(err) - } - - tpl = template.Must(template.New("").Parse(string(bytes))) - - var outFile *os.File - - if packageName == "cli" { - outFile, err = os.Create("flag_generated.go") - } else { - outFile, err = os.Create("altsrc/flag_generated.go") - } - if err != nil { - log.Fatal(err) - } - - err = tpl.Execute(outFile, info) - if err != nil { - log.Fatal(err) - } - - err = outFile.Close() - if err != nil { - log.Fatal(err) - } - } - - return nil -} - -func ReadTemplate(packageName string) ([]byte, error) { - templateFile, err := assets.Open(fmt.Sprintf("/templates/%s_flags_generated.gotpl", packageName)) - if err != nil { - return nil, err - } - - templateFileBytes, err := ioutil.ReadAll(templateFile) - if err != nil { - return nil, err - } - - return templateFileBytes, nil -} diff --git a/flag-gen/source/flag-types.json b/flag-gen/source/flag-types.json deleted file mode 100644 index 472e572..0000000 --- a/flag-gen/source/flag-types.json +++ /dev/null @@ -1,139 +0,0 @@ -[ - { - "name": "Bool", - "type": "bool", - "value": false, - "dest": true, - "context_default": "false", - "parser": "strconv.ParseBool(f.Value.String())", - "valueString": "return \"\"", - "takes_file": false - }, - { - "name": "BoolT", - "type": "bool", - "value": false, - "dest": true, - "doctail": " that is true by default", - "context_default": "false", - "parser": "strconv.ParseBool(f.Value.String())", - "valueString": "return \"\"", - "takes_file": false - }, - { - "name": "Duration", - "type": "time.Duration", - "value": true, - "dest": true, - "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", - "context_default": "0", - "parser": "time.ParseDuration(f.Value.String())", - "valueString": "return f.Value.String()", - "takes_file": false - }, - { - "name": "Float64", - "type": "float64", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseFloat(f.Value.String(), 64)", - "valueString": "return fmt.Sprintf(\"%f\", f.Value)", - "takes_file": false - }, - { - "name": "Generic", - "type": "Generic", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "interface{}", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": true - }, - { - "name": "Int64", - "type": "int64", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - }, - { - "name": "Int", - "type": "int", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", - "parser_cast": "int(parsed)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - }, - { - "name": "IntSlice", - "type": "*IntSlice", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "[]int", - "parser": "(f.Value.(*IntSlice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": false - }, - { - "name": "Int64Slice", - "type": "*Int64Slice", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "[]int64", - "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": false - }, - { - "name": "String", - "type": "string", - "value": true, - "dest": true, - "context_default": "\"\"", - "parser": "f.Value.String(), error(nil)", - "valueString": "return f.Value", - "takes_file": true - }, - { - "name": "StringSlice", - "type": "*StringSlice", - "value": true, - "dest": false, - "context_default": "nil", - "context_type": "[]string", - "parser": "(f.Value.(*StringSlice)).Value(), error(nil)", - "valueString": "if f.Value != nil {\n\t\treturn f.Value.String()\n\t}\n\treturn \"\"", - "takes_file": true - }, - { - "name": "Uint64", - "type": "uint64", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - }, - { - "name": "Uint", - "type": "uint", - "value": true, - "dest": true, - "context_default": "0", - "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", - "parser_cast": "uint(parsed)", - "valueString": "return fmt.Sprintf(\"%d\", f.Value)", - "takes_file": false - } -] diff --git a/flag-gen/templates/altsrc_flags_generated.gotpl b/flag-gen/templates/altsrc_flags_generated.gotpl deleted file mode 100644 index 07f6e82..0000000 --- a/flag-gen/templates/altsrc_flags_generated.gotpl +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package {{ .PackageName }} - -import ( - "flag" - "github.com/urfave/cli" -) -{{ range $i, $flag := .Flags }} -// {{ $flag.Name }}Flag is the flag type that wraps cli.{{ $flag.Name }}Flag to allow -// for other values to be specified -type {{ $flag.Name }}Flag struct { - cli.{{ $flag.Name }}Flag - set *flag.FlagSet -} - -// New{{ $flag.Name }}Flag creates a new {{ $flag.Name }}Flag -func New{{ $flag.Name }}Flag(fl cli.{{ $flag.Name }}Flag) *{{ $flag.Name }}Flag { - return &{{ $flag.Name }}Flag{ {{ $flag.Name }}Flag: fl, set: nil } -} - -// Apply saves the flagSet for later usage calls, then calls -// the wrapped {{ $flag.Name }}Flag.Apply -func (f *{{ $flag.Name }}Flag) Apply(set *flag.FlagSet) { - f.set = set - f.{{ $flag.Name }}Flag.Apply(set) -} - -// ApplyWithError saves the flagSet for later usage calls, then calls -// the wrapped {{ $flag.Name }}Flag.ApplyWithError -func (f *{{ $flag.Name }}Flag) ApplyWithError(set *flag.FlagSet) error { - f.set = set - return f.{{ $flag.Name }}Flag.ApplyWithError(set) -} -{{ end }} \ No newline at end of file diff --git a/flag-gen/templates/cli_flags_generated.gotpl b/flag-gen/templates/cli_flags_generated.gotpl deleted file mode 100644 index 61d06f0..0000000 --- a/flag-gen/templates/cli_flags_generated.gotpl +++ /dev/null @@ -1,89 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package {{ .PackageName }} - -import ( - "flag" - "fmt" - "strconv" - "time" -) -{{ range $i, $flag := .Flags }} -// {{ $flag.Name }}Flag is a flag with type {{ $flag.Type }}{{ $flag.Doctail }} -type {{ $flag.Name }}Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - {{- if eq $flag.TakesFile true }} - TakesFile bool - {{- end }} - {{- if eq $flag.Value true }} - Value {{ $flag.Type }} - {{- end }} - {{- if eq $flag.Destination true }} - Destination *{{ $flag.Type }} - {{- end }} -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f {{ $flag.Name }}Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f {{ $flag.Name }}Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f {{ $flag.Name }}Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f {{ $flag.Name }}Flag) TakesValue() bool { - return {{ $flag.Value }} -} - -// GetUsage returns the usage string for the flag -func (f {{ $flag.Name }}Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f {{ $flag.Name }}Flag) GetValue() string { - {{ $flag.ValueString }} -} - -// {{ $flag.Name }} looks up the value of a local {{ $flag.Name }}Flag, returns -// {{ $flag.ContextDefault }} if not found -func (c *Context) {{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { - return lookup{{ $flag.Name }}(name, c.flagSet) -} - -// Global{{ $flag.Name }} looks up the value of a global {{ $flag.Name }}Flag, returns -// {{ $flag.ContextDefault }} if not found -func (c *Context) Global{{ $flag.Name }}(name string) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookup{{ $flag.Name }}(name, fs) - } - return {{ $flag.ContextDefault }} -} - -func lookup{{ $flag.Name }}(name string, set *flag.FlagSet) {{ if ne .ContextType "" }}{{ $flag.ContextType }}{{ else }}{{ $flag.Type }}{{- end }} { - f := set.Lookup(name) - if f != nil { - {{ if ne .Parser "" }}parsed, err := {{ $flag.Parser }}{{ else }}parsed, err := f.Value, error(nil){{ end }} - if err != nil { - return {{ $flag.ContextDefault }} - } - {{ if ne .ParserCast "" }}return {{ $flag.ParserCast }}{{ else }}return parsed{{ end }} - } - return {{ $flag.ContextDefault }} -} -{{ end }} diff --git a/flag.go b/flag.go index be55a33..3c05270 100644 --- a/flag.go +++ b/flag.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" "syscall" - "time" ) const defaultPlaceholder = "value" @@ -132,469 +131,6 @@ func eachName(longName string, fn func(string)) { } } -// Generic is a generic parseable type identified by a specific flag -type Generic interface { - Set(value string) error - String() string -} - -// 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 fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - if err := val.Set(fileEnvVal); err != nil { - return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) - } - } - - 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 and flag.Getter -type StringSlice []string - -// Set appends the string value to the list of values -func (f *StringSlice) Set(value string) error { - *f = append(*f, value) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *StringSlice) String() string { - return fmt.Sprintf("%s", *f) -} - -// Value returns the slice of strings set by this flag -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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - newVal := &StringSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(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) - } - } - if f.Value == nil { - f.Value = newVal - } else { - *f.Value = *newVal - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &StringSlice{} - } - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// 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 -func (f *IntSlice) Set(value string) error { - tmp, err := strconv.Atoi(value) - if err != nil { - return err - } - *f = append(*f, tmp) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *IntSlice) String() string { - return fmt.Sprintf("%#v", *f) -} - -// Value returns the slice of ints set by this flag -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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - newVal := &IntSlice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - 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) - } - } - if f.Value == nil { - f.Value = newVal - } else { - *f.Value = *newVal - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &IntSlice{} - } - set.Var(f.Value, name, f.Usage) - }) - - return nil -} - -// 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 -func (f *Int64Slice) Set(value string) error { - tmp, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - *f = append(*f, tmp) - return nil -} - -// String returns a readable representation of this value (for usage defaults) -func (f *Int64Slice) String() string { - return fmt.Sprintf("%#v", *f) -} - -// Value returns the slice of ints set by this flag -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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - newVal := &Int64Slice{} - for _, s := range strings.Split(envVal, ",") { - s = strings.TrimSpace(s) - 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) - } - } - if f.Value == nil { - f.Value = newVal - } else { - *f.Value = *newVal - } - } - - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &Int64Slice{} - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - if envVal == "" { - val = false - } else { - 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 - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.BoolVar(f.Destination, name, val, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - if envVal == "" { - val = false - } else { - 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 - } - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.BoolVar(f.Destination, name, val, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - f.Value = envVal - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.StringVar(f.Destination, name, f.Value, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - 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) { - if f.Destination != nil { - set.IntVar(f.Destination, name, f.Value, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseInt(envVal, 0, 64) - 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) { - if f.Destination != nil { - set.Int64Var(f.Destination, name, f.Value, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - 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) { - if f.Destination != nil { - set.UintVar(f.Destination, name, f.Value, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValInt, err := strconv.ParseUint(envVal, 0, 64) - if err != nil { - return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) - } - - f.Value = envValInt - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Uint64Var(f.Destination, name, f.Value, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - envValDuration, err := time.ParseDuration(envVal) - 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) { - if f.Destination != nil { - set.DurationVar(f.Destination, name, f.Value, f.Usage) - return - } - 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { - 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 = envValFloat - } - - eachName(f.Name, func(name string) { - if f.Destination != nil { - set.Float64Var(f.Destination, name, f.Value, f.Usage) - return - } - set.Float64(name, f.Value, f.Usage) - }) - - return nil -} - func visibleFlags(fl []Flag) []Flag { var visible []Flag for _, f := range fl { diff --git a/flag_bool.go b/flag_bool.go new file mode 100644 index 0000000..2499b0b --- /dev/null +++ b/flag_bool.go @@ -0,0 +1,109 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// BoolFlag is a flag with type bool +type BoolFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f BoolFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f BoolFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f BoolFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f BoolFlag) TakesValue() bool { + return false +} + +// GetUsage returns the usage string for the flag +func (f BoolFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolFlag) GetValue() string { + return "" +} + +// Bool looks up the value of a local BoolFlag, returns +// false if not found +func (c *Context) Bool(name string) bool { + return lookupBool(name, c.flagSet) +} + +// GlobalBool looks up the value of a global BoolFlag, returns +// false if not found +func (c *Context) GlobalBool(name string) bool { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupBool(name, fs) + } + return false +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if envVal == "" { + val = false + } else { + 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 + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.BoolVar(f.Destination, name, val, f.Usage) + return + } + set.Bool(name, val, f.Usage) + }) + + return nil +} + +func lookupBool(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return parsed + } + return false +} diff --git a/flag_bool_t.go b/flag_bool_t.go new file mode 100644 index 0000000..cd0888f --- /dev/null +++ b/flag_bool_t.go @@ -0,0 +1,110 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// BoolTFlag is a flag with type bool that is true by default +type BoolTFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Destination *bool +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f BoolTFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f BoolTFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f BoolTFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f BoolTFlag) TakesValue() bool { + return false +} + +// GetUsage returns the usage string for the flag +func (f BoolTFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f BoolTFlag) GetValue() string { + return "" +} + +// BoolT looks up the value of a local BoolTFlag, returns +// false if not found +func (c *Context) BoolT(name string) bool { + return lookupBoolT(name, c.flagSet) +} + +// GlobalBoolT looks up the value of a global BoolTFlag, returns +// false if not found +func (c *Context) GlobalBoolT(name string) bool { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupBoolT(name, fs) + } + return false +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if envVal == "" { + val = false + } else { + 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 + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.BoolVar(f.Destination, name, val, f.Usage) + return + } + set.Bool(name, val, f.Usage) + }) + + return nil +} + +func lookupBoolT(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return parsed + } + return false +} diff --git a/flag_duration.go b/flag_duration.go new file mode 100644 index 0000000..df4ade5 --- /dev/null +++ b/flag_duration.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "time" +) + +// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) +type DurationFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value time.Duration + Destination *time.Duration +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f DurationFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f DurationFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f DurationFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f DurationFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f DurationFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f DurationFlag) GetValue() string { + return f.Value.String() +} + +// Duration looks up the value of a local DurationFlag, returns +// 0 if not found +func (c *Context) Duration(name string) time.Duration { + return lookupDuration(name, c.flagSet) +} + +// GlobalDuration looks up the value of a global DurationFlag, returns +// 0 if not found +func (c *Context) GlobalDuration(name string) time.Duration { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupDuration(name, fs) + } + return 0 +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValDuration, err := time.ParseDuration(envVal) + 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) { + if f.Destination != nil { + set.DurationVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Duration(name, f.Value, f.Usage) + }) + + return nil +} + +func lookupDuration(name string, set *flag.FlagSet) time.Duration { + f := set.Lookup(name) + if f != nil { + parsed, err := time.ParseDuration(f.Value.String()) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/flag_float64.go b/flag_float64.go new file mode 100644 index 0000000..65398d3 --- /dev/null +++ b/flag_float64.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// Float64Flag is a flag with type float64 +type Float64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value float64 + Destination *float64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Float64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Float64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Float64Flag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Float64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Float64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Float64Flag) GetValue() string { + return fmt.Sprintf("%f", f.Value) +} + +// Float64 looks up the value of a local Float64Flag, returns +// 0 if not found +func (c *Context) Float64(name string) float64 { + return lookupFloat64(name, c.flagSet) +} + +// GlobalFloat64 looks up the value of a global Float64Flag, returns +// 0 if not found +func (c *Context) GlobalFloat64(name string) float64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupFloat64(name, fs) + } + return 0 +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + 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 = envValFloat + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Float64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Float64(name, f.Value, f.Usage) + }) + + return nil +} + +func lookupFloat64(name string, set *flag.FlagSet) float64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseFloat(f.Value.String(), 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/flag_generated.go b/flag_generated.go deleted file mode 100644 index 9dcdfd6..0000000 --- a/flag_generated.go +++ /dev/null @@ -1,943 +0,0 @@ -// Code generated by fg; DO NOT EDIT. - -package cli - -import ( - "flag" - "fmt" - "strconv" - "time" -) - -// BoolFlag is a flag with type bool -type BoolFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f BoolFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f BoolFlag) TakesValue() bool { - return false -} - -// GetUsage returns the usage string for the flag -func (f BoolFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f BoolFlag) GetValue() string { - return "" -} - -// Bool looks up the value of a local BoolFlag, returns -// false if not found -func (c *Context) Bool(name string) bool { - return lookupBool(name, c.flagSet) -} - -// GlobalBool looks up the value of a global BoolFlag, returns -// false if not found -func (c *Context) GlobalBool(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBool(name, fs) - } - return false -} - -func lookupBool(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// BoolTFlag is a flag with type bool that is true by default -type BoolTFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Destination *bool -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f BoolTFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f BoolTFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f BoolTFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f BoolTFlag) TakesValue() bool { - return false -} - -// GetUsage returns the usage string for the flag -func (f BoolTFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f BoolTFlag) GetValue() string { - return "" -} - -// BoolT looks up the value of a local BoolTFlag, returns -// false if not found -func (c *Context) BoolT(name string) bool { - return lookupBoolT(name, c.flagSet) -} - -// GlobalBoolT looks up the value of a global BoolTFlag, returns -// false if not found -func (c *Context) GlobalBoolT(name string) bool { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupBoolT(name, fs) - } - return false -} - -func lookupBoolT(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return parsed - } - return false -} - -// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) -type DurationFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value time.Duration - Destination *time.Duration -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f DurationFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f DurationFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f DurationFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f DurationFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f DurationFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f DurationFlag) GetValue() string { - return f.Value.String() -} - -// Duration looks up the value of a local DurationFlag, returns -// 0 if not found -func (c *Context) Duration(name string) time.Duration { - return lookupDuration(name, c.flagSet) -} - -// GlobalDuration looks up the value of a global DurationFlag, returns -// 0 if not found -func (c *Context) GlobalDuration(name string) time.Duration { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupDuration(name, fs) - } - return 0 -} - -func lookupDuration(name string, set *flag.FlagSet) time.Duration { - f := set.Lookup(name) - if f != nil { - parsed, err := time.ParseDuration(f.Value.String()) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// Float64Flag is a flag with type float64 -type Float64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value float64 - Destination *float64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Float64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Float64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Float64Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Float64Flag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Float64Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Float64Flag) GetValue() string { - return fmt.Sprintf("%f", f.Value) -} - -// Float64 looks up the value of a local Float64Flag, returns -// 0 if not found -func (c *Context) Float64(name string) float64 { - return lookupFloat64(name, c.flagSet) -} - -// GlobalFloat64 looks up the value of a global Float64Flag, returns -// 0 if not found -func (c *Context) GlobalFloat64(name string) float64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupFloat64(name, fs) - } - return 0 -} - -func lookupFloat64(name string, set *flag.FlagSet) float64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseFloat(f.Value.String(), 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// GenericFlag is a flag with type Generic -type GenericFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value Generic -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f GenericFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f GenericFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f GenericFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f GenericFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f GenericFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f GenericFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// Generic looks up the value of a local GenericFlag, returns -// nil if not found -func (c *Context) Generic(name string) interface{} { - return lookupGeneric(name, c.flagSet) -} - -// GlobalGeneric looks up the value of a global GenericFlag, returns -// nil if not found -func (c *Context) GlobalGeneric(name string) interface{} { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupGeneric(name, fs) - } - return nil -} - -func lookupGeneric(name string, set *flag.FlagSet) interface{} { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value, error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64Flag is a flag with type int64 -type Int64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int64 - Destination *int64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Int64Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Int64Flag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Int64Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Int64Flag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Int64 looks up the value of a local Int64Flag, returns -// 0 if not found -func (c *Context) Int64(name string) int64 { - return lookupInt64(name, c.flagSet) -} - -// GlobalInt64 looks up the value of a global Int64Flag, returns -// 0 if not found -func (c *Context) GlobalInt64(name string) int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64(name, fs) - } - return 0 -} - -func lookupInt64(name string, set *flag.FlagSet) int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// IntFlag is a flag with type int -type IntFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value int - Destination *int -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f IntFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f IntFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f IntFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f IntFlag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Int looks up the value of a local IntFlag, returns -// 0 if not found -func (c *Context) Int(name string) int { - return lookupInt(name, c.flagSet) -} - -// GlobalInt looks up the value of a global IntFlag, returns -// 0 if not found -func (c *Context) GlobalInt(name string) int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt(name, fs) - } - return 0 -} - -func lookupInt(name string, set *flag.FlagSet) int { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return int(parsed) - } - return 0 -} - -// IntSliceFlag is a flag with type *IntSlice -type IntSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *IntSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f IntSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f IntSliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f IntSliceFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f IntSliceFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f IntSliceFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f IntSliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// IntSlice looks up the value of a local IntSliceFlag, returns -// nil if not found -func (c *Context) IntSlice(name string) []int { - return lookupIntSlice(name, c.flagSet) -} - -// GlobalIntSlice looks up the value of a global IntSliceFlag, returns -// nil if not found -func (c *Context) GlobalIntSlice(name string) []int { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupIntSlice(name, fs) - } - return nil -} - -func lookupIntSlice(name string, set *flag.FlagSet) []int { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Int64SliceFlag is a flag with type *Int64Slice -type Int64SliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value *Int64Slice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Int64SliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Int64SliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Int64SliceFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Int64SliceFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Int64SliceFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Int64SliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// Int64Slice looks up the value of a local Int64SliceFlag, returns -// nil if not found -func (c *Context) Int64Slice(name string) []int64 { - return lookupInt64Slice(name, c.flagSet) -} - -// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns -// nil if not found -func (c *Context) GlobalInt64Slice(name string) []int64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupInt64Slice(name, fs) - } - return nil -} - -func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// StringFlag is a flag with type string -type StringFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value string - Destination *string -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f StringFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f StringFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f StringFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f StringFlag) GetValue() string { - return f.Value -} - -// String looks up the value of a local StringFlag, returns -// "" if not found -func (c *Context) String(name string) string { - return lookupString(name, c.flagSet) -} - -// GlobalString looks up the value of a global StringFlag, returns -// "" if not found -func (c *Context) GlobalString(name string) string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupString(name, fs) - } - return "" -} - -func lookupString(name string, set *flag.FlagSet) string { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value.String(), error(nil) - if err != nil { - return "" - } - return parsed - } - return "" -} - -// StringSliceFlag is a flag with type *StringSlice -type StringSliceFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value *StringSlice -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f StringSliceFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f StringSliceFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f StringSliceFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f StringSliceFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f StringSliceFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f StringSliceFlag) GetValue() string { - if f.Value != nil { - return f.Value.String() - } - return "" -} - -// StringSlice looks up the value of a local StringSliceFlag, returns -// nil if not found -func (c *Context) StringSlice(name string) []string { - return lookupStringSlice(name, c.flagSet) -} - -// GlobalStringSlice looks up the value of a global StringSliceFlag, returns -// nil if not found -func (c *Context) GlobalStringSlice(name string) []string { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupStringSlice(name, fs) - } - return nil -} - -func lookupStringSlice(name string, set *flag.FlagSet) []string { - f := set.Lookup(name) - if f != nil { - parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) - if err != nil { - return nil - } - return parsed - } - return nil -} - -// Uint64Flag is a flag with type uint64 -type Uint64Flag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint64 - Destination *uint64 -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f Uint64Flag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f Uint64Flag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f Uint64Flag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f Uint64Flag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f Uint64Flag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f Uint64Flag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Uint64 looks up the value of a local Uint64Flag, returns -// 0 if not found -func (c *Context) Uint64(name string) uint64 { - return lookupUint64(name, c.flagSet) -} - -// GlobalUint64 looks up the value of a global Uint64Flag, returns -// 0 if not found -func (c *Context) GlobalUint64(name string) uint64 { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint64(name, fs) - } - return 0 -} - -func lookupUint64(name string, set *flag.FlagSet) uint64 { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return parsed - } - return 0 -} - -// UintFlag is a flag with type uint -type UintFlag struct { - Name string - Usage string - EnvVar string - FilePath string - Required bool - Hidden bool - Value uint - Destination *uint -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f UintFlag) String() string { - return FlagStringer(f) -} - -// GetName returns the name of the flag -func (f UintFlag) GetName() string { - return f.Name -} - -// IsRequired returns whether or not the flag is required -func (f UintFlag) IsRequired() bool { - return f.Required -} - -// TakesValue returns true of the flag takes a value, otherwise false -func (f UintFlag) TakesValue() bool { - return true -} - -// GetUsage returns the usage string for the flag -func (f UintFlag) GetUsage() string { - return f.Usage -} - -// GetValue returns the flags value as string representation and an empty -// string if the flag takes no value at all. -func (f UintFlag) GetValue() string { - return fmt.Sprintf("%d", f.Value) -} - -// Uint looks up the value of a local UintFlag, returns -// 0 if not found -func (c *Context) Uint(name string) uint { - return lookupUint(name, c.flagSet) -} - -// GlobalUint looks up the value of a global UintFlag, returns -// 0 if not found -func (c *Context) GlobalUint(name string) uint { - if fs := lookupGlobalFlagSet(name, c); fs != nil { - return lookupUint(name, fs) - } - return 0 -} - -func lookupUint(name string, set *flag.FlagSet) uint { - f := set.Lookup(name) - if f != nil { - parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) - if err != nil { - return 0 - } - return uint(parsed) - } - return 0 -} - diff --git a/flag_generic.go b/flag_generic.go new file mode 100644 index 0000000..c43dae7 --- /dev/null +++ b/flag_generic.go @@ -0,0 +1,110 @@ +package cli + +import ( + "flag" + "fmt" +) + +// Generic is a generic parseable type identified by a specific flag +type Generic interface { + Set(value string) error + String() string +} + +// GenericFlag is a flag with type Generic +type GenericFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + TakesFile bool + Value Generic +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f GenericFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f GenericFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f GenericFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f GenericFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f GenericFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f GenericFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// 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 fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + if err := val.Set(fileEnvVal); err != nil { + return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) + } + } + + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) + + return nil +} + +// Generic looks up the value of a local GenericFlag, returns +// nil if not found +func (c *Context) Generic(name string) interface{} { + return lookupGeneric(name, c.flagSet) +} + +// GlobalGeneric looks up the value of a global GenericFlag, returns +// nil if not found +func (c *Context) GlobalGeneric(name string) interface{} { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupGeneric(name, fs) + } + return nil +} + +func lookupGeneric(name string, set *flag.FlagSet) interface{} { + f := set.Lookup(name) + if f != nil { + parsed, err := f.Value, error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_int.go b/flag_int.go new file mode 100644 index 0000000..bae32e2 --- /dev/null +++ b/flag_int.go @@ -0,0 +1,105 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// IntFlag is a flag with type int +type IntFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int + Destination *int +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f IntFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f IntFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f IntFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f IntFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f IntFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntFlag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + 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) { + if f.Destination != nil { + set.IntVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Int(name, f.Value, f.Usage) + }) + + return nil +} + +// Int looks up the value of a local IntFlag, returns +// 0 if not found +func (c *Context) Int(name string) int { + return lookupInt(name, c.flagSet) +} + +// GlobalInt looks up the value of a global IntFlag, returns +// 0 if not found +func (c *Context) GlobalInt(name string) int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt(name, fs) + } + return 0 +} + +func lookupInt(name string, set *flag.FlagSet) int { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return int(parsed) + } + return 0 +} diff --git a/flag_int64.go b/flag_int64.go new file mode 100644 index 0000000..aaafbe9 --- /dev/null +++ b/flag_int64.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// Int64Flag is a flag with type int64 +type Int64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value int64 + Destination *int64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Int64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Int64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Int64Flag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Int64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Int64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64Flag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + 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) { + if f.Destination != nil { + set.Int64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Int64(name, f.Value, f.Usage) + }) + + return nil +} + +// Int64 looks up the value of a local Int64Flag, returns +// 0 if not found +func (c *Context) Int64(name string) int64 { + return lookupInt64(name, c.flagSet) +} + +// GlobalInt64 looks up the value of a global Int64Flag, returns +// 0 if not found +func (c *Context) GlobalInt64(name string) int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64(name, fs) + } + return 0 +} + +func lookupInt64(name string, set *flag.FlagSet) int64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/flag_int64_slice.go b/flag_int64_slice.go new file mode 100644 index 0000000..ed2e983 --- /dev/null +++ b/flag_int64_slice.go @@ -0,0 +1,141 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" + "strings" +) + +// 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 +func (f *Int64Slice) Set(value string) error { + tmp, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + *f = append(*f, tmp) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *Int64Slice) String() string { + return fmt.Sprintf("%#v", *f) +} + +// Value returns the slice of ints set by this flag +func (f *Int64Slice) Value() []int64 { + return *f +} + +// Get returns the slice of ints set by this flag +func (f *Int64Slice) Get() interface{} { + return *f +} + +// Int64SliceFlag is a flag with type *Int64Slice +type Int64SliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *Int64Slice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Int64SliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Int64SliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Int64SliceFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Int64SliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Int64SliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Int64SliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &Int64Slice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + 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) + } + } + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &Int64Slice{} + } + set.Var(f.Value, name, f.Usage) + }) + return nil +} + +// Int64Slice looks up the value of a local Int64SliceFlag, returns +// nil if not found +func (c *Context) Int64Slice(name string) []int64 { + return lookupInt64Slice(name, c.flagSet) +} + +// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns +// nil if not found +func (c *Context) GlobalInt64Slice(name string) []int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64Slice(name, fs) + } + return nil +} + +func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_int_slice.go b/flag_int_slice.go new file mode 100644 index 0000000..c38d010 --- /dev/null +++ b/flag_int_slice.go @@ -0,0 +1,142 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" + "strings" +) + +// 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 +func (f *IntSlice) Set(value string) error { + tmp, err := strconv.Atoi(value) + if err != nil { + return err + } + *f = append(*f, tmp) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *IntSlice) String() string { + return fmt.Sprintf("%#v", *f) +} + +// Value returns the slice of ints set by this flag +func (f *IntSlice) Value() []int { + return *f +} + +// Get returns the slice of ints set by this flag +func (f *IntSlice) Get() interface{} { + return *f +} + +// IntSliceFlag is a flag with type *IntSlice +type IntSliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value *IntSlice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f IntSliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f IntSliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f IntSliceFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f IntSliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f IntSliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f IntSliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &IntSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + 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) + } + } + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &IntSlice{} + } + set.Var(f.Value, name, f.Usage) + }) + + return nil +} + +// IntSlice looks up the value of a local IntSliceFlag, returns +// nil if not found +func (c *Context) IntSlice(name string) []int { + return lookupIntSlice(name, c.flagSet) +} + +// GlobalIntSlice looks up the value of a global IntSliceFlag, returns +// nil if not found +func (c *Context) GlobalIntSlice(name string) []int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupIntSlice(name, fs) + } + return nil +} + +func lookupIntSlice(name string, set *flag.FlagSet) []int { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_string.go b/flag_string.go new file mode 100644 index 0000000..9f29da4 --- /dev/null +++ b/flag_string.go @@ -0,0 +1,98 @@ +package cli + +import "flag" + +// StringFlag is a flag with type string +type StringFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + TakesFile bool + Value string + Destination *string +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f StringFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f StringFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f StringFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f StringFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f StringFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringFlag) GetValue() string { + return f.Value +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + f.Value = envVal + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.StringVar(f.Destination, name, f.Value, f.Usage) + return + } + set.String(name, f.Value, f.Usage) + }) + + return nil +} + +// String looks up the value of a local StringFlag, returns +// "" if not found +func (c *Context) String(name string) string { + return lookupString(name, c.flagSet) +} + +// GlobalString looks up the value of a global StringFlag, returns +// "" if not found +func (c *Context) GlobalString(name string) string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupString(name, fs) + } + return "" +} + +func lookupString(name string, set *flag.FlagSet) string { + f := set.Lookup(name) + if f != nil { + parsed, err := f.Value.String(), error(nil) + if err != nil { + return "" + } + return parsed + } + return "" +} diff --git a/flag_string_slice.go b/flag_string_slice.go new file mode 100644 index 0000000..e865b2f --- /dev/null +++ b/flag_string_slice.go @@ -0,0 +1,138 @@ +package cli + +import ( + "flag" + "fmt" + "strings" +) + +// 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 +func (f *StringSlice) Set(value string) error { + *f = append(*f, value) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *StringSlice) String() string { + return fmt.Sprintf("%s", *f) +} + +// Value returns the slice of strings set by this flag +func (f *StringSlice) Value() []string { + return *f +} + +// Get returns the slice of strings set by this flag +func (f *StringSlice) Get() interface{} { + return *f +} + +// StringSliceFlag is a flag with type *StringSlice +type StringSliceFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + TakesFile bool + Value *StringSlice +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f StringSliceFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f StringSliceFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f StringSliceFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f StringSliceFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f StringSliceFlag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f StringSliceFlag) GetValue() string { + if f.Value != nil { + return f.Value.String() + } + return "" +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + newVal := &StringSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(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) + } + } + if f.Value == nil { + f.Value = newVal + } else { + *f.Value = *newVal + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &StringSlice{} + } + set.Var(f.Value, name, f.Usage) + }) + + return nil +} + +// StringSlice looks up the value of a local StringSliceFlag, returns +// nil if not found +func (c *Context) StringSlice(name string) []string { + return lookupStringSlice(name, c.flagSet) +} + +// GlobalStringSlice looks up the value of a global StringSliceFlag, returns +// nil if not found +func (c *Context) GlobalStringSlice(name string) []string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupStringSlice(name, fs) + } + return nil +} + +func lookupStringSlice(name string, set *flag.FlagSet) []string { + f := set.Lookup(name) + if f != nil { + parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) + if err != nil { + return nil + } + return parsed + } + return nil +} diff --git a/flag_uint.go b/flag_uint.go new file mode 100644 index 0000000..d6a04f4 --- /dev/null +++ b/flag_uint.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// UintFlag is a flag with type uint +type UintFlag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint + Destination *uint +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f UintFlag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f UintFlag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f UintFlag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f UintFlag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f UintFlag) GetUsage() string { + return f.Usage +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + 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) { + if f.Destination != nil { + set.UintVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint(name, f.Value, f.Usage) + }) + + return nil +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f UintFlag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// Uint looks up the value of a local UintFlag, returns +// 0 if not found +func (c *Context) Uint(name string) uint { + return lookupUint(name, c.flagSet) +} + +// GlobalUint looks up the value of a global UintFlag, returns +// 0 if not found +func (c *Context) GlobalUint(name string) uint { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint(name, fs) + } + return 0 +} + +func lookupUint(name string, set *flag.FlagSet) uint { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return uint(parsed) + } + return 0 +} diff --git a/flag_uint64.go b/flag_uint64.go new file mode 100644 index 0000000..ea6493a --- /dev/null +++ b/flag_uint64.go @@ -0,0 +1,106 @@ +package cli + +import ( + "flag" + "fmt" + "strconv" +) + +// Uint64Flag is a flag with type uint64 +type Uint64Flag struct { + Name string + Usage string + EnvVar string + FilePath string + Required bool + Hidden bool + Value uint64 + Destination *uint64 +} + +// String returns a readable representation of this value +// (for usage defaults) +func (f Uint64Flag) String() string { + return FlagStringer(f) +} + +// GetName returns the name of the flag +func (f Uint64Flag) GetName() string { + return f.Name +} + +// IsRequired returns whether or not the flag is required +func (f Uint64Flag) IsRequired() bool { + return f.Required +} + +// TakesValue returns true of the flag takes a value, otherwise false +func (f Uint64Flag) TakesValue() bool { + return true +} + +// GetUsage returns the usage string for the flag +func (f Uint64Flag) GetUsage() string { + return f.Usage +} + +// GetValue returns the flags value as string representation and an empty +// string if the flag takes no value at all. +func (f Uint64Flag) GetValue() string { + return fmt.Sprintf("%d", f.Value) +} + +// 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 envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + if err != nil { + return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) + } + + f.Value = envValInt + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Uint64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint64(name, f.Value, f.Usage) + }) + + return nil +} + +// Uint64 looks up the value of a local Uint64Flag, returns +// 0 if not found +func (c *Context) Uint64(name string) uint64 { + return lookupUint64(name, c.flagSet) +} + +// GlobalUint64 looks up the value of a global Uint64Flag, returns +// 0 if not found +func (c *Context) GlobalUint64(name string) uint64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint64(name, fs) + } + return 0 +} + +func lookupUint64(name string, set *flag.FlagSet) uint64 { + f := set.Lookup(name) + if f != nil { + parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return parsed + } + return 0 +} diff --git a/runtests b/runtests deleted file mode 100755 index b073dc2..0000000 --- a/runtests +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import argparse -import os -import sys -import tempfile - -from subprocess import check_call, check_output - - -PACKAGE_NAME = os.environ.get( - 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' -) - - -def main(sysargs=sys.argv[:]): - targets = { - 'vet': _vet, - 'test': _test, - 'gfmrun': _gfmrun, - 'toc': _toc, - 'gen': _gen, - } - - parser = argparse.ArgumentParser() - parser.add_argument( - 'target', nargs='?', choices=tuple(targets.keys()), default='test' - ) - args = parser.parse_args(sysargs[1:]) - - targets[args.target]() - return 0 - - -def _test(): - coverprofiles = [] - for subpackage in ['', 'altsrc']: - coverprofile = 'cli.coverprofile' - if subpackage != '': - coverprofile = '{}.coverprofile'.format(subpackage) - - coverprofiles.append(coverprofile) - - _run('go test -v'.split() + [ - '-coverprofile={}'.format(coverprofile), - ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') - ]) - - combined_name = _combine_coverprofiles(coverprofiles) - _run('go tool cover -func={}'.format(combined_name)) - os.remove(combined_name) - - -def _gfmrun(): - _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) - - -def _vet(): - _run('go vet ./...') - - -def _toc(): - _run('node_modules/.bin/markdown-toc -i README.md') - _run('git diff --exit-code') - - -def _gen(): - _run('go generate ./...') - _run('git diff --exit-code') - - -def _run(command): - if hasattr(command, 'split'): - command = command.split() - print('runtests: {}'.format(' '.join(command)), file=sys.stderr) - check_call(command) - - -def _gfmrun_count(): - with open('README.md') as infile: - lines = infile.read().splitlines() - return len(filter(_is_go_runnable, lines)) - - -def _is_go_runnable(line): - return line.startswith('package main') - - -def _combine_coverprofiles(coverprofiles): - combined = tempfile.NamedTemporaryFile( - suffix='.coverprofile', delete=False - ) - combined.write('mode: set\n') - - for coverprofile in coverprofiles: - with open(coverprofile, 'r') as infile: - for line in infile.readlines(): - if not line.startswith('mode: '): - combined.write(line) - - combined.flush() - name = combined.name - combined.close() - return name - - -if __name__ == '__main__': - sys.exit(main()) From 5c81af9f10b974cecbec6e20e4976574e86fc78b Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 07:47:18 +0530 Subject: [PATCH 196/215] Remove generate script from travis flow Remove unused dependencies from travis script --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce2a96b..f401fa8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,6 @@ cache: - node_modules before_script: - - go get github.com/shurcooL/vfsgen || true - - go get github.com/shurcooL/httpfs/union || true - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - if [ ! -f node_modules/.bin/markdown-toc ] ; then @@ -25,7 +23,6 @@ before_script: fi script: - - go run build.go generate - go run build.go vet - go run build.go test - go run build.go gfmrun From 4a9e440503f4113a351322da93f8630df14dfcaa Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:12:09 +0530 Subject: [PATCH 197/215] Fix AppVeyor build --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ed74366..6d2dcee 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,13 +14,10 @@ install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env - - go get github.com/shurcooL/vfsgen - - go get github.com/shurcooL/httpfs/union - go get github.com/urfave/gfmrun/... - go get -v -t ./... build_script: - - go run build.go generate - go run build.go vet - go run build.go test - go run build.go gfmrun From afd0ecbbf2fbda2f9459046228ccc9e8d2693258 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:19:06 +0530 Subject: [PATCH 198/215] Add support for Go 1.13 Drop support for Go 1.11 Use md2man v2 to avoid dependency issues when building with Go Modules Enabled Update TravisCI build environment images (trusty was deprecated) Add optional Windows build --- .travis.yml | 16 +++++++++------- docs.go | 2 +- go.mod | 2 +- go.sum | 12 ++++++++---- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce2a96b..7eca52c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,19 @@ language: go sudo: false -dist: trusty -osx_image: xcode8.3 +dist: bionic +osx_image: xcode10 go: - - 1.10.x - 1.11.x - 1.12.x + - 1.13.x os: - linux - osx + - windows + +env: + GO111MODULE=on cache: directories: @@ -20,14 +24,12 @@ before_script: - go get github.com/shurcooL/httpfs/union || true - go get github.com/urfave/gfmrun/... || true - go get golang.org/x/tools/cmd/goimports - - if [ ! -f node_modules/.bin/markdown-toc ] ; then - npm install markdown-toc ; - fi + - npm install markdown-toc script: - go run build.go generate - go run build.go vet - - go run build.go test + - go run build.go testm - go run build.go gfmrun - go run build.go toc diff --git a/docs.go b/docs.go index 0beadf2..ed79b44 100644 --- a/docs.go +++ b/docs.go @@ -8,7 +8,7 @@ import ( "strings" "text/template" - "github.com/cpuguy83/go-md2man/md2man" + "github.com/cpuguy83/go-md2man/v2/md2man" ) // ToMarkdown creates a markdown string for the `*App` diff --git a/go.mod b/go.mod index 1b94cfe..38803d6 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,6 @@ go 1.10 require ( github.com/BurntSushi/toml v0.3.1 - github.com/cpuguy83/go-md2man v1.0.10 + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index a8e436a..ef121ff 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,13 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= From edbf66c25cf83541faee77d0064fdb5ac35a51b1 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:23:04 +0530 Subject: [PATCH 199/215] Update gfmrun import command to suite Go Modules pattern Fix test command typo in travis script --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7eca52c..86394e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,16 +20,14 @@ cache: - node_modules before_script: - - go get github.com/shurcooL/vfsgen || true - - go get github.com/shurcooL/httpfs/union || true - - go get github.com/urfave/gfmrun/... || true + - go get github.com/urfave/gfmrun/cmd/gfmrun - go get golang.org/x/tools/cmd/goimports - npm install markdown-toc script: - go run build.go generate - go run build.go vet - - go run build.go testm + - go run build.go test - go run build.go gfmrun - go run build.go toc From 39bd6176649871817d1966b6b91f042be4b62fe9 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 08:37:16 +0530 Subject: [PATCH 200/215] Cleanup after before_script to avoid git diff errors remove windows build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 86394e5..e978823 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,6 @@ go: os: - linux - osx - - windows env: GO111MODULE=on @@ -23,6 +22,7 @@ before_script: - go get github.com/urfave/gfmrun/cmd/gfmrun - go get golang.org/x/tools/cmd/goimports - npm install markdown-toc + - go mod tidy script: - go run build.go generate From 426e21c150d9a33e4d8c13c2a13c5234e85f3a0e Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Mon, 9 Sep 2019 13:15:47 +0530 Subject: [PATCH 201/215] Update .travis.yml Set GOPROXY in Travis environment --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e978823..bfd07ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ os: env: GO111MODULE=on + GOPROXY=https://proxy.golang.org cache: directories: From 534d60bb9bb0476141540ec77c5a3d51e176d162 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Tue, 10 Sep 2019 06:40:45 +0530 Subject: [PATCH 202/215] Bump minimum supported version of Go to 1.11 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 38803d6..7d04d20 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/urfave/cli -go 1.10 +go 1.11 require ( github.com/BurntSushi/toml v0.3.1 From 054fbefec36cad7425dc6f4cfb6d2963c2710751 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Tue, 10 Sep 2019 06:42:34 +0530 Subject: [PATCH 203/215] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a30fcf..b49ab5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ ## [Unreleased] +## [1.22.1] - 2019-09-10 + +### Changed + +* Remove flag code generation logic, legacy python test runner in [urfave/cli/pull/883](https://github.com/urfave/cli/pull/883) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) +* Enable Go Modules support, drop support for `Go 1.10` add support for `Go 1.13` in [urfave/cli/pull/885](https://github.com/urfave/cli/pull/885) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) + ## [1.22.0] - 2019-09-07 ### Fixed From 487be14dceb185a3321cacd9bf302f5e811f5ee8 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 10 Sep 2019 13:49:11 -0400 Subject: [PATCH 204/215] Don't output hidden commands for man pages Signed-off-by: Michael Crosby --- docs.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs.go b/docs.go index ed79b44..5b94566 100644 --- a/docs.go +++ b/docs.go @@ -57,6 +57,9 @@ func prepareCommands(commands []Command, level int) []string { coms := []string{} for i := range commands { command := &commands[i] + if command.Hidden { + continue + } usage := "" if command.Usage != "" { usage = command.Usage From 5c019b10ca37c1a0b74b637d3b0aa25dfeb0110f Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 05:33:46 +0530 Subject: [PATCH 205/215] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b49ab5f..8b22624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ ## [1.22.1] - 2019-09-10 +### Fixed + +* Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael) + ### Changed * Remove flag code generation logic, legacy python test runner in [urfave/cli/pull/883](https://github.com/urfave/cli/pull/883) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) From 0aee120c32003fff6f320c2a00a41d03285fdde0 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 05:37:41 +0530 Subject: [PATCH 206/215] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b22624..1d734c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -468,7 +468,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[Unreleased]: https://github.com/urfave/cli/compare/v1.22.0...HEAD +[Unreleased]: https://github.com/urfave/cli/compare/v1.22.1...HEAD +[1.22.1]: https://github.com/urfave/cli/compare/v1.22.0...v1.22.1 [1.22.0]: https://github.com/urfave/cli/compare/v1.21.0...v1.22.0 [1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 [1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0 From 7d6a604106e44732edc0a76f4a4800c8c27ddfbe Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 08:59:51 +0530 Subject: [PATCH 207/215] Fix #878 --- context.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index db7cd69..485f529 100644 --- a/context.go +++ b/context.go @@ -313,9 +313,17 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { - key := strings.Split(f.GetName(), ",")[0] - if !context.IsSet(key) { - missingFlags = append(missingFlags, key) + key := strings.Split(f.GetName(), ",") + if len(key) > 1 { + // has short name + if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { + missingFlags = append(missingFlags, key[0]) + } + } else { + // does not have short name + if !context.IsSet(strings.TrimSpace(key[0])) { + missingFlags = append(missingFlags, key[0]) + } } } } From cbb9e015b89225aa090c41085bdb0933f6290d96 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 09:21:45 +0530 Subject: [PATCH 208/215] Improve Code and Add Test Case --- context.go | 6 ++++-- context_test.go | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/context.go b/context.go index 485f529..8f1dcd8 100644 --- a/context.go +++ b/context.go @@ -314,14 +314,16 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { key := strings.Split(f.GetName(), ",") + shortName := strings.TrimSpace(key[0]) if len(key) > 1 { // has short name - if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { + longName := strings.TrimSpace(key[1]) + if !context.IsSet(shortName) && !context.IsSet(longName) { missingFlags = append(missingFlags, key[0]) } } else { // does not have short name - if !context.IsSet(strings.TrimSpace(key[0])) { + if !context.IsSet(shortName) { missingFlags = append(missingFlags, key[0]) } } diff --git a/context_test.go b/context_test.go index 9e594dd..1d52921 100644 --- a/context_test.go +++ b/context_test.go @@ -517,6 +517,13 @@ func TestCheckRequiredFlags(t *testing.T) { }, parseInput: []string{"--requiredFlag", "myinput", "--requiredFlagTwo", "myinput"}, }, + { + testCase: "required_flag_with_short_name", + flags: []Flag{ + StringSliceFlag{Name: "names, N", Required: true}, + }, + parseInput: []string{"-N", "asd", "-N", "qwe"}, + }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { From 36cdaa9964df03e2b8f8d2147a99497536851ad9 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 10:34:00 +0530 Subject: [PATCH 209/215] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d734c4..4a3fd46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,12 @@ ## [Unreleased] -## [1.22.1] - 2019-09-10 +## [1.22.1] - 2019-09-11 ### Fixed * Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael) +* Using short flag names for required flags throws an error in [urfave/cli/pull/890](https://github.com/urfave/cli/pull/890) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) ### Changed From bac5bde38c7725990645cf9b2bf2c824594f3963 Mon Sep 17 00:00:00 2001 From: Sascha Grunert Date: Wed, 11 Sep 2019 09:06:02 +0200 Subject: [PATCH 210/215] Don't generate fish completion for hidden commands Added the missing test case as well. Signed-off-by: Sascha Grunert --- docs_test.go | 3 +++ fish.go | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs_test.go b/docs_test.go index 6ffc995..f18d12d 100644 --- a/docs_test.go +++ b/docs_test.go @@ -53,6 +53,9 @@ func testApp() *App { Usage: "retrieve generic information", }, { Name: "some-command", + }, { + Name: "hidden-command", + Hidden: true, }} app.UsageText = "app [first_arg] [second_arg]" app.Usage = "Some app" diff --git a/fish.go b/fish.go index 1121a20..cf183af 100644 --- a/fish.go +++ b/fish.go @@ -69,6 +69,10 @@ func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, pre for i := range commands { command := &commands[i] + if command.Hidden { + continue + } + var completion strings.Builder completion.WriteString(fmt.Sprintf( "complete -r -c %s -n '%s' -a '%s'", From 71eaf37e337d5daea12c6a137113c71056151530 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:09:50 +0530 Subject: [PATCH 211/215] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a3fd46..7c1a27e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixed * Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael) +* Don't generate fish completion for hidden commands [urfave/cli/pull/891](https://github.com/urfave/891) via [@saschagrunert](https://github.com/saschagrunert) * Using short flag names for required flags throws an error in [urfave/cli/pull/890](https://github.com/urfave/cli/pull/890) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) ### Changed From 1547ac2f6a3d3d39fe4d49570c0d1c2401a8f20e Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:15:20 +0530 Subject: [PATCH 212/215] Modify variable names --- context.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index 8f1dcd8..485f529 100644 --- a/context.go +++ b/context.go @@ -314,16 +314,14 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { key := strings.Split(f.GetName(), ",") - shortName := strings.TrimSpace(key[0]) if len(key) > 1 { // has short name - longName := strings.TrimSpace(key[1]) - if !context.IsSet(shortName) && !context.IsSet(longName) { + if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { missingFlags = append(missingFlags, key[0]) } } else { // does not have short name - if !context.IsSet(shortName) { + if !context.IsSet(strings.TrimSpace(key[0])) { missingFlags = append(missingFlags, key[0]) } } From c6ee3b4904ed76d34f277c315c2097ae7b22d38f Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:34:41 +0530 Subject: [PATCH 213/215] Use iterative logic to determine missing flag --- context.go | 21 ++++++++++++--------- context_test.go | 7 +++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index 485f529..2f18f3f 100644 --- a/context.go +++ b/context.go @@ -313,18 +313,21 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { - key := strings.Split(f.GetName(), ",") - if len(key) > 1 { - // has short name - if !context.IsSet(strings.TrimSpace(key[0])) && !context.IsSet(strings.TrimSpace(key[1])) { - missingFlags = append(missingFlags, key[0]) + var flagPresent bool + var flagName string + for _, key := range strings.Split(f.GetName(), ",") { + if len(key) > 1 { + flagName = key } - } else { - // does not have short name - if !context.IsSet(strings.TrimSpace(key[0])) { - missingFlags = append(missingFlags, key[0]) + + if context.IsSet(strings.TrimSpace(key)) { + flagPresent = true } } + + if !flagPresent { + missingFlags = append(missingFlags, flagName) + } } } diff --git a/context_test.go b/context_test.go index 1d52921..13c3701 100644 --- a/context_test.go +++ b/context_test.go @@ -524,6 +524,13 @@ func TestCheckRequiredFlags(t *testing.T) { }, parseInput: []string{"-N", "asd", "-N", "qwe"}, }, + { + testCase: "required_flag_with_short_name", + flags: []Flag{ + StringSliceFlag{Name: "names, N, n", Required: true}, + }, + parseInput: []string{"-n", "asd", "-n", "qwe"}, + }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { From f8bb66ae7d679973cf9b3f6f8c3dc6933404a31a Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 14:42:38 +0530 Subject: [PATCH 214/215] Fix Typo --- context_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context_test.go b/context_test.go index 13c3701..28f5e08 100644 --- a/context_test.go +++ b/context_test.go @@ -525,7 +525,7 @@ func TestCheckRequiredFlags(t *testing.T) { parseInput: []string{"-N", "asd", "-N", "qwe"}, }, { - testCase: "required_flag_with_short_name", + testCase: "required_flag_with_multiple_short_names", flags: []Flag{ StringSliceFlag{Name: "names, N, n", Required: true}, }, From fa858dcc260fb07c25aab13650d9fa0e64f851c7 Mon Sep 17 00:00:00 2001 From: Ajitem Sahasrabuddhe Date: Wed, 11 Sep 2019 15:10:14 +0530 Subject: [PATCH 215/215] Ensure flag is not blank --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index 2f18f3f..ecfc032 100644 --- a/context.go +++ b/context.go @@ -325,7 +325,7 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { } } - if !flagPresent { + if !flagPresent && flagName != "" { missingFlags = append(missingFlags, flagName) } }