From ef9430e77eb1e7e05daecdfaa9ec75fa43b79e7e Mon Sep 17 00:00:00 2001 From: Ally Dale Date: Mon, 8 Feb 2021 20:10:28 +0800 Subject: [PATCH 01/22] fix #1238: accept multi-value input on sclice flags --- app_test.go | 34 ++++++++++++++++++++++++++++++ docs/v2/manual.md | 46 +++++++++++++++++++++++++++++++++++++++++ flag.go | 4 ++++ flag_float64_slice.go | 12 ++++++----- flag_int64_slice.go | 14 +++++++------ flag_int_slice.go | 14 +++++++------ flag_string_slice.go | 6 ++++-- flag_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 159 insertions(+), 19 deletions(-) diff --git a/app_test.go b/app_test.go index 7c38f60489..0b8b4bfae0 100644 --- a/app_test.go +++ b/app_test.go @@ -390,6 +390,40 @@ func ExampleApp_Run_zshComplete() { // h:Shows a list of commands or help for one command } +func ExampleApp_Run_sliceValues() { + // set args for examples sake + os.Args = []string{"multi_values", + "--stringSclice", "parsed1,parsed2", "--stringSclice", "parsed3,parsed4", + "--float64Sclice", "13.3,14.4", "--float64Sclice", "15.5,16.6", + "--int64Sclice", "13,14", "--int64Sclice", "15,16", + "--intSclice", "13,14", "--intSclice", "15,16", + } + app := NewApp() + app.Name = "multi_values" + app.Flags = []Flag{ + &StringSliceFlag{Name: "stringSclice"}, + &Float64SliceFlag{Name: "float64Sclice"}, + &Int64SliceFlag{Name: "int64Sclice"}, + &IntSliceFlag{Name: "intSclice"}, + } + app.Action = func(ctx *Context) error { + for i, v := range ctx.FlagNames() { + fmt.Printf("%d-%s %#v\n", i, v, ctx.Value(v)) + } + err := ctx.Err() + fmt.Println("error:", err) + return err + } + + _ = app.Run(os.Args) + // Output: + // 0-float64Sclice cli.Float64Slice{slice:[]float64{13.3, 14.4, 15.5, 16.6}, hasBeenSet:true} + // 1-int64Sclice cli.Int64Slice{slice:[]int64{13, 14, 15, 16}, hasBeenSet:true} + // 2-intSclice cli.IntSlice{slice:[]int{13, 14, 15, 16}, hasBeenSet:true} + // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, hasBeenSet:true} + // error: +} + func TestApp_Run(t *testing.T) { s := "" diff --git a/docs/v2/manual.md b/docs/v2/manual.md index af09010b9a..8f7cbbb4ca 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -14,6 +14,7 @@ cli v2 manual + [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) + + [Muti values](#multi-values) + [Required Flags](#required-flags) + [Default Values for help output](#default-values-for-help-output) + [Precedence](#precedence) @@ -660,6 +661,51 @@ func main() { } ``` +#### Multi values + +Slice flags(Float64SliceFlag, Int64SliceFlag, IntSliceFlag, StringSliceFlag) are designed to accept multi values. + +Here is a sample of setting multi values for slice flags: + + +``` +package main + +import ( + "fmt" + "os" + "github.com/urfave/cli/v2" +) + +func main() { + os.Args = []string{"multi_values", + "--stringSclice", "parsed1,parsed2", "--stringSclice", "parsed3,parsed4", + "--float64Sclice", "13.3,14.4", "--float64Sclice", "15.5,16.6", + "--int64Sclice", "13,14", "--int64Sclice", "15,16", + "--intSclice", "13,14", "--intSclice", "15,16", + } + app := cli.NewApp() + app.Name = "multi_values" + app.Flags = []cli.Flag{ + &cli.StringSliceFlag{Name: "stringSclice"}, + &cli.Float64SliceFlag{Name: "float64Sclice"}, + &cli.Int64SliceFlag{Name: "int64Sclice"}, + &cli.IntSliceFlag{Name: "intSclice"}, + } + app.Action = func(ctx *cli.Context) error { + for i, v := range ctx.FlagNames() { + fmt.Printf("%d-%s %#v\n", i, v, ctx.Value(v)) + } + return ctx.Err() + } + + _ = app.Run(os.Args) +} + +``` + #### Required Flags You can make a flag required by setting the `Required` field to `true`. If a user diff --git a/flag.go b/flag.go index aff8d5be63..bca41cc316 100644 --- a/flag.go +++ b/flag.go @@ -390,3 +390,7 @@ func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool) } return "", false } + +func flagSplitMultiValues(val string) []string { + return strings.Split(val, ",") +} diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 706ee6cd4b..8ee1204ec3 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -33,12 +33,14 @@ func (f *Float64Slice) Set(value string) error { return nil } - tmp, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } + for _, s := range flagSplitMultiValues(value) { + tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64) + if err != nil { + return err + } - f.slice = append(f.slice, tmp) + f.slice = append(f.slice, tmp) + } return nil } diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 2c9a15af3e..2edc3c6be6 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -33,12 +33,14 @@ func (i *Int64Slice) Set(value string) error { return nil } - tmp, err := strconv.ParseInt(value, 0, 64) - if err != nil { - return err - } + for _, s := range flagSplitMultiValues(value) { + tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) + if err != nil { + return err + } - i.slice = append(i.slice, tmp) + i.slice = append(i.slice, tmp) + } return nil } @@ -123,7 +125,7 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { f.Value = &Int64Slice{} - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := f.Value.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int64 slice value for flag %s: %s", val, f.Name, err) } diff --git a/flag_int_slice.go b/flag_int_slice.go index a73ca6b81c..8861649337 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -44,12 +44,14 @@ func (i *IntSlice) Set(value string) error { return nil } - tmp, err := strconv.ParseInt(value, 0, 64) - if err != nil { - return err - } + for _, s := range flagSplitMultiValues(value) { + tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) + if err != nil { + return err + } - i.slice = append(i.slice, int(tmp)) + i.slice = append(i.slice, int(tmp)) + } return nil } @@ -134,7 +136,7 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { f.Value = &IntSlice{} - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := f.Value.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int slice value for flag %s: %s", val, f.Name, err) } diff --git a/flag_string_slice.go b/flag_string_slice.go index 35497032cb..92ded57c7e 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -32,7 +32,9 @@ func (s *StringSlice) Set(value string) error { return nil } - s.slice = append(s.slice, value) + for _, t := range flagSplitMultiValues(value) { + s.slice = append(s.slice, strings.TrimSpace(t)) + } return nil } @@ -132,7 +134,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { destination = f.Destination } - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := destination.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as string value for flag %s: %s", val, f.Name, err) } diff --git a/flag_test.go b/flag_test.go index b3b0d7c587..1afc63188c 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1973,3 +1973,51 @@ func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) { err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"}) expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\"")) } + +type flagValueTestCase struct { + name string + flag Flag + toParse []string + expect string +} + +func TestFlagValue(t *testing.T) { + cases := []*flagValueTestCase{ + &flagValueTestCase{ + name: "stringSclice", + flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")}, + toParse: []string{"--flag", "parsed,parsed2", "--flag", "parsed3,parsed4"}, + expect: `[parsed parsed2 parsed3 parsed4]`, + }, + &flagValueTestCase{ + name: "float64Sclice", + flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)}, + toParse: []string{"--flag", "13.3,14.4", "--flag", "15.5,16.6"}, + expect: `[]float64{13.3, 14.4, 15.5, 16.6}`, + }, + &flagValueTestCase{ + name: "int64Sclice", + flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)}, + toParse: []string{"--flag", "13,14", "--flag", "15,16"}, + expect: `[]int64{13, 14, 15, 16}`, + }, + &flagValueTestCase{ + name: "intSclice", + flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)}, + toParse: []string{"--flag", "13,14", "--flag", "15,16"}, + expect: `[]int{13, 14, 15, 16}`, + }, + } + for i, v := range cases { + set := flag.NewFlagSet("test", 0) + set.SetOutput(ioutil.Discard) + _ = v.flag.Apply(set) + if err := set.Parse(v.toParse); err != nil { + t.Error(err) + } + f := set.Lookup("flag") + if got := f.Value.String(); got != v.expect { + t.Errorf("TestFlagValue %d-%s\nexpect:%s\ngot:%s", i, v.name, v.expect, got) + } + } +} From 06f6815b8d881483b4ce75de2471fa7d2b724903 Mon Sep 17 00:00:00 2001 From: Ally Dale Date: Mon, 8 Feb 2021 22:41:34 +0800 Subject: [PATCH 02/22] revert docs/v2/manual.md --- docs/v2/manual.md | 46 ------------------------------------------- flag_float64_slice.go | 2 +- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/docs/v2/manual.md b/docs/v2/manual.md index 8f7cbbb4ca..af09010b9a 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -14,7 +14,6 @@ cli v2 manual + [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) - + [Muti values](#multi-values) + [Required Flags](#required-flags) + [Default Values for help output](#default-values-for-help-output) + [Precedence](#precedence) @@ -661,51 +660,6 @@ func main() { } ``` -#### Multi values - -Slice flags(Float64SliceFlag, Int64SliceFlag, IntSliceFlag, StringSliceFlag) are designed to accept multi values. - -Here is a sample of setting multi values for slice flags: - - -``` -package main - -import ( - "fmt" - "os" - "github.com/urfave/cli/v2" -) - -func main() { - os.Args = []string{"multi_values", - "--stringSclice", "parsed1,parsed2", "--stringSclice", "parsed3,parsed4", - "--float64Sclice", "13.3,14.4", "--float64Sclice", "15.5,16.6", - "--int64Sclice", "13,14", "--int64Sclice", "15,16", - "--intSclice", "13,14", "--intSclice", "15,16", - } - app := cli.NewApp() - app.Name = "multi_values" - app.Flags = []cli.Flag{ - &cli.StringSliceFlag{Name: "stringSclice"}, - &cli.Float64SliceFlag{Name: "float64Sclice"}, - &cli.Int64SliceFlag{Name: "int64Sclice"}, - &cli.IntSliceFlag{Name: "intSclice"}, - } - app.Action = func(ctx *cli.Context) error { - for i, v := range ctx.FlagNames() { - fmt.Printf("%d-%s %#v\n", i, v, ctx.Value(v)) - } - return ctx.Err() - } - - _ = app.Run(os.Args) -} - -``` - #### Required Flags You can make a flag required by setting the `Required` field to `true`. If a user diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 8ee1204ec3..343c9b0d9a 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -125,7 +125,7 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { if val != "" { f.Value = &Float64Slice{} - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := f.Value.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as float64 slice value for flag %s: %s", f.Value, f.Name, err) } From e7157a87d96483a94e51dc53f6db7c400a85f74d Mon Sep 17 00:00:00 2001 From: jolheiser Date: Mon, 18 Apr 2022 20:58:38 -0500 Subject: [PATCH 03/22] Exclude hidden sub-command flags from docs Signed-off-by: jolheiser --- docs.go | 4 ++-- docs_test.go | 5 +++++ fish.go | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs.go b/docs.go index 9f82fc6b0b..e6f0265f04 100644 --- a/docs.go +++ b/docs.go @@ -80,14 +80,14 @@ func prepareCommands(commands []*Command, level int) []string { usageText, ) - flags := prepareArgsWithValues(command.Flags) + flags := prepareArgsWithValues(command.VisibleFlags()) if len(flags) > 0 { prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n")) } coms = append(coms, prepared) - // recursevly iterate subcommands + // recursively iterate subcommands if len(command.Subcommands) > 0 { coms = append( coms, diff --git a/docs_test.go b/docs_test.go index adccbbb9ad..ad714e5723 100644 --- a/docs_test.go +++ b/docs_test.go @@ -103,6 +103,11 @@ Should be a part of the same code block Aliases: []string{"s"}, Usage: "some usage text", }, + &StringFlag{ + Name: "sub-command-hidden-flag", + Usage: "some hidden usage text", + Hidden: true, + }, }, Name: "sub-usage", Usage: "standard usage text", diff --git a/fish.go b/fish.go index 588e070eab..eec3253cda 100644 --- a/fish.go +++ b/fish.go @@ -95,7 +95,7 @@ func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, pr completions = append(completions, completion.String()) completions = append( completions, - a.prepareFishFlags(command.Flags, command.Names())..., + a.prepareFishFlags(command.VisibleFlags(), command.Names())..., ) // recursevly iterate subcommands From 156d47e6964c769561edf7946aa168be6d4d4661 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 28 Mar 2021 08:18:46 -0400 Subject: [PATCH 04/22] Fix: (issue#1254) short options handling needs to proceed from last terminated error --- flag_test.go | 31 +++++++++++++++++++++++++++++++ parse.go | 5 +++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/flag_test.go b/flag_test.go index c563d6f346..f662feefa8 100644 --- a/flag_test.go +++ b/flag_test.go @@ -2059,3 +2059,34 @@ func TestTimestampFlagApply_WithDestination(t *testing.T) { expect(t, err, nil) expect(t, *fl.Destination.timestamp, expectedResult) } + +// Test issue #1254 +// StringSlice() with UseShortOptionHandling causes duplicated entries, depending on the ordering of the flags +func TestSliceShortOptionHandle(t *testing.T) { + _ = (&App{ + Commands: []*Command{ + { + Name: "foobar", + UseShortOptionHandling: true, + Action: func(ctx *Context) error { + if ctx.Bool("i") != true { + t.Errorf("bool i not set") + } + if ctx.Bool("t") != true { + t.Errorf("bool i not set") + } + ss := ctx.StringSlice("net") + if !reflect.DeepEqual(ss, []string{"foo"}) { + t.Errorf("Got different slice(%v) than expected", ss) + } + return nil + }, + Flags: []Flag{ + &StringSliceFlag{Name: "net"}, + &BoolFlag{Name: "i"}, + &BoolFlag{Name: "t"}, + }, + }, + }, + }).Run([]string{"run", "foobar", "--net=foo", "-it"}) +} diff --git a/parse.go b/parse.go index 7df17296a4..57df48bc2a 100644 --- a/parse.go +++ b/parse.go @@ -46,8 +46,9 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple return err } - // swap current argument with the split version - args = append(args[:i], append(shortOpts, args[i+1:]...)...) + // Start processing only from failed argument and not + // from beginning + args = append(shortOpts, args[i+1:]...) argsWereSplit = true break } From dd7065671f833817d58c8f4b9a4c0f46cc4e51c7 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Wed, 28 Apr 2021 20:44:02 -0400 Subject: [PATCH 05/22] Rebase from master --- parse.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parse.go b/parse.go index 57df48bc2a..7df17296a4 100644 --- a/parse.go +++ b/parse.go @@ -46,9 +46,8 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple return err } - // Start processing only from failed argument and not - // from beginning - args = append(shortOpts, args[i+1:]...) + // swap current argument with the split version + args = append(args[:i], append(shortOpts, args[i+1:]...)...) argsWereSplit = true break } From 8c5f1fb359a8f7c2995e823b7248a0ba0b46149f Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 23 Apr 2022 19:30:34 -0400 Subject: [PATCH 06/22] Changes from code review --- flag_test.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/flag_test.go b/flag_test.go index f662feefa8..ecb7847ed3 100644 --- a/flag_test.go +++ b/flag_test.go @@ -2063,17 +2063,19 @@ func TestTimestampFlagApply_WithDestination(t *testing.T) { // Test issue #1254 // StringSlice() with UseShortOptionHandling causes duplicated entries, depending on the ordering of the flags func TestSliceShortOptionHandle(t *testing.T) { - _ = (&App{ + wasCalled := false + err := (&App{ Commands: []*Command{ { Name: "foobar", UseShortOptionHandling: true, Action: func(ctx *Context) error { + wasCalled = true if ctx.Bool("i") != true { - t.Errorf("bool i not set") + t.Error("bool i not set") } if ctx.Bool("t") != true { - t.Errorf("bool i not set") + t.Error("bool i not set") } ss := ctx.StringSlice("net") if !reflect.DeepEqual(ss, []string{"foo"}) { @@ -2089,4 +2091,10 @@ func TestSliceShortOptionHandle(t *testing.T) { }, }, }).Run([]string{"run", "foobar", "--net=foo", "-it"}) + if err != nil { + t.Fatal(err) + } + if !wasCalled { + t.Fatal("Action callback was never called") + } } From 9ce946162f52dff23413c1cb98d817962cf0eb9d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 23 Apr 2022 22:07:16 -0400 Subject: [PATCH 07/22] Unshadow `context` package and consistently name `*cli.Context` vars and method receivers `cCtx` --- altsrc/flag.go | 54 ++++++++++---------- altsrc/json_source_context.go | 6 +-- altsrc/toml_file_loader.go | 8 +-- altsrc/yaml_file_loader.go | 8 +-- app.go | 90 +++++++++++++++++----------------- app_test.go | 8 +-- command.go | 36 +++++++------- command_test.go | 6 +-- context.go | 56 ++++++++++----------- flag_bool.go | 4 +- flag_duration.go | 4 +- flag_float64.go | 4 +- flag_float64_slice.go | 4 +- flag_generic.go | 4 +- flag_int.go | 4 +- flag_int64.go | 4 +- flag_int64_slice.go | 4 +- flag_int_slice.go | 4 +- flag_path.go | 4 +- flag_string.go | 4 +- flag_string_slice.go | 4 +- flag_timestamp.go | 4 +- flag_uint.go | 4 +- flag_uint64.go | 4 +- funcs.go | 4 +- help.go | 92 +++++++++++++++++------------------ 26 files changed, 214 insertions(+), 214 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 31b8a04e87..4b5588ade7 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -13,18 +13,18 @@ import ( // allows a value to be set on the existing parsed flags. type FlagInputSourceExtension interface { cli.Flag - ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error } // ApplyInputSourceValues iterates over all provided flags and // executes ApplyInputSourceValue on flags implementing the // FlagInputSourceExtension interface to initialize these flags // to an alternate input source. -func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error { +func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error { for _, f := range flags { inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension) if isType { - err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext) + err := inputSourceExtendedFlag.ApplyInputSourceValue(cCtx, inputSourceContext) if err != nil { return err } @@ -38,34 +38,34 @@ func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSource // input source based on the func provided. If there is no error it will then apply the new input source to any flags // that are supported by the input source func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc { - return func(context *cli.Context) error { + return func(cCtx *cli.Context) error { inputSource, err := createInputSource() if err != nil { return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error()) } - return ApplyInputSourceValues(context, inputSource, flags) + return ApplyInputSourceValues(cCtx, inputSource, flags) } } // InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new // input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is // no error it will then apply the new input source to any flags that are supported by the input source -func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFunc { - return func(context *cli.Context) error { - inputSource, err := createInputSource(context) +func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc { + return func(cCtx *cli.Context) error { + inputSource, err := createInputSource(cCtx) if err != nil { return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error()) } - return ApplyInputSourceValues(context, inputSource, flags) + return ApplyInputSourceValues(cCtx, inputSource, flags) } } // ApplyInputSourceValue applies a generic value to the flagSet if required -func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { return err @@ -82,9 +82,9 @@ func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourc } // ApplyInputSourceValue applies a StringSlice value to the flagSet if required -func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { return err @@ -104,9 +104,9 @@ func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputS } // ApplyInputSourceValue applies a IntSlice value if required -func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { return err @@ -126,9 +126,9 @@ func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour } // ApplyInputSourceValue applies a Bool value to the flagSet if required -func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { return err @@ -144,9 +144,9 @@ func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo } // ApplyInputSourceValue applies a String value to the flagSet if required -func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.String(f.StringFlag.Name) if err != nil { return err @@ -162,9 +162,9 @@ func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSource } // ApplyInputSourceValue applies a Path value to the flagSet if required -func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.String(f.PathFlag.Name) if err != nil { return err @@ -190,9 +190,9 @@ func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo } // ApplyInputSourceValue applies a int value to the flagSet if required -func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Int(f.IntFlag.Name) if err != nil { return err @@ -208,9 +208,9 @@ func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCon } // ApplyInputSourceValue applies a Duration value to the flagSet if required -func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { return err @@ -226,9 +226,9 @@ func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour } // ApplyInputSourceValue applies a Float64 value to the flagSet if required -func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { return err diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 6e7bf11f91..9307a35870 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -16,9 +16,9 @@ import ( // 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) { - if context.IsSet(flag) { - return NewJSONSourceFromFile(context.String(flag)) + return func(cCtx *cli.Context) (InputSourceContext, error) { + if cCtx.IsSet(flag) { + return NewJSONSourceFromFile(cCtx.String(flag)) } return defaultInputSource() diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index 9b86ee1a63..dfc9b7b1c8 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -85,10 +85,10 @@ func NewTomlSourceFromFile(file string) (InputSourceContext, error) { } // NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. -func NewTomlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { - return func(context *cli.Context) (InputSourceContext, error) { - if context.IsSet(flagFileName) { - filePath := context.String(flagFileName) +func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) { + return func(cCtx *cli.Context) (InputSourceContext, error) { + if cCtx.IsSet(flagFileName) { + filePath := cCtx.String(flagFileName) return NewTomlSourceFromFile(filePath) } diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index a49df56f35..4ace1f23ec 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -31,10 +31,10 @@ func NewYamlSourceFromFile(file string) (InputSourceContext, error) { } // NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context. -func NewYamlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { - return func(context *cli.Context) (InputSourceContext, error) { - if context.IsSet(flagFileName) { - filePath := context.String(flagFileName) +func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) { + return func(cCtx *cli.Context) (InputSourceContext, error) { + if cCtx.IsSet(flagFileName) { + filePath := cCtx.String(flagFileName) return NewYamlSourceFromFile(filePath) } diff --git a/app.go b/app.go index 6abcc08a9b..52ef1b0fa0 100644 --- a/app.go +++ b/app.go @@ -245,48 +245,48 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { err = parseIter(set, a, arguments[1:], shellComplete) nerr := normalizeFlags(a.Flags, set) - context := NewContext(a, set, &Context{Context: ctx}) + cCtx := NewContext(a, set, &Context{Context: ctx}) if nerr != nil { _, _ = fmt.Fprintln(a.Writer, nerr) - _ = ShowAppHelp(context) + _ = ShowAppHelp(cCtx) return nerr } - context.shellComplete = shellComplete + cCtx.shellComplete = shellComplete - if checkCompletions(context) { + if checkCompletions(cCtx) { return nil } if err != nil { if a.OnUsageError != nil { - err := a.OnUsageError(context, err, false) - a.handleExitCoder(context, err) + err := a.OnUsageError(cCtx, err, false) + a.handleExitCoder(cCtx, err) return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - _ = ShowAppHelp(context) + _ = ShowAppHelp(cCtx) return err } - if !a.HideHelp && checkHelp(context) { - _ = ShowAppHelp(context) + if !a.HideHelp && checkHelp(cCtx) { + _ = ShowAppHelp(cCtx) return nil } - if !a.HideVersion && checkVersion(context) { - ShowVersion(context) + if !a.HideVersion && checkVersion(cCtx) { + ShowVersion(cCtx) return nil } - cerr := context.checkRequiredFlags(a.Flags) + cerr := cCtx.checkRequiredFlags(a.Flags) if cerr != nil { - _ = ShowAppHelp(context) + _ = ShowAppHelp(cCtx) return cerr } if a.After != nil { defer func() { - if afterErr := a.After(context); afterErr != nil { + if afterErr := a.After(cCtx); afterErr != nil { if err != nil { err = newMultiError(err, afterErr) } else { @@ -297,20 +297,20 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { } if a.Before != nil { - beforeErr := a.Before(context) + beforeErr := a.Before(cCtx) if beforeErr != nil { - a.handleExitCoder(context, beforeErr) + a.handleExitCoder(cCtx, beforeErr) err = beforeErr return err } } - args := context.Args() + args := cCtx.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { - return c.Run(context) + return c.Run(cCtx) } } @@ -319,9 +319,9 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { } // Run default Action - err = a.Action(context) + err = a.Action(cCtx) - a.handleExitCoder(context, err) + a.handleExitCoder(cCtx, err) return err } @@ -359,55 +359,55 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete) nerr := normalizeFlags(a.Flags, set) - context := NewContext(a, set, ctx) + cCtx := NewContext(a, set, ctx) if nerr != nil { _, _ = fmt.Fprintln(a.Writer, nerr) _, _ = fmt.Fprintln(a.Writer) if len(a.Commands) > 0 { - _ = ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(cCtx) } else { - _ = ShowCommandHelp(ctx, context.Args().First()) + _ = ShowCommandHelp(ctx, cCtx.Args().First()) } return nerr } - if checkCompletions(context) { + if checkCompletions(cCtx) { return nil } if err != nil { if a.OnUsageError != nil { - err = a.OnUsageError(context, err, true) - a.handleExitCoder(context, err) + err = a.OnUsageError(cCtx, err, true) + a.handleExitCoder(cCtx, err) return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - _ = ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(cCtx) return err } if len(a.Commands) > 0 { - if checkSubcommandHelp(context) { + if checkSubcommandHelp(cCtx) { return nil } } else { - if checkCommandHelp(ctx, context.Args().First()) { + if checkCommandHelp(ctx, cCtx.Args().First()) { return nil } } - cerr := context.checkRequiredFlags(a.Flags) + cerr := cCtx.checkRequiredFlags(a.Flags) if cerr != nil { - _ = ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(cCtx) return cerr } if a.After != nil { defer func() { - afterErr := a.After(context) + afterErr := a.After(cCtx) if afterErr != nil { - a.handleExitCoder(context, err) + a.handleExitCoder(cCtx, err) if err != nil { err = newMultiError(err, afterErr) } else { @@ -418,27 +418,27 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } if a.Before != nil { - beforeErr := a.Before(context) + beforeErr := a.Before(cCtx) if beforeErr != nil { - a.handleExitCoder(context, beforeErr) + a.handleExitCoder(cCtx, beforeErr) err = beforeErr return err } } - args := context.Args() + args := cCtx.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { - return c.Run(context) + return c.Run(cCtx) } } // Run default Action - err = a.Action(context) + err = a.Action(cCtx) - a.handleExitCoder(context, err) + a.handleExitCoder(cCtx, err) return err } @@ -498,9 +498,9 @@ func (a *App) appendCommand(c *Command) { } } -func (a *App) handleExitCoder(context *Context, err error) { +func (a *App) handleExitCoder(cCtx *Context, err error) { if a.ExitErrHandler != nil { - a.ExitErrHandler(context, err) + a.ExitErrHandler(cCtx, err) } else { HandleExitCoder(err) } @@ -525,14 +525,14 @@ func (a *Author) String() string { // HandleAction attempts to figure out which Action signature was used. If // 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) { +func HandleAction(action interface{}, cCtx *Context) (err error) { switch a := action.(type) { case ActionFunc: - return a(context) + return a(cCtx) case func(*Context) error: - return a(context) + return a(cCtx) case func(*Context): // deprecated function signature - a(context) + a(cCtx) return nil } diff --git a/app_test.go b/app_test.go index 76e211d681..0829ac6193 100644 --- a/app_test.go +++ b/app_test.go @@ -445,14 +445,14 @@ func TestApp_Setup_defaultsWriter(t *testing.T) { } func TestApp_RunAsSubcommandParseFlags(t *testing.T) { - var context *Context + var cCtx *Context a := &App{ Commands: []*Command{ { Name: "foo", Action: func(c *Context) error { - context = c + cCtx = c return nil }, Flags: []Flag{ @@ -468,8 +468,8 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { } _ = a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) - expect(t, context.Args().Get(0), "abcd") - expect(t, context.String("lang"), "spanish") + expect(t, cCtx.Args().Get(0), "abcd") + expect(t, cCtx.String("lang"), "spanish") } func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { diff --git a/command.go b/command.go index 3477686bb3..ba0169bb11 100644 --- a/command.go +++ b/command.go @@ -105,39 +105,39 @@ func (c *Command) Run(ctx *Context) (err error) { set, err := c.parseFlags(ctx.Args(), ctx.shellComplete) - context := NewContext(ctx.App, set, ctx) - context.Command = c - if checkCommandCompletions(context, c.Name) { + cCtx := NewContext(ctx.App, set, ctx) + cCtx.Command = c + if checkCommandCompletions(cCtx, c.Name) { return nil } if err != nil { if c.OnUsageError != nil { - err = c.OnUsageError(context, err, false) - context.App.handleExitCoder(context, err) + err = c.OnUsageError(cCtx, err, false) + cCtx.App.handleExitCoder(cCtx, err) return err } - _, _ = fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) - _, _ = fmt.Fprintln(context.App.Writer) - _ = ShowCommandHelp(context, c.Name) + _, _ = fmt.Fprintln(cCtx.App.Writer, "Incorrect Usage:", err.Error()) + _, _ = fmt.Fprintln(cCtx.App.Writer) + _ = ShowCommandHelp(cCtx, c.Name) return err } - if checkCommandHelp(context, c.Name) { + if checkCommandHelp(cCtx, c.Name) { return nil } - cerr := context.checkRequiredFlags(c.Flags) + cerr := cCtx.checkRequiredFlags(c.Flags) if cerr != nil { - _ = ShowCommandHelp(context, c.Name) + _ = ShowCommandHelp(cCtx, c.Name) return cerr } if c.After != nil { defer func() { - afterErr := c.After(context) + afterErr := c.After(cCtx) if afterErr != nil { - context.App.handleExitCoder(context, err) + cCtx.App.handleExitCoder(cCtx, err) if err != nil { err = newMultiError(err, afterErr) } else { @@ -148,9 +148,9 @@ func (c *Command) Run(ctx *Context) (err error) { } if c.Before != nil { - err = c.Before(context) + err = c.Before(cCtx) if err != nil { - context.App.handleExitCoder(context, err) + cCtx.App.handleExitCoder(cCtx, err) return err } } @@ -159,11 +159,11 @@ func (c *Command) Run(ctx *Context) (err error) { c.Action = helpSubcommand.Action } - context.Command = c - err = c.Action(context) + cCtx.Command = c + err = c.Action(cCtx) if err != nil { - context.App.handleExitCoder(context, err) + cCtx.App.handleExitCoder(cCtx, err) } return err } diff --git a/command_test.go b/command_test.go index 6add442865..9dfd46f476 100644 --- a/command_test.go +++ b/command_test.go @@ -30,7 +30,7 @@ func TestCommandFlagParsing(t *testing.T) { set := flag.NewFlagSet("test", 0) _ = set.Parse(c.testArgs) - context := NewContext(app, set, nil) + cCtx := NewContext(app, set, nil) command := Command{ Name: "test-cmd", @@ -41,10 +41,10 @@ func TestCommandFlagParsing(t *testing.T) { SkipFlagParsing: c.skipFlagParsing, } - err := command.Run(context) + err := command.Run(cCtx) expect(t, err, c.expectedErr) - expect(t, context.Args().Slice(), c.testArgs) + expect(t, cCtx.Args().Slice(), c.testArgs) } } diff --git a/context.go b/context.go index da090e8256..6b497ed20d 100644 --- a/context.go +++ b/context.go @@ -40,18 +40,18 @@ func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { } // NumFlags returns the number of flags set -func (c *Context) NumFlags() int { - return c.flagSet.NFlag() +func (cCtx *Context) NumFlags() int { + return cCtx.flagSet.NFlag() } // Set sets a context flag to a value. -func (c *Context) Set(name, value string) error { - return c.flagSet.Set(name, value) +func (cCtx *Context) Set(name, value string) error { + return cCtx.flagSet.Set(name, value) } // IsSet determines if the flag was actually set -func (c *Context) IsSet(name string) bool { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) IsSet(name string) bool { + if fs := cCtx.lookupFlagSet(name); fs != nil { isSet := false fs.Visit(func(f *flag.Flag) { if f.Name == name { @@ -62,7 +62,7 @@ func (c *Context) IsSet(name string) bool { return true } - f := c.lookupFlag(name) + f := cCtx.lookupFlag(name) if f == nil { return false } @@ -74,28 +74,28 @@ func (c *Context) IsSet(name string) bool { } // LocalFlagNames returns a slice of flag names used in this context. -func (c *Context) LocalFlagNames() []string { +func (cCtx *Context) LocalFlagNames() []string { var names []string - c.flagSet.Visit(makeFlagNameVisitor(&names)) + cCtx.flagSet.Visit(makeFlagNameVisitor(&names)) return names } // FlagNames returns a slice of flag names used by the this context and all of // its parent contexts. -func (c *Context) FlagNames() []string { +func (cCtx *Context) FlagNames() []string { var names []string - for _, ctx := range c.Lineage() { - ctx.flagSet.Visit(makeFlagNameVisitor(&names)) + for _, pCtx := range cCtx.Lineage() { + pCtx.flagSet.Visit(makeFlagNameVisitor(&names)) } return names } // Lineage returns *this* context and all of its ancestor contexts in order from // child to parent -func (c *Context) Lineage() []*Context { +func (cCtx *Context) Lineage() []*Context { var lineage []*Context - for cur := c; cur != nil; cur = cur.parentContext { + for cur := cCtx; cur != nil; cur = cur.parentContext { lineage = append(lineage, cur) } @@ -103,26 +103,26 @@ func (c *Context) Lineage() []*Context { } // Value returns the value of the flag corresponding to `name` -func (c *Context) Value(name string) interface{} { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Value(name string) interface{} { + if fs := cCtx.lookupFlagSet(name); fs != nil { return fs.Lookup(name).Value.(flag.Getter).Get() } return nil } // Args returns the command line arguments associated with the context. -func (c *Context) Args() Args { - ret := args(c.flagSet.Args()) +func (cCtx *Context) Args() Args { + ret := args(cCtx.flagSet.Args()) return &ret } // NArg returns the number of the command line arguments. -func (c *Context) NArg() int { - return c.Args().Len() +func (cCtx *Context) NArg() int { + return cCtx.Args().Len() } -func (ctx *Context) lookupFlag(name string) Flag { - for _, c := range ctx.Lineage() { +func (cCtx *Context) lookupFlag(name string) Flag { + for _, c := range cCtx.Lineage() { if c.Command == nil { continue } @@ -136,8 +136,8 @@ func (ctx *Context) lookupFlag(name string) Flag { } } - if ctx.App != nil { - for _, f := range ctx.App.Flags { + if cCtx.App != nil { + for _, f := range cCtx.App.Flags { for _, n := range f.Names() { if n == name { return f @@ -149,8 +149,8 @@ func (ctx *Context) lookupFlag(name string) Flag { return nil } -func (ctx *Context) lookupFlagSet(name string) *flag.FlagSet { - for _, c := range ctx.Lineage() { +func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet { + for _, c := range cCtx.Lineage() { if c.flagSet == nil { continue } @@ -162,7 +162,7 @@ func (ctx *Context) lookupFlagSet(name string) *flag.FlagSet { return nil } -func (context *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { +func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { @@ -174,7 +174,7 @@ func (context *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { flagName = key } - if context.IsSet(strings.TrimSpace(key)) { + if cCtx.IsSet(strings.TrimSpace(key)) { flagPresent = true } } diff --git a/flag_bool.go b/flag_bool.go index b8e625a1d1..6e832f964b 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -104,8 +104,8 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error { // Bool looks up the value of a local BoolFlag, returns // false if not found -func (c *Context) Bool(name string) bool { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Bool(name string) bool { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupBool(name, fs) } return false diff --git a/flag_duration.go b/flag_duration.go index e8ca15ec02..d30bd1d6ce 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -103,8 +103,8 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error { // Duration looks up the value of a local DurationFlag, returns // 0 if not found -func (c *Context) Duration(name string) time.Duration { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Duration(name string) time.Duration { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupDuration(name, fs) } return 0 diff --git a/flag_float64.go b/flag_float64.go index 0ac5b43f0d..f11bef3a7b 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -103,8 +103,8 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error { // Float64 looks up the value of a local Float64Flag, returns // 0 if not found -func (c *Context) Float64(name string) float64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Float64(name string) float64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupFloat64(name, fs) } return 0 diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 984f77f7f3..d573ae5f35 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -177,8 +177,8 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { // Float64Slice looks up the value of a local Float64SliceFlag, returns // nil if not found -func (c *Context) Float64Slice(name string) []float64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Float64Slice(name string) []float64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupFloat64Slice(name, fs) } return nil diff --git a/flag_generic.go b/flag_generic.go index d159507147..e44e7d7517 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -106,8 +106,8 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error { // Generic looks up the value of a local GenericFlag, returns // nil if not found -func (c *Context) Generic(name string) interface{} { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Generic(name string) interface{} { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupGeneric(name, fs) } return nil diff --git a/flag_int.go b/flag_int.go index 62c0848688..2c4e602711 100644 --- a/flag_int.go +++ b/flag_int.go @@ -104,8 +104,8 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error { // Int looks up the value of a local IntFlag, returns // 0 if not found -func (c *Context) Int(name string) int { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Int(name string) int { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt(name, fs) } return 0 diff --git a/flag_int64.go b/flag_int64.go index 2f0be7aa06..f21e7e44f1 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -103,8 +103,8 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error { // Int64 looks up the value of a local Int64Flag, returns // 0 if not found -func (c *Context) Int64(name string) int64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Int64(name string) int64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt64(name, fs) } return 0 diff --git a/flag_int64_slice.go b/flag_int64_slice.go index a53b185078..16f5665b0a 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -176,8 +176,8 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found -func (c *Context) Int64Slice(name string) []int64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Int64Slice(name string) []int64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt64Slice(name, fs) } return nil diff --git a/flag_int_slice.go b/flag_int_slice.go index 5f3bd88f05..b0032299af 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -187,8 +187,8 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found -func (c *Context) IntSlice(name string) []int { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) IntSlice(name string) []int { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupIntSlice(name, fs) } return nil diff --git a/flag_path.go b/flag_path.go index 4010e84c62..de19eea217 100644 --- a/flag_path.go +++ b/flag_path.go @@ -98,8 +98,8 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error { // Path looks up the value of a local PathFlag, returns // "" if not found -func (c *Context) Path(name string) string { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Path(name string) string { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupPath(name, fs) } diff --git a/flag_string.go b/flag_string.go index cd3c7dff4a..936ce728a9 100644 --- a/flag_string.go +++ b/flag_string.go @@ -99,8 +99,8 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error { // String looks up the value of a local StringFlag, returns // "" if not found -func (c *Context) String(name string) string { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) String(name string) string { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupString(name, fs) } return "" diff --git a/flag_string_slice.go b/flag_string_slice.go index 166424775f..3879235779 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -188,8 +188,8 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found -func (c *Context) StringSlice(name string) []string { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) StringSlice(name string) []string { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupStringSlice(name, fs) } return nil diff --git a/flag_timestamp.go b/flag_timestamp.go index ed06418284..dcb9d84152 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -165,8 +165,8 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { } // Timestamp gets the timestamp from a flag name -func (c *Context) Timestamp(name string) *time.Time { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Timestamp(name string) *time.Time { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupTimestamp(name, fs) } return nil diff --git a/flag_uint.go b/flag_uint.go index dd10e1c01d..e9ce9fec7d 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -103,8 +103,8 @@ func (f *UintFlag) GetEnvVars() []string { // Uint looks up the value of a local UintFlag, returns // 0 if not found -func (c *Context) Uint(name string) uint { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Uint(name string) uint { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUint(name, fs) } return 0 diff --git a/flag_uint64.go b/flag_uint64.go index 017db53c1d..2a2669fd24 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -103,8 +103,8 @@ func (f *Uint64Flag) GetEnvVars() []string { // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found -func (c *Context) Uint64(name string) uint64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Uint64(name string) uint64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUint64(name, fs) } return 0 diff --git a/funcs.go b/funcs.go index 842b4aa99c..0a9b22c94a 100644 --- a/funcs.go +++ b/funcs.go @@ -21,11 +21,11 @@ type CommandNotFoundFunc func(*Context, string) // customized usage error messages. This function is able to replace the // original error messages. If this function is not set, the "Incorrect usage" // is displayed and the execution is interrupted. -type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error +type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error // ExitErrHandlerFunc is executed if provided in order to handle exitError values // returned by Actions and Before/After functions. -type ExitErrHandlerFunc func(context *Context, err error) +type ExitErrHandlerFunc func(cCtx *Context, err error) // FlagStringFunc is used by the help generation to display a flag, which is // expected to be a single line. diff --git a/help.go b/help.go index fad990a6e3..2f8156fca4 100644 --- a/help.go +++ b/help.go @@ -15,13 +15,13 @@ var helpCommand = &Command{ Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", - Action: func(c *Context) error { - args := c.Args() + Action: func(cCtx *Context) error { + args := cCtx.Args() if args.Present() { - return ShowCommandHelp(c, args.First()) + return ShowCommandHelp(cCtx, args.First()) } - _ = ShowAppHelp(c) + _ = ShowAppHelp(cCtx) return nil }, } @@ -31,13 +31,13 @@ var helpSubcommand = &Command{ Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", - Action: func(c *Context) error { - args := c.Args() + Action: func(cCtx *Context) error { + args := cCtx.Args() if args.Present() { - return ShowCommandHelp(c, args.First()) + return ShowCommandHelp(cCtx, args.First()) } - return ShowSubcommandHelp(c) + return ShowSubcommandHelp(cCtx) }, } @@ -71,30 +71,30 @@ func ShowAppHelpAndExit(c *Context, exitCode int) { } // ShowAppHelp is an action that displays the help. -func ShowAppHelp(c *Context) error { - tpl := c.App.CustomAppHelpTemplate +func ShowAppHelp(cCtx *Context) error { + tpl := cCtx.App.CustomAppHelpTemplate if tpl == "" { tpl = AppHelpTemplate } - if c.App.ExtraInfo == nil { - HelpPrinter(c.App.Writer, tpl, c.App) + if cCtx.App.ExtraInfo == nil { + HelpPrinter(cCtx.App.Writer, tpl, cCtx.App) return nil } customAppData := func() map[string]interface{} { return map[string]interface{}{ - "ExtraInfo": c.App.ExtraInfo, + "ExtraInfo": cCtx.App.ExtraInfo, } } - HelpPrinterCustom(c.App.Writer, tpl, c.App, customAppData()) + HelpPrinterCustom(cCtx.App.Writer, tpl, cCtx.App, customAppData()) return nil } // DefaultAppComplete prints the list of subcommands as the default app completion method -func DefaultAppComplete(c *Context) { - DefaultCompleteWithFlags(nil)(c) +func DefaultAppComplete(cCtx *Context) { + DefaultCompleteWithFlags(nil)(cCtx) } func printCommandSuggestions(commands []*Command, writer io.Writer) { @@ -159,30 +159,30 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { } } -func DefaultCompleteWithFlags(cmd *Command) func(c *Context) { - return func(c *Context) { +func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) { + return func(cCtx *Context) { if len(os.Args) > 2 { lastArg := os.Args[len(os.Args)-2] if strings.HasPrefix(lastArg, "-") { if cmd != nil { - printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer) + printFlagSuggestions(lastArg, cmd.Flags, cCtx.App.Writer) return } - printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer) + printFlagSuggestions(lastArg, cCtx.App.Flags, cCtx.App.Writer) return } } if cmd != nil { - printCommandSuggestions(cmd.Subcommands, c.App.Writer) + printCommandSuggestions(cmd.Subcommands, cCtx.App.Writer) return } - printCommandSuggestions(c.App.Commands, c.App.Writer) + printCommandSuggestions(cCtx.App.Commands, cCtx.App.Writer) } } @@ -228,32 +228,32 @@ func ShowSubcommandHelpAndExit(c *Context, exitCode int) { } // ShowSubcommandHelp prints help for the given subcommand -func ShowSubcommandHelp(c *Context) error { - if c == nil { +func ShowSubcommandHelp(cCtx *Context) error { + if cCtx == nil { return nil } - if c.Command != nil { - return ShowCommandHelp(c, c.Command.Name) + if cCtx.Command != nil { + return ShowCommandHelp(cCtx, cCtx.Command.Name) } - return ShowCommandHelp(c, "") + return ShowCommandHelp(cCtx, "") } // ShowVersion prints the version number of the App -func ShowVersion(c *Context) { - VersionPrinter(c) +func ShowVersion(cCtx *Context) { + VersionPrinter(cCtx) } -func printVersion(c *Context) { - _, _ = fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) +func printVersion(cCtx *Context) { + _, _ = fmt.Fprintf(cCtx.App.Writer, "%v version %v\n", cCtx.App.Name, cCtx.App.Version) } // ShowCompletions prints the lists of commands within a given context -func ShowCompletions(c *Context) { - a := c.App +func ShowCompletions(cCtx *Context) { + a := cCtx.App if a != nil && a.BashComplete != nil { - a.BashComplete(c) + a.BashComplete(cCtx) } } @@ -304,20 +304,20 @@ func printHelp(out io.Writer, templ string, data interface{}) { HelpPrinterCustom(out, templ, data, nil) } -func checkVersion(c *Context) bool { +func checkVersion(cCtx *Context) bool { found := false for _, name := range VersionFlag.Names() { - if c.Bool(name) { + if cCtx.Bool(name) { found = true } } return found } -func checkHelp(c *Context) bool { +func checkHelp(cCtx *Context) bool { found := false for _, name := range HelpFlag.Names() { - if c.Bool(name) { + if cCtx.Bool(name) { found = true } } @@ -333,9 +333,9 @@ func checkCommandHelp(c *Context, name string) bool { return false } -func checkSubcommandHelp(c *Context) bool { - if c.Bool("h") || c.Bool("help") { - _ = ShowSubcommandHelp(c) +func checkSubcommandHelp(cCtx *Context) bool { + if cCtx.Bool("h") || cCtx.Bool("help") { + _ = ShowSubcommandHelp(cCtx) return true } @@ -357,20 +357,20 @@ func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { return true, arguments[:pos] } -func checkCompletions(c *Context) bool { - if !c.shellComplete { +func checkCompletions(cCtx *Context) bool { + if !cCtx.shellComplete { return false } - if args := c.Args(); args.Present() { + if args := cCtx.Args(); args.Present() { name := args.First() - if cmd := c.App.Command(name); cmd != nil { + if cmd := cCtx.App.Command(name); cmd != nil { // let the command handle the completion return false } } - ShowCompletions(c) + ShowCompletions(cCtx) return true } From f1ce5c74b0c8646817acc6df26190a7ada66dfce Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 08:10:33 -0400 Subject: [PATCH 08/22] Cleaning up some release-related metadata --- LICENSE | 2 +- README.md | 4 ++++ docs/CHANGELOG.md | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 42a597e29b..2c84c78a11 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016 Jeremy Saenz & Contributors +Copyright (c) 2022 urfave/cli maintainers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5b8d2b6711..79791adf05 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,7 @@ export PATH=$PATH:$GOPATH/bin cli is tested against multiple versions of Go on Linux, and against the latest released version of Go on OS X and Windows. This project uses Github Actions for builds. To see our currently supported go versions and platforms, look at the [./.github/workflows/cli.yml](https://github.com/urfave/cli/blob/main/.github/workflows/cli.yml). + +## License + +See [`LICENSE`](./LICENSE) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e957d945d1..6dfff88f73 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,3 +1,9 @@ +> This document is no longer being actively maintained. Please see the +> [releases page](https://github.com/urfave/cli/releases) for all release notes +> and related hypermedia for releases > 1.22.5+, 2.3.0+ and more. + +--- + # Change Log **ATTN**: This project uses [semantic versioning](http://semver.org/). From de589511d80635bab63a0b822fc598aa7b56b961 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 08:14:05 -0400 Subject: [PATCH 09/22] Nit updates to changelog admonition --- docs/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6dfff88f73..6aec50b429 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ -> This document is no longer being actively maintained. Please see the +> :warning: This document is no longer being actively maintained. Please see the > [releases page](https://github.com/urfave/cli/releases) for all release notes -> and related hypermedia for releases > 1.22.5+, 2.3.0+ and more. +> and related hypermedia for releases `>= 1.22.5`, `>= 2.3.0`. --- From 11b3a30b4ac662cd3c490f11117460b6fe34e7f6 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 09:17:59 -0400 Subject: [PATCH 10/22] Add a new document specifically for releasing --- docs/RELEASING.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/RELEASING.md diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000000..57d75a08f7 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,61 @@ +# Releasing urfave/cli + +Releasing small batches often is [backed by +research](https://itrevolution.com/accelerate-book/) as part of the +virtuous cycles that keep teams and products healthy. + +To that end, the overall goal of the release process is to send +changes out into the world as close to the time the commits were +merged to the `main` branch as possible. In this way, the community +of humans depending on this library are able to make use of the +changes they need **quickly**, which means they should'nt have to +maintain long-lived forks of the project, which means they can get +back to focusing on the work on which they want to focus. This also +means that the @urfave/cli team should be ablo to focus on +delivering a steadily improving product with significantly eased +ability to associate bugs and regressions with specific releases. + +## Process + +- Release versions follow [semantic versioning](https://semver.org/) +- Releases are associated with **signed, annotated git tags**[^1]. +- Release notes are **automatically generated**[^2]. + +In the `main` or `v1` branch, the current version is always +available via: + +```sh +git describe --always --dirty --tags +``` + +**NOTE**: if the version reported contains `-dirty`, this is +indicative of a "dirty" work tree, which is not a great state for +creating a new release tag. Seek help from @urfave/cli teammates. + +For example, given a described version of `v2.4.7-3-g68da1cd` and a +diff of `v2.4.7...` that contains only bug fixes, the next version +should be `v2.4.8`: + +```sh +git tag -a -s -m 'Release 2.4.8' v2.4.8 +git push origin v2.4.8 +``` + +The tag push will trigger a GitHub Actions workflow. The remaining +steps require human intervention through the GitHub web view +although [automated solutions +exist](https://github.com/softprops/action-gh-release) that may be +adopted in the future. + +- Open the [the new release page](https://github.com/urfave/cli/releases/new) +- At the top of the form, click on the `Choose a tag` select control and select `v2.4.8` +- In the `Write` tab below, click the `Auto-generate release notes` button +- At the bottom of the form, click the `Publish release` button +- :white_check_mark: you're done! + +[^1]: This was not always true. There are many **lightweight git + tags** present in the repository history. + +[^2]: This was not always true. The + [`docs/CHANGELOG.md`](./CHANGELOG.md) document used to be + manually maintained. From 7f37d1d13c1652b28b1074aa944d37a8af17da71 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Fri, 2 Apr 2021 20:19:13 -0400 Subject: [PATCH 11/22] Fix(1199). Allow -ve values for int, float & duration --- altsrc/flag.go | 20 +++++++------------- altsrc/flag_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 4b5588ade7..53382b261e 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -197,10 +197,8 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex if err != nil { return err } - if value > 0 { - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) - } + for _, name := range f.Names() { + _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) } } } @@ -215,10 +213,8 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC if err != nil { return err } - if value > 0 { - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) - } + for _, name := range f.Names() { + _ = f.set.Set(name, value.String()) } } } @@ -233,11 +229,9 @@ func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo if err != nil { return err } - if value > 0 { - floatStr := float64ToString(value) - for _, name := range f.Names() { - _ = f.set.Set(name, floatStr) - } + floatStr := float64ToString(value) + for _, name := range f.Names() { + _ = f.set.Set(name, floatStr) } } } diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index 204833118f..e20ab02b6e 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -234,6 +234,15 @@ func TestIntApplyInputSourceMethodSet(t *testing.T) { expect(t, 15, c.Int("test")) } +func TestIntApplyInputSourceMethodSetNegativeValue(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), + FlagName: "test", + MapValue: -1, + }) + expect(t, -1, c.Int("test")) +} + func TestIntApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), @@ -264,6 +273,15 @@ func TestDurationApplyInputSourceMethodSet(t *testing.T) { expect(t, 30*time.Second, c.Duration("test")) } +func TestDurationApplyInputSourceMethodSetNegativeValue(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), + FlagName: "test", + MapValue: -30 * time.Second, + }) + expect(t, -30*time.Second, c.Duration("test")) +} + func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), @@ -294,6 +312,15 @@ func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { expect(t, 1.3, c.Float64("test")) } +func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), + FlagName: "test", + MapValue: -1.3, + }) + expect(t, -1.3, c.Float64("test")) +} + func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), From d198aed170178d0d875914288926e300bdf116c1 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 24 Apr 2022 12:06:28 -0400 Subject: [PATCH 12/22] Add test when flag is not set --- altsrc/flag_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index e20ab02b6e..dcb8263ab3 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -321,6 +321,15 @@ func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) { expect(t, -1.3, c.Float64("test")) } +func TestFloat64ApplyInputSourceMethodSetNegativeValueNotSet(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test1"}), + FlagName: "test1", + // dont set map value + }) + expect(t, 0.0, c.Float64("test1")) +} + func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), From f6c020fa0907f08f6e44c9b2c60398f7e599e136 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 12:59:12 -0400 Subject: [PATCH 13/22] Spelling fixes thanks to @asmaloney :bow: --- docs/RELEASING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 57d75a08f7..b10e4e0dbf 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -8,10 +8,10 @@ To that end, the overall goal of the release process is to send changes out into the world as close to the time the commits were merged to the `main` branch as possible. In this way, the community of humans depending on this library are able to make use of the -changes they need **quickly**, which means they should'nt have to +changes they need **quickly**, which means they shouldn't have to maintain long-lived forks of the project, which means they can get back to focusing on the work on which they want to focus. This also -means that the @urfave/cli team should be ablo to focus on +means that the @urfave/cli team should be able to focus on delivering a steadily improving product with significantly eased ability to associate bugs and regressions with specific releases. From aabfea87c811de4c6bf6dcd01f64309a8864fead Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 25 Apr 2022 10:04:40 -0700 Subject: [PATCH 14/22] Move some test helpers from docs_test to fish_test This is in preparation to make docs optional. Signed-off-by: Kir Kolyshkin --- docs_test.go | 123 --------------------------------------------------- fish_test.go | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 123 deletions(-) diff --git a/docs_test.go b/docs_test.go index adccbbb9ad..b925cb9232 100644 --- a/docs_test.go +++ b/docs_test.go @@ -1,133 +1,10 @@ package cli import ( - "bytes" "errors" - "io/ioutil" "testing" ) -func testApp() *App { - app := newTestApp() - app.Name = "greet" - app.Flags = []Flag{ - &StringFlag{ - Name: "socket", - Aliases: []string{"s"}, - Usage: "some 'usage' text", - Value: "value", - TakesFile: true, - }, - &StringFlag{Name: "flag", Aliases: []string{"fl", "f"}}, - &BoolFlag{ - Name: "another-flag", - Aliases: []string{"b"}, - Usage: "another usage text", - }, - &BoolFlag{ - Name: "hidden-flag", - Hidden: true, - }, - } - app.Commands = []*Command{{ - Aliases: []string{"c"}, - Flags: []Flag{ - &StringFlag{ - Name: "flag", - Aliases: []string{"fl", "f"}, - TakesFile: true, - }, - &BoolFlag{ - Name: "another-flag", - Aliases: []string{"b"}, - Usage: "another usage text", - }, - }, - Name: "config", - Usage: "another usage test", - Subcommands: []*Command{{ - Aliases: []string{"s", "ss"}, - Flags: []Flag{ - &StringFlag{Name: "sub-flag", Aliases: []string{"sub-fl", "s"}}, - &BoolFlag{ - Name: "sub-command-flag", - Aliases: []string{"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", - }, { - Name: "hidden-command", - Hidden: true, - }, { - Aliases: []string{"u"}, - Flags: []Flag{ - &StringFlag{ - Name: "flag", - Aliases: []string{"fl", "f"}, - TakesFile: true, - }, - &BoolFlag{ - Name: "another-flag", - Aliases: []string{"b"}, - Usage: "another usage text", - }, - }, - Name: "usage", - Usage: "standard usage text", - UsageText: ` -Usage for the usage text -- formatted: Based on the specified ConfigMap and summon secrets.yml -- list: Inspect the environment for a specific process running on a Pod -- for_effect: Compare 'namespace' environment with 'local' - -` + "```" + ` -func() { ... } -` + "```" + ` - -Should be a part of the same code block -`, - Subcommands: []*Command{{ - Aliases: []string{"su"}, - Flags: []Flag{ - &BoolFlag{ - Name: "sub-command-flag", - Aliases: []string{"s"}, - Usage: "some usage text", - }, - }, - Name: "sub-usage", - Usage: "standard usage text", - UsageText: "Single line of UsageText", - }}, - }} - app.UsageText = "app [first_arg] [second_arg]" - app.Description = `Description of the application.` - app.Usage = "Some app" - app.Authors = []*Author{ - {Name: "Harrison", Email: "harrison@lolwut.com"}, - {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, - } - return app -} - -func expectFileContent(t *testing.T, file, got string) { - data, err := ioutil.ReadFile(file) - // Ignore windows line endings - // TODO: Replace with bytes.ReplaceAll when support for Go 1.11 is dropped - data = bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1) - expect(t, err, nil) - expect(t, got, string(data)) -} - func TestToMarkdownFull(t *testing.T) { // Given app := testApp() diff --git a/fish_test.go b/fish_test.go index 4ca8c47903..af1a14c441 100644 --- a/fish_test.go +++ b/fish_test.go @@ -1,6 +1,8 @@ package cli import ( + "bytes" + "io/ioutil" "testing" ) @@ -19,3 +21,124 @@ func TestFishCompletion(t *testing.T) { expect(t, err, nil) expectFileContent(t, "testdata/expected-fish-full.fish", res) } + +func testApp() *App { + app := newTestApp() + app.Name = "greet" + app.Flags = []Flag{ + &StringFlag{ + Name: "socket", + Aliases: []string{"s"}, + Usage: "some 'usage' text", + Value: "value", + TakesFile: true, + }, + &StringFlag{Name: "flag", Aliases: []string{"fl", "f"}}, + &BoolFlag{ + Name: "another-flag", + Aliases: []string{"b"}, + Usage: "another usage text", + }, + &BoolFlag{ + Name: "hidden-flag", + Hidden: true, + }, + } + app.Commands = []*Command{{ + Aliases: []string{"c"}, + Flags: []Flag{ + &StringFlag{ + Name: "flag", + Aliases: []string{"fl", "f"}, + TakesFile: true, + }, + &BoolFlag{ + Name: "another-flag", + Aliases: []string{"b"}, + Usage: "another usage text", + }, + }, + Name: "config", + Usage: "another usage test", + Subcommands: []*Command{{ + Aliases: []string{"s", "ss"}, + Flags: []Flag{ + &StringFlag{Name: "sub-flag", Aliases: []string{"sub-fl", "s"}}, + &BoolFlag{ + Name: "sub-command-flag", + Aliases: []string{"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", + }, { + Name: "hidden-command", + Hidden: true, + }, { + Aliases: []string{"u"}, + Flags: []Flag{ + &StringFlag{ + Name: "flag", + Aliases: []string{"fl", "f"}, + TakesFile: true, + }, + &BoolFlag{ + Name: "another-flag", + Aliases: []string{"b"}, + Usage: "another usage text", + }, + }, + Name: "usage", + Usage: "standard usage text", + UsageText: ` +Usage for the usage text +- formatted: Based on the specified ConfigMap and summon secrets.yml +- list: Inspect the environment for a specific process running on a Pod +- for_effect: Compare 'namespace' environment with 'local' + +` + "```" + ` +func() { ... } +` + "```" + ` + +Should be a part of the same code block +`, + Subcommands: []*Command{{ + Aliases: []string{"su"}, + Flags: []Flag{ + &BoolFlag{ + Name: "sub-command-flag", + Aliases: []string{"s"}, + Usage: "some usage text", + }, + }, + Name: "sub-usage", + Usage: "standard usage text", + UsageText: "Single line of UsageText", + }}, + }} + app.UsageText = "app [first_arg] [second_arg]" + app.Description = `Description of the application.` + app.Usage = "Some app" + app.Authors = []*Author{ + {Name: "Harrison", Email: "harrison@lolwut.com"}, + {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, + } + return app +} + +func expectFileContent(t *testing.T, file, got string) { + data, err := ioutil.ReadFile(file) + // Ignore windows line endings + // TODO: Replace with bytes.ReplaceAll when support for Go 1.11 is dropped + data = bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1) + expect(t, err, nil) + expect(t, got, string(data)) +} From 4c7b46cb24c9af9b5d93bcc2316b2a877008b723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20B=C4=85k?= Date: Mon, 25 Apr 2022 19:11:11 +0200 Subject: [PATCH 15/22] fix: clean code --- flag.go | 22 +--------------------- flag_test.go | 18 +++++++++--------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/flag.go b/flag.go index 1907622e56..b85bafd546 100644 --- a/flag.go +++ b/flag.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "io/ioutil" - "reflect" "regexp" "runtime" "strconv" @@ -244,7 +243,7 @@ func prefixedNames(names []string, placeholder string) string { func withEnvHint(envVars []string, str string) string { envText := "" - if envVars != nil && len(envVars) > 0 { + if len(envVars) > 0 { prefix := "$" suffix := "" sep := ", $" @@ -273,17 +272,6 @@ func flagNames(name string, aliases []string) []string { return ret } -func flagStringSliceField(f Flag, name string) []string { - fv := flagValue(f) - field := fv.FieldByName(name) - - if field.IsValid() { - return field.Interface().([]string) - } - - return []string{} -} - func withFileHint(filePath, str string) string { fileText := "" if filePath != "" { @@ -292,14 +280,6 @@ func withFileHint(filePath, str string) string { return str + fileText } -func flagValue(f Flag) reflect.Value { - fv := reflect.ValueOf(f) - for fv.Kind() == reflect.Ptr { - fv = reflect.Indirect(fv) - } - return fv -} - func formatDefault(format string) string { return " (default: " + format + ")" } diff --git a/flag_test.go b/flag_test.go index b9cbfc3068..843f646a9d 100644 --- a/flag_test.go +++ b/flag_test.go @@ -400,7 +400,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { } } -var prefixStringFlagTests = []struct { +var _ = []struct { name string aliases []string usage string @@ -490,7 +490,7 @@ func TestPathFlagApply_SetsAllNames(t *testing.T) { expect(t, v, "/path/to/file/PATH") } -var envHintFlagTests = []struct { +var _ = []struct { name string env string hinter FlagEnvHintFunc @@ -2174,43 +2174,43 @@ type flagDefaultTestCase struct { func TestFlagDefaultValue(t *testing.T) { cases := []*flagDefaultTestCase{ - &flagDefaultTestCase{ + { name: "stringSclice", flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")}, toParse: []string{"--flag", "parsed"}, expect: `--flag value (default: "default1", "default2") (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "float64Sclice", flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)}, toParse: []string{"--flag", "13.3"}, expect: `--flag value (default: 1.1, 2.2) (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "int64Sclice", flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)}, toParse: []string{"--flag", "13"}, expect: `--flag value (default: 1, 2) (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "intSclice", flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)}, toParse: []string{"--flag", "13"}, expect: `--flag value (default: 1, 2) (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "string", flag: &StringFlag{Name: "flag", Value: "default"}, toParse: []string{"--flag", "parsed"}, expect: `--flag value (default: "default")`, }, - &flagDefaultTestCase{ + { name: "bool", flag: &BoolFlag{Name: "flag", Value: true}, toParse: []string{"--flag", "false"}, expect: `--flag (default: true)`, }, - &flagDefaultTestCase{ + { name: "uint64", flag: &Uint64Flag{Name: "flag", Value: 1}, toParse: []string{"--flag", "13"}, From 49e43beba32f7fb2007f557bb77129bf8a0e4300 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 25 Apr 2022 10:16:31 -0700 Subject: [PATCH 16/22] Add urfave_cli_no_docs build tag This removes the resulting binary dependency on cpuguy83/md2man and russross/blackfriday, which saves more than 400 KB (more than 300 KB once stripped) from the resulting binary. Document this in README. --- README.md | 10 ++++++++++ docs.go | 3 +++ docs_test.go | 3 +++ 3 files changed, 16 insertions(+) diff --git a/README.md b/README.md index 5b8d2b6711..134e72e893 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,16 @@ import ( ... ``` +### Build tags + +You can use the following build tags: + +#### `urfave_cli_no_docs` + +When set, this removes `ToMarkdown` and `ToMan` methods, so your application +won't be able to call those. This reduces the resulting binary size by about +300-400 KB (measured using Go 1.18.1 on Linux/amd64), due to less dependencies. + ### GOPATH Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can diff --git a/docs.go b/docs.go index 9f82fc6b0b..ace6a6bd5d 100644 --- a/docs.go +++ b/docs.go @@ -1,3 +1,6 @@ +//go:build !urfave_cli_no_docs +// +build !urfave_cli_no_docs + package cli import ( diff --git a/docs_test.go b/docs_test.go index b925cb9232..12d5d3c8b1 100644 --- a/docs_test.go +++ b/docs_test.go @@ -1,3 +1,6 @@ +//go:build !urfave_cli_no_docs +// +build !urfave_cli_no_docs + package cli import ( From b8cb475418d24c63ac616018ac4bdb5f61353f1f Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 25 Apr 2022 10:58:31 -0700 Subject: [PATCH 17/22] ci: test newly added tag We run test with the tag set (to make sure nothing is broken), and also the check-binary-size target (for informational purposes only). Signed-off-by: Kir Kolyshkin --- .github/workflows/cli.yml | 6 ++++++ internal/build/build.go | 20 +++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 0ab3baf360..c60ed4c2fa 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -37,12 +37,18 @@ jobs: - name: vet run: go run internal/build/build.go vet + - name: test with tags + run: go run internal/build/build.go -tags urfave_cli_no_docs test + - name: test run: go run internal/build/build.go test - name: check-binary-size run: go run internal/build/build.go check-binary-size + - name: check-binary-size with tags (informational only) + run: go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size || true + - name: Upload coverage to Codecov if: success() && matrix.go == '1.18.x' && matrix.os == 'ubuntu-latest' uses: codecov/codecov-action@v2 diff --git a/internal/build/build.go b/internal/build/build.go index 4cbaa68979..dec0dfb64f 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -46,6 +46,12 @@ func main() { Action: checkBinarySizeActionFunc, }, } + app.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "tags", + Usage: "set build tags", + }, + } err := app.Run(os.Args) if err != nil { @@ -68,6 +74,8 @@ func VetActionFunc(_ *cli.Context) error { } func TestActionFunc(c *cli.Context) error { + tags := c.String("tags") + for _, pkg := range packages { var packageName string @@ -79,7 +87,7 @@ func TestActionFunc(c *cli.Context) error { coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg) - err := runCmd("go", "test", "-v", coverProfile, packageName) + err := runCmd("go", "test", "-tags", tags, "-v", coverProfile, packageName) if err != nil { return err } @@ -201,14 +209,16 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { mbStringFormatter = "%.1fMB" ) + tags := c.String("tags") + // get cli example size - cliSize, err := getSize(cliSourceFilePath, cliBuiltFilePath) + cliSize, err := getSize(cliSourceFilePath, cliBuiltFilePath, tags) if err != nil { return err } // get hello world size - helloSize, err := getSize(helloSourceFilePath, helloBuiltFilePath) + helloSize, err := getSize(helloSourceFilePath, helloBuiltFilePath, tags) if err != nil { return err } @@ -270,9 +280,9 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { return nil } -func getSize(sourcePath string, builtPath string) (size int64, err error) { +func getSize(sourcePath string, builtPath string, tags string) (size int64, err error) { // build example binary - err = runCmd("go", "build", "-o", builtPath, "-ldflags", "-s -w", sourcePath) + err = runCmd("go", "build", "-tags", tags, "-o", builtPath, "-ldflags", "-s -w", sourcePath) if err != nil { fmt.Println("issue getting size for example binary") return 0, err From e3aa8d3246f4e5d1e26e78c98c6cb5ebb6c3b668 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 20:16:01 -0400 Subject: [PATCH 18/22] Add failing test for #1373 --- altsrc/yaml_file_loader_test.go | 87 +++++++++++++++++++++++++++++++++ testdata/empty.yml | 1 + 2 files changed, 88 insertions(+) create mode 100644 altsrc/yaml_file_loader_test.go create mode 100644 testdata/empty.yml diff --git a/altsrc/yaml_file_loader_test.go b/altsrc/yaml_file_loader_test.go new file mode 100644 index 0000000000..814586b917 --- /dev/null +++ b/altsrc/yaml_file_loader_test.go @@ -0,0 +1,87 @@ +package altsrc_test + +import ( + "fmt" + "log" + "os" + "time" + + "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/altsrc" +) + +func ExampleApp_Run_yamlFileLoaderDuration() { + execServe := func(c *cli.Context) error { + keepaliveInterval := c.Duration("keepalive-interval") + fmt.Printf("keepalive %s\n", keepaliveInterval) + return nil + } + + fileExists := func(filename string) bool { + stat, _ := os.Stat(filename) + return stat != nil + } + + // initConfigFileInputSource is like altsrc.InitInputSourceWithContext and altsrc.NewYamlSourceFromFlagFunc, but checks + // if the config flag is exists and only loads it if it does. If the flag is set and the file exists, it fails. + initConfigFileInputSource := func(configFlag string, flags []cli.Flag) cli.BeforeFunc { + return func(context *cli.Context) error { + configFile := context.String(configFlag) + if context.IsSet(configFlag) && !fileExists(configFile) { + return fmt.Errorf("config file %s does not exist", configFile) + } else if !context.IsSet(configFlag) && !fileExists(configFile) { + return nil + } + inputSource, err := altsrc.NewYamlSourceFromFile(configFile) + if err != nil { + return err + } + return altsrc.ApplyInputSourceValues(context, inputSource, flags) + } + } + + flagsServe := []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + EnvVars: []string{"CONFIG_FILE"}, + Value: "../testdata/empty.yml", + DefaultText: "../testdata/empty.yml", + Usage: "config file", + }, + altsrc.NewDurationFlag( + &cli.DurationFlag{ + Name: "keepalive-interval", + Aliases: []string{"k"}, + EnvVars: []string{"KEEPALIVE_INTERVAL"}, + Value: 45 * time.Second, + Usage: "interval of keepalive messages", + }, + ), + } + + cmdServe := &cli.Command{ + Name: "serve", + Usage: "Run the server", + UsageText: "serve [OPTIONS..]", + Action: execServe, + Flags: flagsServe, + Before: initConfigFileInputSource("config", flagsServe), + } + + c := &cli.App{ + Name: "cmd", + HideVersion: true, + UseShortOptionHandling: true, + Commands: []*cli.Command{ + cmdServe, + }, + } + + if err := c.Run([]string{"cmd", "serve", "--config", "../testdata/empty.yml"}); err != nil { + log.Fatal(err) + } + + // Output: + // keepalive 45s +} diff --git a/testdata/empty.yml b/testdata/empty.yml new file mode 100644 index 0000000000..ab2fc5dd55 --- /dev/null +++ b/testdata/empty.yml @@ -0,0 +1 @@ +# empty file \ No newline at end of file From efe04493fc5a4013f26ad0b1c37df4d6a769f7a8 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 20:43:43 -0400 Subject: [PATCH 19/22] Only apply altsrc input source values when set Closes #1373 --- altsrc/flag.go | 182 +++++++++++++++------------------ altsrc/input_source_context.go | 1 + altsrc/json_source_context.go | 5 + altsrc/map_input_source.go | 13 +++ 4 files changed, 101 insertions(+), 100 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 53382b261e..a90a52a160 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -64,16 +64,14 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *c // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.Generic(f.GenericFlag.Name) - if err != nil { - return err - } - if value != nil { - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.GenericFlag.Name) { + value, err := isc.Generic(f.GenericFlag.Name) + if err != nil { + return err + } + if value != nil { + for _, name := range f.Names() { + _ = f.set.Set(name, value.String()) } } } @@ -83,19 +81,17 @@ func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.StringSlice(f.StringSliceFlag.Name) - if err != nil { - return err - } - if value != nil { - var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...)) - for _, name := range f.Names() { - underlyingFlag := f.set.Lookup(name) - if underlyingFlag != nil { - underlyingFlag.Value = &sliceValue - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.StringSliceFlag.Name) { + value, err := isc.StringSlice(f.StringSliceFlag.Name) + if err != nil { + return err + } + if value != nil { + var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...)) + for _, name := range f.Names() { + underlyingFlag := f.set.Lookup(name) + if underlyingFlag != nil { + underlyingFlag.Value = &sliceValue } } } @@ -105,19 +101,17 @@ func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSour // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.IntSlice(f.IntSliceFlag.Name) - if err != nil { - return err - } - if value != nil { - var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...)) - for _, name := range f.Names() { - underlyingFlag := f.set.Lookup(name) - if underlyingFlag != nil { - underlyingFlag.Value = &sliceValue - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.IntSliceFlag.Name) { + value, err := isc.IntSlice(f.IntSliceFlag.Name) + if err != nil { + return err + } + if value != nil { + var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...)) + for _, name := range f.Names() { + underlyingFlag := f.set.Lookup(name) + if underlyingFlag != nil { + underlyingFlag.Value = &sliceValue } } } @@ -127,16 +121,14 @@ func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.Bool(f.BoolFlag.Name) - if err != nil { - return err - } - if value { - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatBool(value)) - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.BoolFlag.Name) { + value, err := isc.Bool(f.BoolFlag.Name) + if err != nil { + return err + } + if value { + for _, name := range f.Names() { + _ = f.set.Set(name, strconv.FormatBool(value)) } } } @@ -145,16 +137,14 @@ func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.String(f.StringFlag.Name) - if err != nil { - return err - } - if value != "" { - for _, name := range f.Names() { - _ = f.set.Set(name, value) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.StringFlag.Name) { + value, err := isc.String(f.StringFlag.Name) + if err != nil { + return err + } + if value != "" { + for _, name := range f.Names() { + _ = f.set.Set(name, value) } } } @@ -163,26 +153,24 @@ func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCon // ApplyInputSourceValue applies a Path value to the flagSet if required func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.String(f.PathFlag.Name) - if err != nil { - return err - } - if value != "" { - for _, name := range f.Names() { - - if !filepath.IsAbs(value) && isc.Source() != "" { - basePathAbs, err := filepath.Abs(isc.Source()) - if err != nil { - return err - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.PathFlag.Name) { + value, err := isc.String(f.PathFlag.Name) + if err != nil { + return err + } + if value != "" { + for _, name := range f.Names() { - value = filepath.Join(filepath.Dir(basePathAbs), value) + if !filepath.IsAbs(value) && isc.Source() != "" { + basePathAbs, err := filepath.Abs(isc.Source()) + if err != nil { + return err } - _ = f.set.Set(name, value) + value = filepath.Join(filepath.Dir(basePathAbs), value) } + + _ = f.set.Set(name, value) } } } @@ -191,15 +179,13 @@ func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.Int(f.IntFlag.Name) - if err != nil { - return err - } - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.IntFlag.Name) { + value, err := isc.Int(f.IntFlag.Name) + if err != nil { + return err + } + for _, name := range f.Names() { + _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) } } return nil @@ -207,15 +193,13 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.Duration(f.DurationFlag.Name) - if err != nil { - return err - } - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.DurationFlag.Name) { + value, err := isc.Duration(f.DurationFlag.Name) + if err != nil { + return err + } + for _, name := range f.Names() { + _ = f.set.Set(name, value.String()) } } return nil @@ -223,16 +207,14 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.Float64(f.Float64Flag.Name) - if err != nil { - return err - } - floatStr := float64ToString(value) - for _, name := range f.Names() { - _ = f.set.Set(name, floatStr) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.Float64Flag.Name) { + value, err := isc.Float64(f.Float64Flag.Name) + if err != nil { + return err + } + floatStr := float64ToString(value) + for _, name := range f.Names() { + _ = f.set.Set(name, floatStr) } } return nil diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index a639d8b38e..467360c010 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -14,6 +14,7 @@ import ( type InputSourceContext interface { Source() string + IsSet(name string) bool Int(name string) (int, error) Duration(name string) (time.Duration, error) Float64(name string) (float64, error) diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 9307a35870..d5f8cc192e 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -184,6 +184,11 @@ func (x *jsonSource) Bool(name string) (bool, error) { return v, nil } +func (x *jsonSource) IsSet(name string) bool { + _, err := x.getValue(name) + return err == nil +} + func (x *jsonSource) getValue(key string) (interface{}, error) { return jsonGetValue(key, x.deserialized) } diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index 117461f5a0..be94906383 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -244,6 +244,19 @@ func (fsm *MapInputSource) Bool(name string) (bool, error) { return false, nil } +// IsSet returns the truth of the key's existence in the map +func (fsm *MapInputSource) IsSet(name string) bool { + if _, exists := fsm.valueMap[name]; exists { + return exists + } + + if _, exists := nestedVal(name, fsm.valueMap); exists { + return exists + } + + return false +} + func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error { valueType := reflect.TypeOf(value) valueTypeName := "" From fe1468cc86b1cab489b2038a7f83090198b72af8 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 20:49:31 -0400 Subject: [PATCH 20/22] Make the altsrc input source context `isSet` method private so that it doesn't extend the public surface area, instead focusing on being a bugfix _only_. --- altsrc/flag.go | 18 +++++++++--------- altsrc/input_source_context.go | 3 ++- altsrc/json_source_context.go | 2 +- altsrc/map_input_source.go | 3 +-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index a90a52a160..db959493bc 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -64,7 +64,7 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *c // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.GenericFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.GenericFlag.Name) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { return err @@ -81,7 +81,7 @@ func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.StringSliceFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.StringSliceFlag.Name) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { return err @@ -101,7 +101,7 @@ func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSour // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.IntSliceFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.IntSliceFlag.Name) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { return err @@ -121,7 +121,7 @@ func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.BoolFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.BoolFlag.Name) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { return err @@ -137,7 +137,7 @@ func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.StringFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.StringFlag.Name) { value, err := isc.String(f.StringFlag.Name) if err != nil { return err @@ -153,7 +153,7 @@ func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCon // ApplyInputSourceValue applies a Path value to the flagSet if required func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.PathFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.PathFlag.Name) { value, err := isc.String(f.PathFlag.Name) if err != nil { return err @@ -179,7 +179,7 @@ func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.IntFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.IntFlag.Name) { value, err := isc.Int(f.IntFlag.Name) if err != nil { return err @@ -193,7 +193,7 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.DurationFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.DurationFlag.Name) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { return err @@ -207,7 +207,7 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.Float64Flag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.Float64Flag.Name) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { return err diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index 467360c010..d743253050 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -14,7 +14,6 @@ import ( type InputSourceContext interface { Source() string - IsSet(name string) bool Int(name string) (int, error) Duration(name string) (time.Duration, error) Float64(name string) (float64, error) @@ -23,4 +22,6 @@ type InputSourceContext interface { IntSlice(name string) ([]int, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) + + isSet(name string) bool } diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index d5f8cc192e..168b6dade0 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -184,7 +184,7 @@ func (x *jsonSource) Bool(name string) (bool, error) { return v, nil } -func (x *jsonSource) IsSet(name string) bool { +func (x *jsonSource) isSet(name string) bool { _, err := x.getValue(name) return err == nil } diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index be94906383..cc11fc5433 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -244,8 +244,7 @@ func (fsm *MapInputSource) Bool(name string) (bool, error) { return false, nil } -// IsSet returns the truth of the key's existence in the map -func (fsm *MapInputSource) IsSet(name string) bool { +func (fsm *MapInputSource) isSet(name string) bool { if _, exists := fsm.valueMap[name]; exists { return exists } From ac641ffda54727c6af36b80c0941f05886aed217 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 22:15:39 -0400 Subject: [PATCH 21/22] Add more test coverage around unset input source applying --- altsrc/flag_test.go | 259 ++++++++++++++++++++++++++++++++--------- altsrc/helpers_test.go | 7 +- 2 files changed, 206 insertions(+), 60 deletions(-) diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index dcb8263ab3..e3725f7d1c 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -26,29 +26,48 @@ type testApplyInputSource struct { MapValue interface{} } +type racyInputSource struct { + *MapInputSource +} + +func (ris *racyInputSource) isSet(name string) bool { + if _, ok := ris.MapInputSource.valueMap[name]; ok { + ris.MapInputSource.valueMap[name] = bogus{0} + } + return true +} + func TestGenericApplyInputSourceValue(t *testing.T) { v := &Parser{"abc", "def"} - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: v, - }) + } + c := runTest(t, tis) expect(t, v, c.Generic("test")) + + c = runRacyTest(t, tis) + refute(t, v, c.Generic("test")) } func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { p := &Parser{"abc", "def"} - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: &Parser{"efg", "hig"}, ContextValueString: p.String(), - }) + } + c := runTest(t, tis) expect(t, p, c.Generic("test")) + + c = runRacyTest(t, tis) + refute(t, p, c.Generic("test")) } func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewGenericFlag(&cli.GenericFlag{ Name: "test", Value: &Parser{}, @@ -58,17 +77,25 @@ func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { MapValue: &Parser{"efg", "hij"}, EnvVarName: "TEST", EnvVarValue: "abc,def", - }) + } + c := runTest(t, tis) expect(t, &Parser{"abc", "def"}, c.Generic("test")) + + c = runRacyTest(t, tis) + refute(t, &Parser{"abc", "def"}, c.Generic("test")) } func TestStringSliceApplyInputSourceValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, - }) + } + c := runTest(t, tis) expect(t, c.StringSlice("test"), []string{"hello", "world"}) + + c = runRacyTest(t, tis) + refute(t, c.StringSlice("test"), []string{"hello", "world"}) } func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { @@ -82,112 +109,154 @@ func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { } func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, EnvVarName: "TEST", EnvVarValue: "oh,no", - }) + } + c := runTest(t, tis) expect(t, c.StringSlice("test"), []string{"oh", "no"}) + + c = runRacyTest(t, tis) + refute(t, c.StringSlice("test"), []string{"oh", "no"}) } func TestIntSliceApplyInputSourceValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, - }) + } + c := runTest(t, tis) expect(t, c.IntSlice("test"), []int{1, 2}) + + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test"), []int{1, 2}) } func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, ContextValueString: "3", - }) + } + c := runTest(t, tis) expect(t, c.IntSlice("test"), []int{3}) + + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test"), []int{3}) } func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []interface{}{1, 2}, EnvVarName: "TEST", EnvVarValue: "3,4", - }) + } + c := runTest(t, tis) expect(t, c.IntSlice("test"), []int{3, 4}) + + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test"), []int{3, 4}) } func TestBoolApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: true, - }) + } + c := runTest(t, tis) expect(t, true, c.Bool("test")) + + c = runRacyTest(t, tis) + refute(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: false, ContextValueString: "true", - }) + } + c := runTest(t, tis) expect(t, true, c.Bool("test")) + + c = runRacyTest(t, tis) + refute(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: false, EnvVarName: "TEST", EnvVarValue: "true", - }) + } + c := runTest(t, tis) expect(t, true, c.Bool("test")) + + c = runRacyTest(t, tis) + refute(t, true, c.Bool("test")) } func TestStringApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", - }) + } + c := runTest(t, tis) expect(t, "hello", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "hello", c.String("test")) } func TestStringApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", ContextValueString: "goodbye", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringFlag(&cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", EnvVarValue: "goodbye", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } + func TestPathApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), FlagName: "test", MapValue: "hello", SourcePath: "/path/to/source/file", - }) + } + c := runTest(t, tis) expected := "/path/to/source/hello" if runtime.GOOS == "windows" { @@ -200,125 +269,176 @@ func TestPathApplyInputSourceMethodSet(t *testing.T) { } } expect(t, expected, c.String("test")) + + c = runRacyTest(t, tis) + refute(t, expected, c.String("test")) } func TestPathApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), FlagName: "test", MapValue: "hello", ContextValueString: "goodbye", SourcePath: "/path/to/source/file", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } func TestPathApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewPathFlag(&cli.PathFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", EnvVarValue: "goodbye", SourcePath: "/path/to/source/file", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } func TestIntApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, - }) + } + c := runTest(t, tis) expect(t, 15, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, 15, c.Int("test")) } func TestIntApplyInputSourceMethodSetNegativeValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: -1, - }) + } + c := runTest(t, tis) expect(t, -1, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, -1, c.Int("test")) } func TestIntApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, ContextValueString: "7", - }) + } + c := runTest(t, tis) expect(t, 7, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, 7, c.Int("test")) } func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 15, EnvVarName: "TEST", EnvVarValue: "12", - }) + } + c := runTest(t, tis) expect(t, 12, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, 12, c.Int("test")) } func TestDurationApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: 30 * time.Second, - }) + } + c := runTest(t, tis) expect(t, 30*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, 30*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodSetNegativeValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: -30 * time.Second, - }) + } + c := runTest(t, tis) expect(t, -30*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, -30*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: 30 * time.Second, ContextValueString: (15 * time.Second).String(), - }) + } + c := runTest(t, tis) expect(t, 15*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, 15*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 30 * time.Second, EnvVarName: "TEST", EnvVarValue: (15 * time.Second).String(), - }) + } + c := runTest(t, tis) expect(t, 15*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, 15*time.Second, c.Duration("test")) } func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, - }) + } + c := runTest(t, tis) expect(t, 1.3, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, 1.3, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: -1.3, - }) + } + c := runTest(t, tis) expect(t, -1.3, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, -1.3, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodSetNegativeValueNotSet(t *testing.T) { @@ -331,24 +451,32 @@ func TestFloat64ApplyInputSourceMethodSetNegativeValueNotSet(t *testing.T) { } func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, ContextValueString: fmt.Sprintf("%v", 1.4), - }) + } + c := runTest(t, tis) expect(t, 1.4, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, 1.4, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 1.3, EnvVarName: "TEST", EnvVarValue: fmt.Sprintf("%v", 1.4), - }) + } + c := runTest(t, tis) expect(t, 1.4, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, 1.4, c.Float64("test")) } func runTest(t *testing.T, test testApplyInputSource) *cli.Context { @@ -376,6 +504,19 @@ func runTest(t *testing.T, test testApplyInputSource) *cli.Context { return c } +func runRacyTest(t *testing.T, test testApplyInputSource) *cli.Context { + set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError) + c := cli.NewContext(nil, set, nil) + _ = test.Flag.ApplyInputSourceValue(c, &racyInputSource{ + MapInputSource: &MapInputSource{ + file: test.SourcePath, + valueMap: map[interface{}]interface{}{test.FlagName: test.MapValue}, + }, + }) + + return c +} + type Parser [2]string func (p *Parser) Set(value string) error { @@ -393,3 +534,5 @@ func (p *Parser) Set(value string) error { func (p *Parser) String() string { return fmt.Sprintf("%s,%s", p[0], p[1]) } + +type bogus [1]uint diff --git a/altsrc/helpers_test.go b/altsrc/helpers_test.go index 33e8a4b976..1f8d5c25fc 100644 --- a/altsrc/helpers_test.go +++ b/altsrc/helpers_test.go @@ -22,7 +22,10 @@ func expect(t *testing.T, a interface{}, b interface{}) { } func refute(t *testing.T, a interface{}, b interface{}) { - if a == b { - t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + _, fn, line, _ := runtime.Caller(1) + fn = strings.Replace(fn, wd+"/", "", -1) + + if reflect.DeepEqual(a, b) { + t.Errorf("(%s:%d) Did not expect %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } From cdb1730b7da03b1d52af0a5cf20d3cf4422c191d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 22:21:46 -0400 Subject: [PATCH 22/22] Drop extra if condition --- altsrc/map_input_source.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index cc11fc5433..e065c7cc43 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -249,11 +249,8 @@ func (fsm *MapInputSource) isSet(name string) bool { return exists } - if _, exists := nestedVal(name, fsm.valueMap); exists { - return exists - } - - return false + _, exists := nestedVal(name, fsm.valueMap) + return exists } func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error {