diff --git a/app_test.go b/app_test.go index ba37cee..f082724 100644 --- a/app_test.go +++ b/app_test.go @@ -2622,19 +2622,6 @@ func TestSetupInitializesOnlyNilWriters(t *testing.T) { } } -type stringGeneric struct { - value string -} - -func (s *stringGeneric) Set(value string) error { - s.value = value - return nil -} - -func (s *stringGeneric) String() string { - return s.value -} - func TestFlagAction(t *testing.T) { stringFlag := &StringFlag{ Name: "f_string", diff --git a/cmd/urfave-cli-genflags/generated.gotmpl b/cmd/urfave-cli-genflags/generated.gotmpl index bee11d5..6b16829 100644 --- a/cmd/urfave-cli-genflags/generated.gotmpl +++ b/cmd/urfave-cli-genflags/generated.gotmpl @@ -22,6 +22,8 @@ type {{.TypeName}} struct { Aliases []string EnvVars []string + defaultValue {{if .ValuePointer}}*{{end}}{{.GoType}} + {{range .StructFields}} {{.Name}} {{if .Pointer}}*{{end}}{{.Type}} {{end}} diff --git a/flag_bool.go b/flag_bool.go index 55b719a..94de735 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -69,7 +69,7 @@ func (f *BoolFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return fmt.Sprintf("%v", f.Value) + return fmt.Sprintf("%v", f.defaultValue) } // RunAction executes flag action if set @@ -83,6 +83,9 @@ func (f *BoolFlag) RunAction(c *Context) error { // Apply populates the flag given the flag set and environment func (f *BoolFlag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valBool, err := strconv.ParseBool(val) diff --git a/flag_duration.go b/flag_duration.go index 06391f3..ad2b83d 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -17,11 +17,14 @@ func (f *DurationFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + return f.defaultValue.String() } // Apply populates the flag given the flag set and environment func (f *DurationFlag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valDuration, err := time.ParseDuration(val) diff --git a/flag_generic.go b/flag_generic.go index d0f6527..0797e5a 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -11,6 +11,19 @@ type Generic interface { String() string } +type stringGeneric struct { + value string +} + +func (s *stringGeneric) Set(value string) error { + s.value = value + return nil +} + +func (s *stringGeneric) String() string { + return s.value +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *GenericFlag) GetValue() string { @@ -25,12 +38,20 @@ func (f *GenericFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + if f.defaultValue != nil { + return f.defaultValue.String() + } + return "" } // Apply takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag func (f *GenericFlag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + if f.Value != nil { + f.defaultValue = &stringGeneric{value: f.Value.String()} + } + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { if err := f.Value.Set(val); err != nil { diff --git a/flag_int.go b/flag_int.go index a716f39..c76e101 100644 --- a/flag_int.go +++ b/flag_int.go @@ -17,11 +17,14 @@ func (f *IntFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + return fmt.Sprintf("%d", f.defaultValue) } // Apply populates the flag given the flag set and environment func (f *IntFlag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseInt(val, f.Base, 64) diff --git a/flag_int64.go b/flag_int64.go index 05d8506..93eae07 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -17,11 +17,14 @@ func (f *Int64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + return fmt.Sprintf("%d", f.defaultValue) } // Apply populates the flag given the flag set and environment func (f *Int64Flag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseInt(val, f.Base, 64) diff --git a/flag_path.go b/flag_path.go index f8bb9c3..8e75953 100644 --- a/flag_path.go +++ b/flag_path.go @@ -18,14 +18,17 @@ func (f *PathFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - if f.Value == "" { - return f.Value + if f.defaultValue == "" { + return f.defaultValue } - return fmt.Sprintf("%q", f.Value) + return fmt.Sprintf("%q", f.defaultValue) } // Apply populates the flag given the flag set and environment func (f *PathFlag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { f.Value = val f.HasBeenSet = true diff --git a/flag_string.go b/flag_string.go index 669f254..b7b78c6 100644 --- a/flag_string.go +++ b/flag_string.go @@ -16,14 +16,17 @@ func (f *StringFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - if f.Value == "" { - return f.Value + if f.defaultValue == "" { + return f.defaultValue } - return fmt.Sprintf("%q", f.Value) + return fmt.Sprintf("%q", f.defaultValue) } // Apply populates the flag given the flag set and environment func (f *StringFlag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { f.Value = val f.HasBeenSet = true diff --git a/flag_test.go b/flag_test.go index 7601b4a..84bc54e 100644 --- a/flag_test.go +++ b/flag_test.go @@ -8,7 +8,6 @@ import ( "os" "reflect" "regexp" - "runtime" "strings" "testing" "time" @@ -477,6 +476,9 @@ var stringFlagTests = []struct { func TestStringFlagHelpOutput(t *testing.T) { for _, test := range stringFlagTests { fl := &StringFlag{Name: test.name, Aliases: test.aliases, Usage: test.usage, Value: test.value} + // create a tmp flagset + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) output := fl.String() if output != test.expected { @@ -504,10 +506,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { fl := &StringFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_FOO"}} output := fl.String() - expectedSuffix := " [$APP_FOO]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_FOO%]" - } + expectedSuffix := withEnvHint([]string{"APP_FOO"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -575,6 +574,11 @@ var pathFlagTests = []struct { func TestPathFlagHelpOutput(t *testing.T) { for _, test := range pathFlagTests { fl := &PathFlag{Name: test.name, Aliases: test.aliases, Usage: test.usage, Value: test.value} + + // create a temporary flag set to apply + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) + output := fl.String() if output != test.expected { @@ -591,10 +595,7 @@ func TestPathFlagWithEnvVarHelpOutput(t *testing.T) { fl := &PathFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_PATH"}} output := fl.String() - expectedSuffix := " [$APP_PATH]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_PATH%]" - } + expectedSuffix := withEnvHint([]string{"APP_PATH"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -688,10 +689,7 @@ func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { fl := &StringSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_QWWX"}} output := fl.String() - expectedSuffix := " [$APP_QWWX]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_QWWX%]" - } + expectedSuffix := withEnvHint([]string{"APP_QWWX"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } @@ -767,6 +765,11 @@ var intFlagTests = []struct { func TestIntFlagHelpOutput(t *testing.T) { for _, test := range intFlagTests { fl := &IntFlag{Name: test.name, Value: 9} + + // create a temporary flag set to apply + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) + output := fl.String() if output != test.expected { @@ -784,10 +787,7 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { fl := &IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := fl.String() - expectedSuffix := " [$APP_BAR]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_BAR%]" - } + expectedSuffix := withEnvHint([]string{"APP_BAR"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -824,6 +824,11 @@ var int64FlagTests = []struct { func TestInt64FlagHelpOutput(t *testing.T) { for _, test := range int64FlagTests { fl := Int64Flag{Name: test.name, Value: 8589934592} + + // create a temporary flag set to apply + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) + output := fl.String() if output != test.expected { @@ -841,10 +846,7 @@ func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { fl := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := fl.String() - expectedSuffix := " [$APP_BAR]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_BAR%]" - } + expectedSuffix := withEnvHint([]string{"APP_BAR"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -870,6 +872,11 @@ var uintFlagTests = []struct { func TestUintFlagHelpOutput(t *testing.T) { for _, test := range uintFlagTests { fl := UintFlag{Name: test.name, Value: 41} + + // create a temporary flag set to apply + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) + output := fl.String() if output != test.expected { @@ -887,10 +894,7 @@ func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { fl := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := fl.String() - expectedSuffix := " [$APP_BAR]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_BAR%]" - } + expectedSuffix := withEnvHint([]string{"APP_BAR"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -916,6 +920,11 @@ var uint64FlagTests = []struct { func TestUint64FlagHelpOutput(t *testing.T) { for _, test := range uint64FlagTests { fl := Uint64Flag{Name: test.name, Value: 8589934582} + + // create a temporary flag set to apply + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) + output := fl.String() if output != test.expected { @@ -933,10 +942,7 @@ func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { fl := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := fl.String() - expectedSuffix := " [$APP_BAR]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_BAR%]" - } + expectedSuffix := withEnvHint([]string{"APP_BAR"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -962,6 +968,11 @@ var durationFlagTests = []struct { func TestDurationFlagHelpOutput(t *testing.T) { for _, test := range durationFlagTests { fl := &DurationFlag{Name: test.name, Value: 1 * time.Second} + + // create a temporary flag set to apply + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) + output := fl.String() if output != test.expected { @@ -979,10 +990,7 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { fl := &DurationFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := fl.String() - expectedSuffix := " [$APP_BAR]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_BAR%]" - } + expectedSuffix := withEnvHint([]string{"APP_BAR"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -1039,10 +1047,7 @@ func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { fl := &IntSliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := fl.String() - expectedSuffix := " [$APP_SMURF]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_SMURF%]" - } + expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } @@ -1178,10 +1183,7 @@ func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) { fl := Int64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := fl.String() - expectedSuffix := " [$APP_SMURF]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_SMURF%]" - } + expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } @@ -1332,10 +1334,7 @@ func TestUintSliceFlagWithEnvVarHelpOutput(t *testing.T) { fl := UintSliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := fl.String() - expectedSuffix := " [$APP_SMURF]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_SMURF%]" - } + expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } @@ -1478,10 +1477,7 @@ func TestUint64SliceFlagWithEnvVarHelpOutput(t *testing.T) { fl := Uint64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := fl.String() - expectedSuffix := " [$APP_SMURF]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_SMURF%]" - } + expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } @@ -1620,10 +1616,7 @@ func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { fl := &Float64Flag{Name: test.name, EnvVars: []string{"APP_BAZ"}} output := fl.String() - expectedSuffix := " [$APP_BAZ]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_BAZ%]" - } + expectedSuffix := withEnvHint([]string{"APP_BAZ"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -1680,10 +1673,7 @@ func TestFloat64SliceFlagWithEnvVarHelpOutput(t *testing.T) { fl := Float64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := fl.String() - expectedSuffix := " [$APP_SMURF]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_SMURF%]" - } + expectedSuffix := withEnvHint([]string{"APP_SMURF"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } @@ -1783,6 +1773,9 @@ var genericFlagTests = []struct { func TestGenericFlagHelpOutput(t *testing.T) { for _, test := range genericFlagTests { fl := &GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"} + // create a temporary flag set to apply + tfs := flag.NewFlagSet("test", 0) + fl.Apply(tfs) output := fl.String() if output != test.expected { @@ -1800,10 +1793,7 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { fl := &GenericFlag{Name: test.name, EnvVars: []string{"APP_ZAP"}} output := fl.String() - expectedSuffix := " [$APP_ZAP]" - if runtime.GOOS == "windows" { - expectedSuffix = " [%APP_ZAP%]" - } + expectedSuffix := withEnvHint([]string{"APP_ZAP"}, "") if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } @@ -3100,6 +3090,186 @@ func TestFlagDefaultValue(t *testing.T) { } } +type flagDefaultTestCaseWithEnv struct { + name string + flag Flag + toParse []string + expect string + environ map[string]string +} + +func TestFlagDefaultValueWithEnv(t *testing.T) { + defer resetEnv(os.Environ()) + os.Clearenv() + + ts, err := time.Parse(time.RFC3339, "2005-01-02T15:04:05Z") + if err != nil { + t.Fatal(err) + } + cases := []*flagDefaultTestCaseWithEnv{ + { + name: "stringSlice", + flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2"), EnvVars: []string{"ssflag"}}, + toParse: []string{"--flag", "parsed"}, + expect: `--flag value [ --flag value ] (default: "default1", "default2")` + withEnvHint([]string{"ssflag"}, ""), + environ: map[string]string{ + "ssflag": "some-other-env_value", + }, + }, + { + name: "float64Slice", + flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2), EnvVars: []string{"fsflag"}}, + toParse: []string{"--flag", "13.3"}, + expect: `--flag value [ --flag value ] (default: 1.1, 2.2)` + withEnvHint([]string{"fsflag"}, ""), + environ: map[string]string{ + "fsflag": "20304.222", + }, + }, + { + name: "int64Slice", + flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2), EnvVars: []string{"isflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"isflag"}, ""), + environ: map[string]string{ + "isflag": "101", + }, + }, + { + name: "intSlice", + flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2), EnvVars: []string{"isflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"isflag"}, ""), + environ: map[string]string{ + "isflag": "101", + }, + }, + { + name: "uint64Slice", + flag: &Uint64SliceFlag{Name: "flag", Value: NewUint64Slice(1, 2), EnvVars: []string{"uisflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"uisflag"}, ""), + environ: map[string]string{ + "uisflag": "3", + }, + }, + { + name: "uintSlice", + flag: &UintSliceFlag{Name: "flag", Value: NewUintSlice(1, 2), EnvVars: []string{"uisflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value [ --flag value ] (default: 1, 2)` + withEnvHint([]string{"uisflag"}, ""), + environ: map[string]string{ + "uisflag": "3", + }, + }, + { + name: "string", + flag: &StringFlag{Name: "flag", Value: "default", EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "parsed"}, + expect: `--flag value (default: "default")` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "some-other-string", + }, + }, + { + name: "bool", + flag: &BoolFlag{Name: "flag", Value: true, EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "false"}, + expect: `--flag (default: true)` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "false", + }, + }, + { + name: "uint64", + flag: &Uint64Flag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "10", + }, + }, + { + name: "uint", + flag: &UintFlag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "10", + }, + }, + { + name: "int64", + flag: &Int64Flag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "10", + }, + }, + { + name: "int", + flag: &IntFlag{Name: "flag", Value: 1, EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "13"}, + expect: `--flag value (default: 1)` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "10", + }, + }, + { + name: "duration", + flag: &DurationFlag{Name: "flag", Value: time.Second, EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "2m"}, + expect: `--flag value (default: 1s)` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "2h4m10s", + }, + }, + { + name: "path", + flag: &PathFlag{Name: "flag", Value: "/tmp/foo", EnvVars: []string{"uflag"}}, + toParse: []string{"--flag", "/bar/ddfr"}, + expect: `--flag value (default: "/tmp/foo")` + withEnvHint([]string{"uflag"}, ""), + environ: map[string]string{ + "uflag": "/bar/t/err", + }, + }, + { + name: "timestamp", + flag: &TimestampFlag{Name: "flag", Value: NewTimestamp(ts), Layout: time.RFC3339, EnvVars: []string{"tflag"}}, + toParse: []string{"--flag", "2006-11-02T15:04:05Z"}, + expect: `--flag value (default: 2005-01-02 15:04:05 +0000 UTC)` + withEnvHint([]string{"tflag"}, ""), + environ: map[string]string{ + "tflag": "2010-01-02T15:04:05Z", + }, + }, + { + name: "generic", + flag: &GenericFlag{Name: "flag", Value: &Parser{"11", "12"}, EnvVars: []string{"gflag"}}, + toParse: []string{"--flag", "15,16"}, + expect: `--flag value (default: 11,12)` + withEnvHint([]string{"gflag"}, ""), + environ: map[string]string{ + "gflag": "13,14", + }, + }, + } + for i, v := range cases { + for key, val := range v.environ { + os.Setenv(key, val) + } + set := flag.NewFlagSet("test", 0) + set.SetOutput(ioutil.Discard) + if err := v.flag.Apply(set); err != nil { + t.Fatal(err) + } + if err := set.Parse(v.toParse); err != nil { + t.Error(err) + } + if got := v.flag.String(); got != v.expect { + t.Errorf("TestFlagDefaultValue %d %s\nexpect:%s\ngot:%s", i, v.name, v.expect, got) + } + } +} + type flagValueTestCase struct { name string flag Flag diff --git a/flag_timestamp.go b/flag_timestamp.go index 4302f13..cc90d42 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -72,6 +72,25 @@ func (t *Timestamp) Get() interface{} { return *t } +// clone timestamp +func (t *Timestamp) clone() *Timestamp { + tc := &Timestamp{ + timestamp: nil, + hasBeenSet: t.hasBeenSet, + layout: t.layout, + location: nil, + } + if t.timestamp != nil { + tts := *t.timestamp + tc.timestamp = &tts + } + if t.location != nil { + loc := *t.location + tc.location = &loc + } + return tc +} + // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f *TimestampFlag) GetValue() string { @@ -86,7 +105,11 @@ func (f *TimestampFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + if f.defaultValue != nil && f.defaultValue.timestamp != nil { + return f.defaultValue.timestamp.String() + } + + return "" } // Apply populates the flag given the flag set and environment @@ -100,6 +123,8 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { f.Value.SetLayout(f.Layout) f.Value.SetLocation(f.Timezone) + f.defaultValue = f.Value.clone() + if f.Destination != nil { f.Destination.SetLayout(f.Layout) f.Destination.SetLocation(f.Timezone) diff --git a/flag_uint.go b/flag_uint.go index 2214386..90a5318 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -8,6 +8,9 @@ import ( // Apply populates the flag given the flag set and environment func (f *UintFlag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseUint(val, f.Base, 64) @@ -42,7 +45,7 @@ func (f *UintFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + return fmt.Sprintf("%d", f.defaultValue) } // Get returns the flag’s value in the given Context. diff --git a/flag_uint64.go b/flag_uint64.go index e314a4a..c66367b 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -8,6 +8,9 @@ import ( // Apply populates the flag given the flag set and environment func (f *Uint64Flag) Apply(set *flag.FlagSet) error { + // set default value so that environment wont be able to overwrite it + f.defaultValue = f.Value + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valInt, err := strconv.ParseUint(val, f.Base, 64) @@ -42,7 +45,7 @@ func (f *Uint64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + return fmt.Sprintf("%d", f.defaultValue) } // Get returns the flag’s value in the given Context. diff --git a/godoc-current.txt b/godoc-current.txt index 2859237..2b1b772 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -445,6 +445,7 @@ type BoolFlag struct { Count *int Action func(*Context, bool) error + // Has unexported fields. } BoolFlag is a flag with type bool @@ -774,6 +775,7 @@ type DurationFlag struct { EnvVars []string Action func(*Context, time.Duration) error + // Has unexported fields. } DurationFlag is a flag with type time.Duration @@ -957,6 +959,7 @@ type Float64Flag struct { EnvVars []string Action func(*Context, float64) error + // Has unexported fields. } Float64Flag is a flag with type float64 @@ -1045,6 +1048,7 @@ type Float64SliceFlag struct { EnvVars []string Action func(*Context, []float64) error + // Has unexported fields. } Float64SliceFlag is a flag with type *Float64Slice @@ -1127,6 +1131,7 @@ type GenericFlag struct { TakesFile bool Action func(*Context, interface{}) error + // Has unexported fields. } GenericFlag is a flag with type Generic @@ -1195,6 +1200,7 @@ type Int64Flag struct { Base int Action func(*Context, int64) error + // Has unexported fields. } Int64Flag is a flag with type int64 @@ -1283,6 +1289,7 @@ type Int64SliceFlag struct { EnvVars []string Action func(*Context, []int64) error + // Has unexported fields. } Int64SliceFlag is a flag with type *Int64Slice @@ -1359,6 +1366,7 @@ type IntFlag struct { Base int Action func(*Context, int) error + // Has unexported fields. } IntFlag is a flag with type int @@ -1451,6 +1459,7 @@ type IntSliceFlag struct { EnvVars []string Action func(*Context, []int) error + // Has unexported fields. } IntSliceFlag is a flag with type *IntSlice @@ -1561,6 +1570,7 @@ type PathFlag struct { TakesFile bool Action func(*Context, Path) error + // Has unexported fields. } PathFlag is a flag with type Path @@ -1704,6 +1714,7 @@ type StringFlag struct { TakesFile bool Action func(*Context, string) error + // Has unexported fields. } StringFlag is a flag with type string @@ -1794,6 +1805,7 @@ type StringSliceFlag struct { TakesFile bool Action func(*Context, []string) error + // Has unexported fields. } StringSliceFlag is a flag with type *StringSlice @@ -1905,6 +1917,7 @@ type TimestampFlag struct { Timezone *time.Location Action func(*Context, *time.Time) error + // Has unexported fields. } TimestampFlag is a flag with type *Timestamp @@ -1972,6 +1985,7 @@ type Uint64Flag struct { Base int Action func(*Context, uint64) error + // Has unexported fields. } Uint64Flag is a flag with type uint64 @@ -2060,6 +2074,7 @@ type Uint64SliceFlag struct { EnvVars []string Action func(*Context, []uint64) error + // Has unexported fields. } Uint64SliceFlag is a flag with type *Uint64Slice @@ -2130,6 +2145,7 @@ type UintFlag struct { Base int Action func(*Context, uint) error + // Has unexported fields. } UintFlag is a flag with type uint @@ -2222,6 +2238,7 @@ type UintSliceFlag struct { EnvVars []string Action func(*Context, []uint) error + // Has unexported fields. } UintSliceFlag is a flag with type *UintSlice diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 10914a3..fc80eb9 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -445,6 +445,7 @@ type BoolFlag struct { Count *int Action func(*Context, bool) error + // Has unexported fields. } BoolFlag is a flag with type bool @@ -774,6 +775,7 @@ type DurationFlag struct { EnvVars []string Action func(*Context, time.Duration) error + // Has unexported fields. } DurationFlag is a flag with type time.Duration @@ -957,6 +959,7 @@ type Float64Flag struct { EnvVars []string Action func(*Context, float64) error + // Has unexported fields. } Float64Flag is a flag with type float64 @@ -1045,6 +1048,7 @@ type Float64SliceFlag struct { EnvVars []string Action func(*Context, []float64) error + // Has unexported fields. } Float64SliceFlag is a flag with type *Float64Slice @@ -1127,6 +1131,7 @@ type GenericFlag struct { TakesFile bool Action func(*Context, interface{}) error + // Has unexported fields. } GenericFlag is a flag with type Generic @@ -1195,6 +1200,7 @@ type Int64Flag struct { Base int Action func(*Context, int64) error + // Has unexported fields. } Int64Flag is a flag with type int64 @@ -1283,6 +1289,7 @@ type Int64SliceFlag struct { EnvVars []string Action func(*Context, []int64) error + // Has unexported fields. } Int64SliceFlag is a flag with type *Int64Slice @@ -1359,6 +1366,7 @@ type IntFlag struct { Base int Action func(*Context, int) error + // Has unexported fields. } IntFlag is a flag with type int @@ -1451,6 +1459,7 @@ type IntSliceFlag struct { EnvVars []string Action func(*Context, []int) error + // Has unexported fields. } IntSliceFlag is a flag with type *IntSlice @@ -1561,6 +1570,7 @@ type PathFlag struct { TakesFile bool Action func(*Context, Path) error + // Has unexported fields. } PathFlag is a flag with type Path @@ -1704,6 +1714,7 @@ type StringFlag struct { TakesFile bool Action func(*Context, string) error + // Has unexported fields. } StringFlag is a flag with type string @@ -1794,6 +1805,7 @@ type StringSliceFlag struct { TakesFile bool Action func(*Context, []string) error + // Has unexported fields. } StringSliceFlag is a flag with type *StringSlice @@ -1905,6 +1917,7 @@ type TimestampFlag struct { Timezone *time.Location Action func(*Context, *time.Time) error + // Has unexported fields. } TimestampFlag is a flag with type *Timestamp @@ -1972,6 +1985,7 @@ type Uint64Flag struct { Base int Action func(*Context, uint64) error + // Has unexported fields. } Uint64Flag is a flag with type uint64 @@ -2060,6 +2074,7 @@ type Uint64SliceFlag struct { EnvVars []string Action func(*Context, []uint64) error + // Has unexported fields. } Uint64SliceFlag is a flag with type *Uint64Slice @@ -2130,6 +2145,7 @@ type UintFlag struct { Base int Action func(*Context, uint) error + // Has unexported fields. } UintFlag is a flag with type uint @@ -2222,6 +2238,7 @@ type UintSliceFlag struct { EnvVars []string Action func(*Context, []uint) error + // Has unexported fields. } UintSliceFlag is a flag with type *UintSlice diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 8c4e6fa..e8497ee 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -23,6 +23,8 @@ type Float64SliceFlag struct { Aliases []string EnvVars []string + defaultValue *Float64Slice + Action func(*Context, []float64) error } @@ -85,6 +87,8 @@ type GenericFlag struct { Aliases []string EnvVars []string + defaultValue Generic + TakesFile bool Action func(*Context, interface{}) error @@ -154,6 +158,8 @@ type Int64SliceFlag struct { Aliases []string EnvVars []string + defaultValue *Int64Slice + Action func(*Context, []int64) error } @@ -216,6 +222,8 @@ type IntSliceFlag struct { Aliases []string EnvVars []string + defaultValue *IntSlice + Action func(*Context, []int) error } @@ -278,6 +286,8 @@ type PathFlag struct { Aliases []string EnvVars []string + defaultValue Path + TakesFile bool Action func(*Context, Path) error @@ -347,6 +357,8 @@ type StringSliceFlag struct { Aliases []string EnvVars []string + defaultValue *StringSlice + TakesFile bool Action func(*Context, []string) error @@ -411,6 +423,8 @@ type TimestampFlag struct { Aliases []string EnvVars []string + defaultValue *Timestamp + Layout string Timezone *time.Location @@ -482,6 +496,8 @@ type Uint64SliceFlag struct { Aliases []string EnvVars []string + defaultValue *Uint64Slice + Action func(*Context, []uint64) error } @@ -544,6 +560,8 @@ type UintSliceFlag struct { Aliases []string EnvVars []string + defaultValue *UintSlice + Action func(*Context, []uint) error } @@ -606,6 +624,8 @@ type BoolFlag struct { Aliases []string EnvVars []string + defaultValue bool + Count *int Action func(*Context, bool) error @@ -675,6 +695,8 @@ type Float64Flag struct { Aliases []string EnvVars []string + defaultValue float64 + Action func(*Context, float64) error } @@ -742,6 +764,8 @@ type IntFlag struct { Aliases []string EnvVars []string + defaultValue int + Base int Action func(*Context, int) error @@ -811,6 +835,8 @@ type Int64Flag struct { Aliases []string EnvVars []string + defaultValue int64 + Base int Action func(*Context, int64) error @@ -880,6 +906,8 @@ type StringFlag struct { Aliases []string EnvVars []string + defaultValue string + TakesFile bool Action func(*Context, string) error @@ -949,6 +977,8 @@ type DurationFlag struct { Aliases []string EnvVars []string + defaultValue time.Duration + Action func(*Context, time.Duration) error } @@ -1016,6 +1046,8 @@ type UintFlag struct { Aliases []string EnvVars []string + defaultValue uint + Base int Action func(*Context, uint) error @@ -1085,6 +1117,8 @@ type Uint64Flag struct { Aliases []string EnvVars []string + defaultValue uint64 + Base int Action func(*Context, uint64) error