diff --git a/altsrc/flag.go b/altsrc/flag.go index 9801644255..1132ded35f 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -64,15 +64,22 @@ 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) { - value, err := isc.Generic(f.GenericFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.GenericFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.Generic(name) if err != nil { return err } - if value != nil { - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) - } + if value == nil { + continue + } + for _, n := range f.Names() { + _ = f.set.Set(n, value.String()) } } @@ -81,19 +88,27 @@ 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) { - value, err := isc.StringSlice(f.StringSliceFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.StringSliceFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.StringSlice(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 value == nil { + continue + } + var sliceValue = *(cli.NewStringSlice(value...)) + for _, n := range f.Names() { + underlyingFlag := f.set.Lookup(n) + if underlyingFlag == nil { + continue } + underlyingFlag.Value = &sliceValue } } return nil @@ -101,19 +116,27 @@ 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) { - value, err := isc.IntSlice(f.IntSliceFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.IntSliceFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.IntSlice(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 value == nil { + continue + } + var sliceValue = *(cli.NewIntSlice(value...)) + for _, n := range f.Names() { + underlyingFlag := f.set.Lookup(n) + if underlyingFlag == nil { + continue } + underlyingFlag.Value = &sliceValue } } return nil @@ -121,13 +144,19 @@ 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) { - value, err := isc.Bool(f.BoolFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.BoolFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.Bool(name) if err != nil { return err } - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatBool(value)) + for _, n := range f.Names() { + _ = f.set.Set(n, strconv.FormatBool(value)) } } return nil @@ -135,15 +164,19 @@ 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) { - value, err := isc.String(f.StringFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.StringFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.String(name) if err != nil { return err } - if value != "" { - for _, name := range f.Names() { - _ = f.set.Set(name, value) - } + for _, n := range f.Names() { + _ = f.set.Set(n, value) } } return nil @@ -151,25 +184,29 @@ 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) { - value, err := isc.String(f.PathFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.PathFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.String(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 - } - - value = filepath.Join(filepath.Dir(basePathAbs), value) + if value == "" { + continue + } + for _, n := range f.Names() { + 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(n, value) } } return nil @@ -177,13 +214,19 @@ 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) { - value, err := isc.Int(f.IntFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.IntFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.Int(name) if err != nil { return err } - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) + for _, n := range f.Names() { + _ = f.set.Set(n, strconv.FormatInt(int64(value), 10)) } } return nil @@ -191,13 +234,19 @@ 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) { - value, err := isc.Duration(f.DurationFlag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.DurationFlag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.Duration(name) if err != nil { return err } - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) + for _, n := range f.Names() { + _ = f.set.Set(n, value.String()) } } return nil @@ -205,14 +254,20 @@ 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) { - value, err := isc.Float64(f.Float64Flag.Name) + if f.set == nil || cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars) { + return nil + } + for _, name := range f.Float64Flag.Names() { + if !isc.isSet(name) { + continue + } + value, err := isc.Float64(name) if err != nil { return err } floatStr := float64ToString(value) - for _, name := range f.Names() { - _ = f.set.Set(name, floatStr) + for _, n := range f.Names() { + _ = f.set.Set(n, floatStr) } } return nil diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index e3725f7d1c..bece6aaf6c 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -37,6 +37,20 @@ func (ris *racyInputSource) isSet(name string) bool { return true } +func TestGenericApplyInputSourceValue_Alias(t *testing.T) { + v := &Parser{"abc", "def"} + tis := testApplyInputSource{ + Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Aliases: []string{"test_alias"}, Value: &Parser{}}), + FlagName: "test_alias", + MapValue: v, + } + c := runTest(t, tis) + expect(t, v, c.Generic("test_alias")) + + c = runRacyTest(t, tis) + refute(t, v, c.Generic("test_alias")) +} + func TestGenericApplyInputSourceValue(t *testing.T) { v := &Parser{"abc", "def"} tis := testApplyInputSource{ @@ -85,6 +99,19 @@ func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { refute(t, &Parser{"abc", "def"}, c.Generic("test")) } +func TestStringSliceApplyInputSourceValue_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: []interface{}{"hello", "world"}, + } + c := runTest(t, tis) + expect(t, c.StringSlice("test_alias"), []string{"hello", "world"}) + + c = runRacyTest(t, tis) + refute(t, c.StringSlice("test_alias"), []string{"hello", "world"}) +} + func TestStringSliceApplyInputSourceValue(t *testing.T) { tis := testApplyInputSource{ Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), @@ -123,6 +150,19 @@ func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { refute(t, c.StringSlice("test"), []string{"oh", "no"}) } +func TestIntSliceApplyInputSourceValue_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: []interface{}{1, 2}, + } + c := runTest(t, tis) + expect(t, c.IntSlice("test_alias"), []int{1, 2}) + + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test_alias"), []int{1, 2}) +} + func TestIntSliceApplyInputSourceValue(t *testing.T) { tis := testApplyInputSource{ Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), @@ -178,6 +218,19 @@ func TestBoolApplyInputSourceMethodSet(t *testing.T) { refute(t, true, c.Bool("test")) } +func TestBoolApplyInputSourceMethodSet_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewBoolFlag(&cli.BoolFlag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: true, + } + c := runTest(t, tis) + expect(t, true, c.Bool("test_alias")) + + c = runRacyTest(t, tis) + refute(t, true, c.Bool("test_alias")) +} + func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), @@ -207,6 +260,19 @@ func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { refute(t, true, c.Bool("test")) } +func TestStringApplyInputSourceMethodSet_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewStringFlag(&cli.StringFlag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: "hello", + } + c := runTest(t, tis) + expect(t, "hello", c.String("test_alias")) + + c = runRacyTest(t, tis) + refute(t, "hello", c.String("test_alias")) +} + func TestStringApplyInputSourceMethodSet(t *testing.T) { tis := testApplyInputSource{ Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), @@ -249,6 +315,31 @@ func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { refute(t, "goodbye", c.String("test")) } +func TestPathApplyInputSourceMethodSet_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewPathFlag(&cli.PathFlag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: "hello", + SourcePath: "/path/to/source/file", + } + c := runTest(t, tis) + + expected := "/path/to/source/hello" + if runtime.GOOS == "windows" { + var err error + // Prepend the corresponding drive letter (or UNC path?), and change + // to windows-style path: + expected, err = filepath.Abs(expected) + if err != nil { + t.Fatal(err) + } + } + expect(t, expected, c.String("test_alias")) + + c = runRacyTest(t, tis) + refute(t, expected, c.String("test_alias")) +} + func TestPathApplyInputSourceMethodSet(t *testing.T) { tis := testApplyInputSource{ Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), @@ -305,6 +396,19 @@ func TestPathApplyInputSourceMethodEnvVarSet(t *testing.T) { refute(t, "goodbye", c.String("test")) } +func TestIntApplyInputSourceMethodSet_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewIntFlag(&cli.IntFlag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: 15, + } + c := runTest(t, tis) + expect(t, 15, c.Int("test_alias")) + + c = runRacyTest(t, tis) + refute(t, 15, c.Int("test_alias")) +} + func TestIntApplyInputSourceMethodSet(t *testing.T) { tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), @@ -360,6 +464,19 @@ func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { refute(t, 12, c.Int("test")) } +func TestDurationApplyInputSourceMethodSet_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: 30 * time.Second, + } + c := runTest(t, tis) + expect(t, 30*time.Second, c.Duration("test_alias")) + + c = runRacyTest(t, tis) + refute(t, 30*time.Second, c.Duration("test_alias")) +} + func TestDurationApplyInputSourceMethodSet(t *testing.T) { tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), @@ -428,6 +545,19 @@ func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { refute(t, 1.3, c.Float64("test")) } +func TestFloat64ApplyInputSourceMethodSetNegativeValue_Alias(t *testing.T) { + tis := testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test", Aliases: []string{"test_alias"}}), + FlagName: "test_alias", + MapValue: -1.3, + } + c := runTest(t, tis) + expect(t, -1.3, c.Float64("test_alias")) + + c = runRacyTest(t, tis) + refute(t, -1.3, c.Float64("test_alias")) +} + func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) { tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}),