From 4609ad2fe47890bfc8536a1c247b2d589d13683a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 23 Jan 2023 20:58:45 -0500 Subject: [PATCH 01/13] Target two most recent Go versions in v2 --- .github/workflows/cli.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 3c244153c3..9c9707df90 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - go: [1.17.x, 1.18.x, 1.19.x] + go: [1.18.x, 1.19.x] name: ${{ matrix.os }} @ Go ${{ matrix.go }} runs-on: ${{ matrix.os }} steps: From dc6dfb7851fbaa6519a9691ac921c9c7e072abc8 Mon Sep 17 00:00:00 2001 From: Rachel Chen Date: Sat, 28 Jan 2023 16:56:11 -0800 Subject: [PATCH 02/13] flag: remove dependencies on shared variables See https://github.com/urfave/cli/issues/1670 for context This commit creates a new type separatorSpec that will get passed into flag parser, instead of reading from shared variables. --- app.go | 24 +++++++++++++++--------- app_test.go | 4 ++-- command.go | 4 +++- flag-spec.yaml | 12 ++++++++++++ flag.go | 31 ++++++++++++++++++++++++++----- flag_float64_slice.go | 14 ++++++++++++-- flag_int64_slice.go | 14 ++++++++++++-- flag_int_slice.go | 14 ++++++++++++-- flag_string_slice.go | 14 ++++++++++++-- flag_test.go | 34 ++++++++++++++++++++++++---------- flag_uint64_slice.go | 14 ++++++++++++-- flag_uint_slice.go | 14 ++++++++++++-- zz_generated.flags.go | 12 ++++++++++++ 13 files changed, 166 insertions(+), 39 deletions(-) diff --git a/app.go b/app.go index 10198f4332..04aa725eb7 100644 --- a/app.go +++ b/app.go @@ -121,7 +121,8 @@ type App struct { // Treat all flags as normal arguments if true SkipFlagParsing bool - didSetup bool + didSetup bool + separator separatorSpec rootCommand *Command } @@ -216,6 +217,16 @@ func (a *App) Setup() { }) } + if len(a.SliceFlagSeparator) != 0 { + a.separator.customized = true + a.separator.sep = a.SliceFlagSeparator + } + + if a.DisableSliceFlagSeparator { + a.separator.customized = true + a.separator.disabled = true + } + var newCommands []*Command for _, c := range a.Commands { @@ -223,8 +234,8 @@ func (a *App) Setup() { if c.HelpName != "" { cname = c.HelpName } + c.separator = a.separator c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname) - c.flagCategories = newFlagCategoriesFromFlags(c.Flags) newCommands = append(newCommands, c) } @@ -262,12 +273,6 @@ func (a *App) Setup() { if a.Metadata == nil { a.Metadata = make(map[string]interface{}) } - - if len(a.SliceFlagSeparator) != 0 { - defaultSliceFlagSeparator = a.SliceFlagSeparator - } - - disableSliceFlagSeparator = a.DisableSliceFlagSeparator } func (a *App) newRootCommand() *Command { @@ -293,11 +298,12 @@ func (a *App) newRootCommand() *Command { categories: a.categories, SkipFlagParsing: a.SkipFlagParsing, isRoot: true, + separator: a.separator, } } func (a *App) newFlagSet() (*flag.FlagSet, error) { - return flagSet(a.Name, a.Flags) + return flagSet(a.Name, a.Flags, a.separator) } func (a *App) useShortOptionHandling() bool { diff --git a/app_test.go b/app_test.go index 7f2a2144e5..c409eb9c95 100644 --- a/app_test.go +++ b/app_test.go @@ -2532,7 +2532,7 @@ func TestCustomHelpVersionFlags(t *testing.T) { func TestHandleExitCoder_Default(t *testing.T) { app := newTestApp() - fs, err := flagSet(app.Name, app.Flags) + fs, err := flagSet(app.Name, app.Flags, separatorSpec{}) if err != nil { t.Errorf("error creating FlagSet: %s", err) } @@ -2548,7 +2548,7 @@ func TestHandleExitCoder_Default(t *testing.T) { func TestHandleExitCoder_Custom(t *testing.T) { app := newTestApp() - fs, err := flagSet(app.Name, app.Flags) + fs, err := flagSet(app.Name, app.Flags, separatorSpec{}) if err != nil { t.Errorf("error creating FlagSet: %s", err) } diff --git a/command.go b/command.go index da9cf5302a..f978b4a43e 100644 --- a/command.go +++ b/command.go @@ -69,6 +69,8 @@ type Command struct { // if this is a root "special" command isRoot bool + + separator separatorSpec } type Commands []*Command @@ -275,7 +277,7 @@ func (c *Command) Run(cCtx *Context, arguments ...string) (err error) { } func (c *Command) newFlagSet() (*flag.FlagSet, error) { - return flagSet(c.Name, c.Flags) + return flagSet(c.Name, c.Flags, c.separator) } func (c *Command) useShortOptionHandling() bool { diff --git a/flag-spec.yaml b/flag-spec.yaml index cfe47df06d..ed4db985d0 100644 --- a/flag-spec.yaml +++ b/flag-spec.yaml @@ -20,6 +20,8 @@ flag_types: skip_interfaces: - fmt.Stringer struct_fields: + - name: separator + type: separatorSpec - name: Action type: "func(*Context, []float64) error" int: @@ -33,6 +35,8 @@ flag_types: skip_interfaces: - fmt.Stringer struct_fields: + - name: separator + type: separatorSpec - name: Action type: "func(*Context, []int) error" int64: @@ -46,6 +50,8 @@ flag_types: skip_interfaces: - fmt.Stringer struct_fields: + - name: separator + type: separatorSpec - name: Action type: "func(*Context, []int64) error" uint: @@ -59,6 +65,8 @@ flag_types: skip_interfaces: - fmt.Stringer struct_fields: + - name: separator + type: separatorSpec - name: Action type: "func(*Context, []uint) error" uint64: @@ -72,6 +80,8 @@ flag_types: skip_interfaces: - fmt.Stringer struct_fields: + - name: separator + type: separatorSpec - name: Action type: "func(*Context, []uint64) error" string: @@ -85,6 +95,8 @@ flag_types: skip_interfaces: - fmt.Stringer struct_fields: + - name: separator + type: separatorSpec - name: TakesFile type: bool - name: Action diff --git a/flag.go b/flag.go index 5260f7f9e2..fc3744ec8e 100644 --- a/flag.go +++ b/flag.go @@ -15,7 +15,7 @@ import ( const defaultPlaceholder = "value" -var ( +const ( defaultSliceFlagSeparator = "," disableSliceFlagSeparator = false ) @@ -167,10 +167,13 @@ type Countable interface { Count() int } -func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { +func flagSet(name string, flags []Flag, spec separatorSpec) (*flag.FlagSet, error) { set := flag.NewFlagSet(name, flag.ContinueOnError) for _, f := range flags { + if c, ok := f.(customizedSeparator); ok { + c.WithSeparatorSpec(spec) + } if err := f.Apply(set); err != nil { return nil, err } @@ -389,10 +392,28 @@ func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhe return "", "", false } -func flagSplitMultiValues(val string) []string { - if disableSliceFlagSeparator { +type customizedSeparator interface { + WithSeparatorSpec(separatorSpec) +} + +type separatorSpec struct { + sep string + disabled bool + customized bool +} + +func (s separatorSpec) flagSplitMultiValues(val string) []string { + var ( + disabled bool = s.disabled + sep string = s.sep + ) + if !s.customized { + disabled = disableSliceFlagSeparator + sep = defaultSliceFlagSeparator + } + if disabled { return []string{val} } - return strings.Split(val, defaultSliceFlagSeparator) + return strings.Split(val, sep) } diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 413aa50e9f..0bc4612c82 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -11,6 +11,7 @@ import ( // Float64Slice wraps []float64 to satisfy flag.Value type Float64Slice struct { slice []float64 + separator separatorSpec hasBeenSet bool } @@ -29,6 +30,10 @@ func (f *Float64Slice) clone() *Float64Slice { return n } +func (f *Float64Slice) WithSeparatorSpec(spec separatorSpec) { + f.separator = spec +} + // Set parses the value into a float64 and appends it to the list of values func (f *Float64Slice) Set(value string) error { if !f.hasBeenSet { @@ -43,7 +48,7 @@ func (f *Float64Slice) Set(value string) error { return nil } - for _, s := range flagSplitMultiValues(value) { + for _, s := range f.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64) if err != nil { return err @@ -148,11 +153,12 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { setValue = f.Value.clone() default: setValue = new(Float64Slice) + setValue.WithSeparatorSpec(f.separator) } if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { - for _, s := range flagSplitMultiValues(val) { + for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", val, source, f.Name, err) } @@ -172,6 +178,10 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { return nil } +func (f *Float64SliceFlag) WithSeparatorSpec(spec separatorSpec) { + f.separator = spec +} + // Get returns the flag’s value in the given Context. func (f *Float64SliceFlag) Get(ctx *Context) []float64 { return ctx.Float64Slice(f.Name) diff --git a/flag_int64_slice.go b/flag_int64_slice.go index c45c43d3ab..d45c2dd440 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -11,6 +11,7 @@ import ( // Int64Slice wraps []int64 to satisfy flag.Value type Int64Slice struct { slice []int64 + separator separatorSpec hasBeenSet bool } @@ -29,6 +30,10 @@ func (i *Int64Slice) clone() *Int64Slice { return n } +func (i *Int64Slice) WithSeparatorSpec(spec separatorSpec) { + i.separator = spec +} + // Set parses the value into an integer and appends it to the list of values func (i *Int64Slice) Set(value string) error { if !i.hasBeenSet { @@ -43,7 +48,7 @@ func (i *Int64Slice) Set(value string) error { return nil } - for _, s := range flagSplitMultiValues(value) { + for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) if err != nil { return err @@ -149,10 +154,11 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { setValue = f.Value.clone() default: setValue = new(Int64Slice) + setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { - for _, s := range flagSplitMultiValues(val) { + for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err) } @@ -171,6 +177,10 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { return nil } +func (f *Int64SliceFlag) WithSeparatorSpec(spec separatorSpec) { + f.separator = spec +} + // Get returns the flag’s value in the given Context. func (f *Int64SliceFlag) Get(ctx *Context) []int64 { return ctx.Int64Slice(f.Name) diff --git a/flag_int_slice.go b/flag_int_slice.go index d4006e594c..da9c09bc73 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -11,6 +11,7 @@ import ( // IntSlice wraps []int to satisfy flag.Value type IntSlice struct { slice []int + separator separatorSpec hasBeenSet bool } @@ -40,6 +41,10 @@ func (i *IntSlice) SetInt(value int) { i.slice = append(i.slice, value) } +func (i *IntSlice) WithSeparatorSpec(spec separatorSpec) { + i.separator = spec +} + // Set parses the value into an integer and appends it to the list of values func (i *IntSlice) Set(value string) error { if !i.hasBeenSet { @@ -54,7 +59,7 @@ func (i *IntSlice) Set(value string) error { return nil } - for _, s := range flagSplitMultiValues(value) { + for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) if err != nil { return err @@ -160,10 +165,11 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { setValue = f.Value.clone() default: setValue = new(IntSlice) + setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { - for _, s := range flagSplitMultiValues(val) { + for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int slice value from %s for flag %s: %s", val, source, f.Name, err) } @@ -182,6 +188,10 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { return nil } +func (f *IntSliceFlag) WithSeparatorSpec(spec separatorSpec) { + f.separator = spec +} + // Get returns the flag’s value in the given Context. func (f *IntSliceFlag) Get(ctx *Context) []int { return ctx.IntSlice(f.Name) diff --git a/flag_string_slice.go b/flag_string_slice.go index 82eeac06d8..82410dbc8b 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -11,6 +11,7 @@ import ( // StringSlice wraps a []string to satisfy flag.Value type StringSlice struct { slice []string + separator separatorSpec hasBeenSet bool } @@ -43,13 +44,17 @@ func (s *StringSlice) Set(value string) error { return nil } - for _, t := range flagSplitMultiValues(value) { + for _, t := range s.separator.flagSplitMultiValues(value) { s.slice = append(s.slice, t) } return nil } +func (s *StringSlice) WithSeparatorSpec(spec separatorSpec) { + s.separator = spec +} + // String returns a readable representation of this value (for usage defaults) func (s *StringSlice) String() string { return fmt.Sprintf("%s", s.slice) @@ -141,10 +146,11 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { setValue = f.Value.clone() default: setValue = new(StringSlice) + setValue.WithSeparatorSpec(f.separator) } if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { - for _, s := range flagSplitMultiValues(val) { + for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err) } @@ -163,6 +169,10 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { return nil } +func (f *StringSliceFlag) WithSeparatorSpec(spec separatorSpec) { + f.separator = spec +} + // Get returns the flag’s value in the given Context. func (f *StringSliceFlag) Get(ctx *Context) []string { return ctx.StringSlice(f.Name) diff --git a/flag_test.go b/flag_test.go index c1523717ff..132a9ede4a 100644 --- a/flag_test.go +++ b/flag_test.go @@ -3425,13 +3425,27 @@ func TestSliceShortOptionHandle(t *testing.T) { } // Test issue #1541 +func TestDefaultSliceFlagSeparator(t *testing.T) { + separator := separatorSpec{} + opts := []string{"opt1", "opt2", "opt3", "opt4"} + ret := separator.flagSplitMultiValues(strings.Join(opts, ",")) + if len(ret) != 4 { + t.Fatalf("split slice flag failed, want: 4, but get: %d", len(ret)) + } + for idx, r := range ret { + if r != opts[idx] { + t.Fatalf("get %dth failed, wanted: %s, but get: %s", idx, opts[idx], r) + } + } +} + func TestCustomizedSliceFlagSeparator(t *testing.T) { - defaultSliceFlagSeparator = ";" - defer func() { - defaultSliceFlagSeparator = "," - }() + separator := separatorSpec{ + customized: true, + sep: ";", + } opts := []string{"opt1", "opt2", "opt3,op", "opt4"} - ret := flagSplitMultiValues(strings.Join(opts, ";")) + ret := separator.flagSplitMultiValues(strings.Join(opts, ";")) if len(ret) != 4 { t.Fatalf("split slice flag failed, want: 4, but get: %d", len(ret)) } @@ -3443,13 +3457,13 @@ func TestCustomizedSliceFlagSeparator(t *testing.T) { } func TestFlagSplitMultiValues_Disabled(t *testing.T) { - disableSliceFlagSeparator = true - defer func() { - disableSliceFlagSeparator = false - }() + separator := separatorSpec{ + customized: true, + disabled: true, + } opts := []string{"opt1", "opt2", "opt3,op", "opt4"} - ret := flagSplitMultiValues(strings.Join(opts, defaultSliceFlagSeparator)) + ret := separator.flagSplitMultiValues(strings.Join(opts, defaultSliceFlagSeparator)) if len(ret) != 1 { t.Fatalf("failed to disable split slice flag, want: 1, but got: %d", len(ret)) } diff --git a/flag_uint64_slice.go b/flag_uint64_slice.go index 61bb30b551..e845dd5257 100644 --- a/flag_uint64_slice.go +++ b/flag_uint64_slice.go @@ -11,6 +11,7 @@ import ( // Uint64Slice wraps []int64 to satisfy flag.Value type Uint64Slice struct { slice []uint64 + separator separatorSpec hasBeenSet bool } @@ -43,7 +44,7 @@ func (i *Uint64Slice) Set(value string) error { return nil } - for _, s := range flagSplitMultiValues(value) { + for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 64) if err != nil { return err @@ -55,6 +56,10 @@ func (i *Uint64Slice) Set(value string) error { return nil } +func (i *Uint64Slice) WithSeparatorSpec(spec separatorSpec) { + i.separator = spec +} + // String returns a readable representation of this value (for usage defaults) func (i *Uint64Slice) String() string { v := i.slice @@ -153,10 +158,11 @@ func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error { setValue = f.Value.clone() default: setValue = new(Uint64Slice) + setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { - for _, s := range flagSplitMultiValues(val) { + for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as uint64 slice value from %s for flag %s: %s", val, source, f.Name, err) } @@ -175,6 +181,10 @@ func (f *Uint64SliceFlag) Apply(set *flag.FlagSet) error { return nil } +func (f *Uint64SliceFlag) WithSeparatorSpec(spec separatorSpec) { + f.separator = spec +} + // Get returns the flag’s value in the given Context. func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 { return ctx.Uint64Slice(f.Name) diff --git a/flag_uint_slice.go b/flag_uint_slice.go index 363aa657f3..d2aed480d9 100644 --- a/flag_uint_slice.go +++ b/flag_uint_slice.go @@ -11,6 +11,7 @@ import ( // UintSlice wraps []int to satisfy flag.Value type UintSlice struct { slice []uint + separator separatorSpec hasBeenSet bool } @@ -54,7 +55,7 @@ func (i *UintSlice) Set(value string) error { return nil } - for _, s := range flagSplitMultiValues(value) { + for _, s := range i.separator.flagSplitMultiValues(value) { tmp, err := strconv.ParseUint(strings.TrimSpace(s), 0, 32) if err != nil { return err @@ -66,6 +67,10 @@ func (i *UintSlice) Set(value string) error { return nil } +func (i *UintSlice) WithSeparatorSpec(spec separatorSpec) { + i.separator = spec +} + // String returns a readable representation of this value (for usage defaults) func (i *UintSlice) String() string { v := i.slice @@ -164,10 +169,11 @@ func (f *UintSliceFlag) Apply(set *flag.FlagSet) error { setValue = f.Value.clone() default: setValue = new(UintSlice) + setValue.WithSeparatorSpec(f.separator) } if val, source, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok && val != "" { - for _, s := range flagSplitMultiValues(val) { + for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as uint slice value from %s for flag %s: %s", val, source, f.Name, err) } @@ -186,6 +192,10 @@ func (f *UintSliceFlag) Apply(set *flag.FlagSet) error { return nil } +func (f *UintSliceFlag) WithSeparatorSpec(spec separatorSpec) { + f.separator = spec +} + // Get returns the flag’s value in the given Context. func (f *UintSliceFlag) Get(ctx *Context) []uint { return ctx.UintSlice(f.Name) diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 016951a3f0..8c29f6ee03 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -25,6 +25,8 @@ type Float64SliceFlag struct { defaultValue *Float64Slice + separator separatorSpec + Action func(*Context, []float64) error } @@ -120,6 +122,8 @@ type Int64SliceFlag struct { defaultValue *Int64Slice + separator separatorSpec + Action func(*Context, []int64) error } @@ -164,6 +168,8 @@ type IntSliceFlag struct { defaultValue *IntSlice + separator separatorSpec + Action func(*Context, []int) error } @@ -259,6 +265,8 @@ type StringSliceFlag struct { defaultValue *StringSlice + separator separatorSpec + TakesFile bool Action func(*Context, []string) error @@ -358,6 +366,8 @@ type Uint64SliceFlag struct { defaultValue *Uint64Slice + separator separatorSpec + Action func(*Context, []uint64) error } @@ -402,6 +412,8 @@ type UintSliceFlag struct { defaultValue *UintSlice + separator separatorSpec + Action func(*Context, []uint) error } From e496537bc528cbfbe96d15e94b4568fe6ce9d726 Mon Sep 17 00:00:00 2001 From: Samuel Stoltenberg Date: Sun, 29 Jan 2023 02:15:34 -0600 Subject: [PATCH 03/13] Fix category --- app.go | 9 +-------- category.go | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app.go b/app.go index 10198f4332..9512392907 100644 --- a/app.go +++ b/app.go @@ -250,14 +250,7 @@ func (a *App) Setup() { } sort.Sort(a.categories.(*commandCategories)) - a.flagCategories = newFlagCategories() - for _, fl := range a.Flags { - if cf, ok := fl.(CategorizableFlag); ok { - if cf.GetCategory() != "" { - a.flagCategories.AddFlag(cf.GetCategory(), cf) - } - } - } + a.flagCategories = newFlagCategoriesFromFlags(a.Flags) if a.Metadata == nil { a.Metadata = make(map[string]interface{}) diff --git a/category.go b/category.go index 7aca0c7684..ccc043c254 100644 --- a/category.go +++ b/category.go @@ -100,10 +100,23 @@ func newFlagCategories() FlagCategories { func newFlagCategoriesFromFlags(fs []Flag) FlagCategories { fc := newFlagCategories() + + var categorized bool for _, fl := range fs { if cf, ok := fl.(CategorizableFlag); ok { - if cf.GetCategory() != "" { - fc.AddFlag(cf.GetCategory(), cf) + if cat := cf.GetCategory(); cat != "" { + fc.AddFlag(cat, cf) + categorized = true + } + } + } + + if categorized == true { + for _, fl := range fs { + if cf, ok := fl.(CategorizableFlag); ok { + if cf.GetCategory() == "" { + fc.AddFlag("", fl) + } } } } From 75a20d461b4b114d8174d08ffa4b9902ae0561cf Mon Sep 17 00:00:00 2001 From: Samuel Stoltenberg Date: Sun, 29 Jan 2023 02:17:33 -0600 Subject: [PATCH 04/13] Fix tests --- app_test.go | 31 ------------------------------- command_test.go | 8 ++++---- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/app_test.go b/app_test.go index 7f2a2144e5..d2cbb677cf 100644 --- a/app_test.go +++ b/app_test.go @@ -2303,37 +2303,6 @@ func TestApp_VisibleCategories(t *testing.T) { expect(t, []CommandCategory{}, app.VisibleCategories()) } -func TestApp_VisibleFlagCategories(t *testing.T) { - app := &App{ - Flags: []Flag{ - &StringFlag{ - Name: "strd", // no category set - }, - &Int64Flag{ - Name: "intd", - Aliases: []string{"altd1", "altd2"}, - Category: "cat1", - }, - }, - } - app.Setup() - vfc := app.VisibleFlagCategories() - if len(vfc) != 1 { - t.Fatalf("unexpected visible flag categories %+v", vfc) - } - if vfc[0].Name() != "cat1" { - t.Errorf("expected category name cat1 got %s", vfc[0].Name()) - } - if len(vfc[0].Flags()) != 1 { - t.Fatalf("expected flag category to have just one flag got %+v", vfc[0].Flags()) - } - - fl := vfc[0].Flags()[0] - if !reflect.DeepEqual(fl.Names(), []string{"intd", "altd1", "altd2"}) { - t.Errorf("unexpected flag %+v", fl.Names()) - } -} - func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := &App{ Action: func(c *Context) error { return nil }, diff --git a/command_test.go b/command_test.go index 478c3251a6..70caee750d 100644 --- a/command_test.go +++ b/command_test.go @@ -470,17 +470,17 @@ func TestCommand_VisibleFlagCategories(t *testing.T) { } vfc := c.VisibleFlagCategories() - if len(vfc) != 1 { + if len(vfc) != 2 { t.Fatalf("unexpected visible flag categories %+v", vfc) } - if vfc[0].Name() != "cat1" { + if vfc[1].Name() != "cat1" { t.Errorf("expected category name cat1 got %s", vfc[0].Name()) } - if len(vfc[0].Flags()) != 1 { + if len(vfc[1].Flags()) != 1 { t.Fatalf("expected flag category to have just one flag got %+v", vfc[0].Flags()) } - fl := vfc[0].Flags()[0] + fl := vfc[1].Flags()[0] if !reflect.DeepEqual(fl.Names(), []string{"intd", "altd1", "altd2"}) { t.Errorf("unexpected flag %+v", fl.Names()) } From 31d75022c166504f1bb18cfeb2d549b60a621642 Mon Sep 17 00:00:00 2001 From: Samuel Stoltenberg Date: Sun, 29 Jan 2023 02:17:37 -0600 Subject: [PATCH 05/13] Add new test --- help_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/help_test.go b/help_test.go index 0a2d9f97fc..8e9396dc8f 100644 --- a/help_test.go +++ b/help_test.go @@ -1520,3 +1520,68 @@ OPTIONS: output.String(), expected) } } + +func TestCategorizedHelp(t *testing.T) { + // Reset HelpPrinter after this test. + defer func(old helpPrinter) { + HelpPrinter = old + }(HelpPrinter) + + output := new(bytes.Buffer) + app := &App{ + Name: "cli.test", + Writer: output, + Action: func(ctx *Context) error { return nil }, + Flags: []Flag{ + &StringFlag{ + Name: "strd", // no category set + }, + &Int64Flag{ + Name: "intd", + Aliases: []string{"altd1", "altd2"}, + Category: "cat1", + }, + }, + } + + c := NewContext(app, nil, nil) + app.Setup() + + HelpPrinter = func(w io.Writer, templ string, data interface{}) { + funcMap := map[string]interface{}{ + "wrapAt": func() int { + return 30 + }, + } + + HelpPrinterCustom(w, templ, data, funcMap) + } + + _ = ShowAppHelp(c) + + expected := `NAME: + cli.test - A new cli + application + +USAGE: + cli.test [global options] command [command options] [arguments...] + +COMMANDS: + help, h Shows a list of + commands or help + for one command + +GLOBAL OPTIONS: + --help, -h show help + --strd value + + cat1 + + --intd value, --altd1 value, --altd2 value (default: 0) + +` + if output.String() != expected { + t.Errorf("Unexpected wrapping, got:\n%s\nexpected:\n%s", + output.String(), expected) + } +} From 94ee95ce2c6f8a8c8918c6b2d1b8d19cfbcb41f6 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 29 Jan 2023 16:55:18 -0500 Subject: [PATCH 06/13] Fix errors in examples --- app_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app_test.go b/app_test.go index c409eb9c95..eb13ec1c1a 100644 --- a/app_test.go +++ b/app_test.go @@ -425,10 +425,10 @@ func ExampleApp_Run_sliceValues() { _ = 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} + // 0-float64Sclice cli.Float64Slice{slice:[]float64{13.3, 14.4, 15.5, 16.6}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} + // 1-int64Sclice cli.Int64Slice{slice:[]int64{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} + // 2-intSclice cli.IntSlice{slice:[]int{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} + // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} // error: } From cb2eafd33f54303e911cd33ea6f44da43d765182 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 29 Jan 2023 16:58:42 -0500 Subject: [PATCH 07/13] Fix docs --- godoc-current.txt | 24 ++++++++++++++++++++++++ testdata/godoc-v2.x.txt | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/godoc-current.txt b/godoc-current.txt index c1eb170573..599332b4f2 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -1038,6 +1038,8 @@ func (f *Float64Slice) String() string func (f *Float64Slice) Value() []float64 Value returns the slice of float64s set by this flag +func (f *Float64Slice) WithSeparatorSpec(spec separatorSpec) + type Float64SliceFlag struct { Name string @@ -1113,6 +1115,8 @@ func (f *Float64SliceFlag) String() string func (f *Float64SliceFlag) TakesValue() bool TakesValue returns true if the flag takes a value, otherwise false +func (f *Float64SliceFlag) WithSeparatorSpec(spec separatorSpec) + type Generic interface { Set(value string) error String() string @@ -1279,6 +1283,8 @@ func (i *Int64Slice) String() string func (i *Int64Slice) Value() []int64 Value returns the slice of ints set by this flag +func (i *Int64Slice) WithSeparatorSpec(spec separatorSpec) + type Int64SliceFlag struct { Name string @@ -1354,6 +1360,8 @@ func (f *Int64SliceFlag) String() string func (f *Int64SliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *Int64SliceFlag) WithSeparatorSpec(spec separatorSpec) + type IntFlag struct { Name string @@ -1449,6 +1457,8 @@ func (i *IntSlice) String() string func (i *IntSlice) Value() []int Value returns the slice of ints set by this flag +func (i *IntSlice) WithSeparatorSpec(spec separatorSpec) + type IntSliceFlag struct { Name string @@ -1524,6 +1534,8 @@ func (f *IntSliceFlag) String() string func (f *IntSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *IntSliceFlag) WithSeparatorSpec(spec separatorSpec) + type InvalidFlagAccessFunc func(*Context, string) InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context. @@ -1792,6 +1804,8 @@ func (s *StringSlice) String() string func (s *StringSlice) Value() []string Value returns the slice of strings set by this flag +func (s *StringSlice) WithSeparatorSpec(spec separatorSpec) + type StringSliceFlag struct { Name string @@ -1869,6 +1883,8 @@ func (f *StringSliceFlag) String() string func (f *StringSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *StringSliceFlag) WithSeparatorSpec(spec separatorSpec) + type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string @@ -2063,6 +2079,8 @@ func (i *Uint64Slice) String() string func (i *Uint64Slice) Value() []uint64 Value returns the slice of ints set by this flag +func (i *Uint64Slice) WithSeparatorSpec(spec separatorSpec) + type Uint64SliceFlag struct { Name string @@ -2129,6 +2147,8 @@ func (f *Uint64SliceFlag) String() string func (f *Uint64SliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *Uint64SliceFlag) WithSeparatorSpec(spec separatorSpec) + type UintFlag struct { Name string @@ -2224,6 +2244,8 @@ func (i *UintSlice) String() string func (i *UintSlice) Value() []uint Value returns the slice of ints set by this flag +func (i *UintSlice) WithSeparatorSpec(spec separatorSpec) + type UintSliceFlag struct { Name string @@ -2290,6 +2312,8 @@ func (f *UintSliceFlag) String() string func (f *UintSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *UintSliceFlag) WithSeparatorSpec(spec separatorSpec) + type VisibleFlag interface { Flag diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index c1eb170573..599332b4f2 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -1038,6 +1038,8 @@ func (f *Float64Slice) String() string func (f *Float64Slice) Value() []float64 Value returns the slice of float64s set by this flag +func (f *Float64Slice) WithSeparatorSpec(spec separatorSpec) + type Float64SliceFlag struct { Name string @@ -1113,6 +1115,8 @@ func (f *Float64SliceFlag) String() string func (f *Float64SliceFlag) TakesValue() bool TakesValue returns true if the flag takes a value, otherwise false +func (f *Float64SliceFlag) WithSeparatorSpec(spec separatorSpec) + type Generic interface { Set(value string) error String() string @@ -1279,6 +1283,8 @@ func (i *Int64Slice) String() string func (i *Int64Slice) Value() []int64 Value returns the slice of ints set by this flag +func (i *Int64Slice) WithSeparatorSpec(spec separatorSpec) + type Int64SliceFlag struct { Name string @@ -1354,6 +1360,8 @@ func (f *Int64SliceFlag) String() string func (f *Int64SliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *Int64SliceFlag) WithSeparatorSpec(spec separatorSpec) + type IntFlag struct { Name string @@ -1449,6 +1457,8 @@ func (i *IntSlice) String() string func (i *IntSlice) Value() []int Value returns the slice of ints set by this flag +func (i *IntSlice) WithSeparatorSpec(spec separatorSpec) + type IntSliceFlag struct { Name string @@ -1524,6 +1534,8 @@ func (f *IntSliceFlag) String() string func (f *IntSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *IntSliceFlag) WithSeparatorSpec(spec separatorSpec) + type InvalidFlagAccessFunc func(*Context, string) InvalidFlagAccessFunc is executed when an invalid flag is accessed from the context. @@ -1792,6 +1804,8 @@ func (s *StringSlice) String() string func (s *StringSlice) Value() []string Value returns the slice of strings set by this flag +func (s *StringSlice) WithSeparatorSpec(spec separatorSpec) + type StringSliceFlag struct { Name string @@ -1869,6 +1883,8 @@ func (f *StringSliceFlag) String() string func (f *StringSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *StringSliceFlag) WithSeparatorSpec(spec separatorSpec) + type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string @@ -2063,6 +2079,8 @@ func (i *Uint64Slice) String() string func (i *Uint64Slice) Value() []uint64 Value returns the slice of ints set by this flag +func (i *Uint64Slice) WithSeparatorSpec(spec separatorSpec) + type Uint64SliceFlag struct { Name string @@ -2129,6 +2147,8 @@ func (f *Uint64SliceFlag) String() string func (f *Uint64SliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *Uint64SliceFlag) WithSeparatorSpec(spec separatorSpec) + type UintFlag struct { Name string @@ -2224,6 +2244,8 @@ func (i *UintSlice) String() string func (i *UintSlice) Value() []uint Value returns the slice of ints set by this flag +func (i *UintSlice) WithSeparatorSpec(spec separatorSpec) + type UintSliceFlag struct { Name string @@ -2290,6 +2312,8 @@ func (f *UintSliceFlag) String() string func (f *UintSliceFlag) TakesValue() bool TakesValue returns true of the flag takes a value, otherwise false +func (f *UintSliceFlag) WithSeparatorSpec(spec separatorSpec) + type VisibleFlag interface { Flag From 12b6187e7f5a91cfbaaa123ff2debdc7da4c76e9 Mon Sep 17 00:00:00 2001 From: Pal Sivertsen Date: Tue, 31 Jan 2023 18:55:05 +0100 Subject: [PATCH 08/13] Fixes urfave/cli#1648 This adds a new field to the `FlagStringSlice` struct for controlling the trimming of space when parsing arguments. The new field is called `NoTrimSpace` and trims space by default. This is to not break existing programs that depends on this behavior. See https://github.com/urfave/cli/pull/1649#issuecomment-1407739883 for further details. --- app_test.go | 2 +- flag-spec.yaml | 2 ++ flag_string_slice.go | 12 ++++++++--- flag_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++- zz_generated.flags.go | 2 ++ 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/app_test.go b/app_test.go index b05e8e62d5..6f2115cb72 100644 --- a/app_test.go +++ b/app_test.go @@ -428,7 +428,7 @@ func ExampleApp_Run_sliceValues() { // 0-float64Sclice cli.Float64Slice{slice:[]float64{13.3, 14.4, 15.5, 16.6}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} // 1-int64Sclice cli.Int64Slice{slice:[]int64{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} // 2-intSclice cli.IntSlice{slice:[]int{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} - // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} + // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true, noTrimSpace:false} // error: } diff --git a/flag-spec.yaml b/flag-spec.yaml index ed4db985d0..92168245f7 100644 --- a/flag-spec.yaml +++ b/flag-spec.yaml @@ -101,6 +101,8 @@ flag_types: type: bool - name: Action type: "func(*Context, []string) error" + - name: NoTrimSpace + type: bool time.Duration: struct_fields: - name: Action diff --git a/flag_string_slice.go b/flag_string_slice.go index 82410dbc8b..b19d9715d1 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -10,9 +10,10 @@ import ( // StringSlice wraps a []string to satisfy flag.Value type StringSlice struct { - slice []string - separator separatorSpec - hasBeenSet bool + slice []string + separator separatorSpec + hasBeenSet bool + noTrimSpace bool } // NewStringSlice creates a *StringSlice with default values @@ -45,6 +46,9 @@ func (s *StringSlice) Set(value string) error { } for _, t := range s.separator.flagSplitMultiValues(value) { + if !s.noTrimSpace { + t = strings.TrimSpace(t) + } s.slice = append(s.slice, t) } @@ -149,6 +153,8 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { setValue.WithSeparatorSpec(f.separator) } + setValue.noTrimSpace = f.NoTrimSpace + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { for _, s := range f.separator.flagSplitMultiValues(val) { if err := setValue.Set(strings.TrimSpace(s)); err != nil { diff --git a/flag_test.go b/flag_test.go index 132a9ede4a..e08a5e2ef9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -774,7 +774,7 @@ func TestStringSliceFlag_MatchStringFlagBehavior(t *testing.T) { app := App{ Flags: []Flag{ &StringFlag{Name: "string"}, - &StringSliceFlag{Name: "slice"}, + &StringSliceFlag{Name: "slice", NoTrimSpace: true}, }, Action: func(ctx *Context) error { f1, f2 := ctx.String("string"), ctx.StringSlice("slice") @@ -797,6 +797,52 @@ func TestStringSliceFlag_MatchStringFlagBehavior(t *testing.T) { } } +func TestStringSliceFlag_TrimSpace(t *testing.T) { + t.Parallel() + + tests := []struct { + in, out string + }{ + {" asd", "asd"}, + {"123 ", "123"}, + {" asd ", "asd"}, + } + for testNum, tt := range tests { + tt := tt + t.Run(fmt.Sprintf("%d", testNum), func(t *testing.T) { + t.Parallel() + + app := App{ + Flags: []Flag{ + &StringSliceFlag{Name: "trim"}, + &StringSliceFlag{Name: "no-trim", NoTrimSpace: true}, + }, + Action: func(ctx *Context) error { + flagTrim, flagNoTrim := ctx.StringSlice("trim"), ctx.StringSlice("no-trim") + if l := len(flagTrim); l != 1 { + t.Fatalf("slice flag 'trim' should result in exactly one value, got %d", l) + } + if l := len(flagNoTrim); l != 1 { + t.Fatalf("slice flag 'no-trim' should result in exactly one value, got %d", l) + } + + if v := flagTrim[0]; v != tt.out { + t.Errorf("Expected trimmed value %q, got %q", tt.out, v) + } + if v := flagNoTrim[0]; v != tt.in { + t.Errorf("Expected no trimmed value%q, got %q", tt.out, v) + } + return nil + }, + } + + if err := app.Run([]string{"", "--trim", tt.in, "--no-trim", tt.in}); err != nil { + t.Errorf("app run error: %s", err) + } + }) + } +} + var intFlagTests = []struct { name string expected string diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 8c29f6ee03..39813cbe58 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -270,6 +270,8 @@ type StringSliceFlag struct { TakesFile bool Action func(*Context, []string) error + + NoTrimSpace bool } // IsSet returns whether or not the flag has been set through env or file From d14954f13b31e8bc62598568f2118b43e980eef5 Mon Sep 17 00:00:00 2001 From: Pal Sivertsen Date: Tue, 31 Jan 2023 19:19:49 +0100 Subject: [PATCH 09/13] Honor NoTrimSpace field for flags from env This commits addresses the following comment: https://github.com/urfave/cli/pull/1649#issuecomment-1410475971 String slice flag values from environment now follows the same rules as parsed values. --- flag_string_slice.go | 5 ++++- flag_test.go | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/flag_string_slice.go b/flag_string_slice.go index b19d9715d1..4395432b74 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -157,7 +157,10 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { for _, s := range f.separator.flagSplitMultiValues(val) { - if err := setValue.Set(strings.TrimSpace(s)); err != nil { + if !f.NoTrimSpace { + s = strings.TrimSpace(s) + } + if err := setValue.Set(s); err != nil { return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err) } } diff --git a/flag_test.go b/flag_test.go index e08a5e2ef9..06813f48c9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -144,6 +144,11 @@ func TestFlagsFromEnv(t *testing.T) { s.hasBeenSet = false return *s } + newSetStringSliceNoTrimSpace := func(defaults ...string) StringSlice { + s := newSetStringSlice(defaults...) + s.noTrimSpace = true + return s + } var flagTests = []struct { input string @@ -198,6 +203,8 @@ func TestFlagsFromEnv(t *testing.T) { {"path", "path", &PathFlag{Name: "path", EnvVars: []string{"PATH"}}, ""}, {"foo,bar", newSetStringSlice("foo", "bar"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""}, + {" space ", newSetStringSliceNoTrimSpace(" space "), &StringSliceFlag{Name: "names", NoTrimSpace: true, EnvVars: []string{"NAMES"}}, ""}, + {" no space ", newSetStringSlice("no space"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""}, {"1", uint(1), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""}, {"08", uint(8), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}, Base: 10}, ""}, From 9998599b1e5a29af0f28e8112817e70dd895f1b6 Mon Sep 17 00:00:00 2001 From: Pal Sivertsen Date: Tue, 31 Jan 2023 19:48:59 +0100 Subject: [PATCH 10/13] Regenerate v2 docs via 'make v2approve' --- godoc-current.txt | 2 ++ testdata/godoc-v2.x.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/godoc-current.txt b/godoc-current.txt index 599332b4f2..d6c7a402f9 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -1827,6 +1827,8 @@ type StringSliceFlag struct { TakesFile bool Action func(*Context, []string) error + + NoTrimSpace bool // Has unexported fields. } StringSliceFlag is a flag with type *StringSlice diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 599332b4f2..d6c7a402f9 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -1827,6 +1827,8 @@ type StringSliceFlag struct { TakesFile bool Action func(*Context, []string) error + + NoTrimSpace bool // Has unexported fields. } StringSliceFlag is a flag with type *StringSlice From 30bb7980afde4a3b297cfed875d4f45f6e996d6e Mon Sep 17 00:00:00 2001 From: Pal Sivertsen Date: Tue, 31 Jan 2023 22:29:22 +0100 Subject: [PATCH 11/13] Rename field for better readability Ref: https://github.com/urfave/cli/pull/1675#issuecomment-1410988910 --- app_test.go | 2 +- flag-spec.yaml | 2 +- flag_string_slice.go | 14 +++++++------- flag_test.go | 10 +++++----- godoc-current.txt | 2 +- testdata/godoc-v2.x.txt | 2 +- zz_generated.flags.go | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app_test.go b/app_test.go index 6f2115cb72..ccd63b9ef8 100644 --- a/app_test.go +++ b/app_test.go @@ -428,7 +428,7 @@ func ExampleApp_Run_sliceValues() { // 0-float64Sclice cli.Float64Slice{slice:[]float64{13.3, 14.4, 15.5, 16.6}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} // 1-int64Sclice cli.Int64Slice{slice:[]int64{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} // 2-intSclice cli.IntSlice{slice:[]int{13, 14, 15, 16}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true} - // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true, noTrimSpace:false} + // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, separator:cli.separatorSpec{sep:"", disabled:false, customized:false}, hasBeenSet:true, keepSpace:false} // error: } diff --git a/flag-spec.yaml b/flag-spec.yaml index 92168245f7..03d82e7011 100644 --- a/flag-spec.yaml +++ b/flag-spec.yaml @@ -101,7 +101,7 @@ flag_types: type: bool - name: Action type: "func(*Context, []string) error" - - name: NoTrimSpace + - name: KeepSpace type: bool time.Duration: struct_fields: diff --git a/flag_string_slice.go b/flag_string_slice.go index 4395432b74..28f4798f55 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -10,10 +10,10 @@ import ( // StringSlice wraps a []string to satisfy flag.Value type StringSlice struct { - slice []string - separator separatorSpec - hasBeenSet bool - noTrimSpace bool + slice []string + separator separatorSpec + hasBeenSet bool + keepSpace bool } // NewStringSlice creates a *StringSlice with default values @@ -46,7 +46,7 @@ func (s *StringSlice) Set(value string) error { } for _, t := range s.separator.flagSplitMultiValues(value) { - if !s.noTrimSpace { + if !s.keepSpace { t = strings.TrimSpace(t) } s.slice = append(s.slice, t) @@ -153,11 +153,11 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { setValue.WithSeparatorSpec(f.separator) } - setValue.noTrimSpace = f.NoTrimSpace + setValue.keepSpace = f.KeepSpace if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { for _, s := range f.separator.flagSplitMultiValues(val) { - if !f.NoTrimSpace { + if !f.KeepSpace { s = strings.TrimSpace(s) } if err := setValue.Set(s); err != nil { diff --git a/flag_test.go b/flag_test.go index 06813f48c9..60370aceb2 100644 --- a/flag_test.go +++ b/flag_test.go @@ -144,9 +144,9 @@ func TestFlagsFromEnv(t *testing.T) { s.hasBeenSet = false return *s } - newSetStringSliceNoTrimSpace := func(defaults ...string) StringSlice { + newSetStringSliceKeepSpace := func(defaults ...string) StringSlice { s := newSetStringSlice(defaults...) - s.noTrimSpace = true + s.keepSpace = true return s } @@ -203,7 +203,7 @@ func TestFlagsFromEnv(t *testing.T) { {"path", "path", &PathFlag{Name: "path", EnvVars: []string{"PATH"}}, ""}, {"foo,bar", newSetStringSlice("foo", "bar"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""}, - {" space ", newSetStringSliceNoTrimSpace(" space "), &StringSliceFlag{Name: "names", NoTrimSpace: true, EnvVars: []string{"NAMES"}}, ""}, + {" space ", newSetStringSliceKeepSpace(" space "), &StringSliceFlag{Name: "names", KeepSpace: true, EnvVars: []string{"NAMES"}}, ""}, {" no space ", newSetStringSlice("no space"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""}, {"1", uint(1), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""}, @@ -781,7 +781,7 @@ func TestStringSliceFlag_MatchStringFlagBehavior(t *testing.T) { app := App{ Flags: []Flag{ &StringFlag{Name: "string"}, - &StringSliceFlag{Name: "slice", NoTrimSpace: true}, + &StringSliceFlag{Name: "slice", KeepSpace: true}, }, Action: func(ctx *Context) error { f1, f2 := ctx.String("string"), ctx.StringSlice("slice") @@ -822,7 +822,7 @@ func TestStringSliceFlag_TrimSpace(t *testing.T) { app := App{ Flags: []Flag{ &StringSliceFlag{Name: "trim"}, - &StringSliceFlag{Name: "no-trim", NoTrimSpace: true}, + &StringSliceFlag{Name: "no-trim", KeepSpace: true}, }, Action: func(ctx *Context) error { flagTrim, flagNoTrim := ctx.StringSlice("trim"), ctx.StringSlice("no-trim") diff --git a/godoc-current.txt b/godoc-current.txt index d6c7a402f9..bd5e1defcd 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -1828,7 +1828,7 @@ type StringSliceFlag struct { Action func(*Context, []string) error - NoTrimSpace bool + KeepSpace bool // Has unexported fields. } StringSliceFlag is a flag with type *StringSlice diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index d6c7a402f9..bd5e1defcd 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -1828,7 +1828,7 @@ type StringSliceFlag struct { Action func(*Context, []string) error - NoTrimSpace bool + KeepSpace bool // Has unexported fields. } StringSliceFlag is a flag with type *StringSlice diff --git a/zz_generated.flags.go b/zz_generated.flags.go index 39813cbe58..73e771451a 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -271,7 +271,7 @@ type StringSliceFlag struct { Action func(*Context, []string) error - NoTrimSpace bool + KeepSpace bool } // IsSet returns whether or not the flag has been set through env or file From 341ee8dc0efeb50f3aa74c90bcc5c80a7d3e96f1 Mon Sep 17 00:00:00 2001 From: MrNaif2018 Date: Wed, 1 Feb 2023 17:39:18 +0300 Subject: [PATCH 12/13] Backport [v2]: Fix some issues in bash autocompletion --- autocomplete/bash_autocomplete | 22 ++++++++++++++++++---- help_test.go | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index f0f624183b..fea6ee33ad 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -2,17 +2,31 @@ : ${PROG:=$(basename ${BASH_SOURCE})} +# Macs have bash3 for which the bash-completion package doesn't include +# _init_completion. This is a minimal version of that function. +_cli_init_completion() { + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} + _cli_bash_autocomplete() { if [[ "${COMP_WORDS[0]}" != "source" ]]; then - local cur opts base + local cur opts base words COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -n "=:" || return + else + _cli_init_completion -n "=:" || return + fi + words=("${words[@]:0:$cword}") if [[ "$cur" == "-"* ]]; then - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) + requestComp="${words[*]} ${cur} --generate-bash-completion" else - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + requestComp="${words[*]} --generate-bash-completion" fi - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + opts=$(eval "${requestComp}" 2>/dev/null) + COMPREPLY=($(compgen -W "${opts}" -- ${cur})) return 0 fi } diff --git a/help_test.go b/help_test.go index 8e9396dc8f..84277f016f 100644 --- a/help_test.go +++ b/help_test.go @@ -1195,6 +1195,28 @@ func TestDefaultCompleteWithFlags(t *testing.T) { argv: []string{"cmd", "--generate-bash-completion"}, expected: "futz\n", }, + { + name: "autocomplete-with-spaces", + c: &Context{App: &App{ + Name: "cmd", + Flags: []Flag{ + &BoolFlag{Name: "happiness"}, + &Int64Flag{Name: "everybody-jump-on"}, + }, + }}, + cmd: &Command{ + Name: "putz", + Subcommands: []*Command{ + {Name: "help"}, + }, + Flags: []Flag{ + &BoolFlag{Name: "excitement"}, + &StringFlag{Name: "hat-shape"}, + }, + }, + argv: []string{"cmd", "--url", "http://localhost:8000", "h", "--generate-bash-completion"}, + expected: "help\n", + }, } { t.Run(tc.name, func(ct *testing.T) { writer := &bytes.Buffer{} From 0a5e5dac4c2dc22a4e9a36c64dc859da287f4270 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Arango Gutierrez Date: Wed, 1 Feb 2023 16:15:23 +0100 Subject: [PATCH 13/13] Deprecation of package ioutil in Go 1.16 ioutil.ReadFile(...) became -> os.ReadFile(...) https://github.com/go-critic/go-critic/issues/1019 --- altsrc/json_command_test.go | 3 +-- altsrc/json_source_context.go | 3 +-- altsrc/toml_command_test.go | 19 +++++++++---------- altsrc/yaml_command_test.go | 19 +++++++++---------- altsrc/yaml_file_loader.go | 8 ++++---- app_test.go | 21 ++++++++++----------- command_test.go | 14 +++++++------- docs/v1/examples/version-flag.md | 3 +-- docs/v2/examples/full-api-example.md | 7 +++---- fish_test.go | 4 ++-- flag.go | 6 +++--- flag_test.go | 13 ++++++------- help_test.go | 13 ++++++------- 13 files changed, 62 insertions(+), 71 deletions(-) diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go index f2d3a8196d..cd583226fb 100644 --- a/altsrc/json_command_test.go +++ b/altsrc/json_command_test.go @@ -2,7 +2,6 @@ package altsrc import ( "flag" - "io/ioutil" "os" "testing" @@ -318,7 +317,7 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *tes } func writeTempFile(t *testing.T, name string, content string) func() { - if err := ioutil.WriteFile(name, []byte(content), 0666); err != nil { + if err := os.WriteFile(name, []byte(content), 0666); err != nil { t.Fatalf("cannot write %q: %v", name, err) } return func() { diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 9beaca135e..aef357b7be 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "strings" "time" @@ -40,7 +39,7 @@ func NewJSONSourceFromFile(f string) (InputSourceContext, error) { // NewJSONSourceFromReader returns an InputSourceContext suitable for // retrieving config variables from an io.Reader that returns JSON data. func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) { - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { return nil, err } diff --git a/altsrc/toml_command_test.go b/altsrc/toml_command_test.go index d6def0efd2..91204d2d1c 100644 --- a/altsrc/toml_command_test.go +++ b/altsrc/toml_command_test.go @@ -2,7 +2,6 @@ package altsrc import ( "flag" - "io/ioutil" "os" "testing" @@ -12,7 +11,7 @@ import ( func TestCommandTomFileTest(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} _ = set.Parse(test) @@ -42,7 +41,7 @@ func TestCommandTomFileTest(t *testing.T) { func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") _ = os.Setenv("THE_TEST", "10") @@ -76,7 +75,7 @@ func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) { func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") _ = os.Setenv("THE_TEST", "10") @@ -110,7 +109,7 @@ func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) { func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml", "--test", "7"} @@ -142,7 +141,7 @@ func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) { func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte(`[top] + _ = os.WriteFile("current.toml", []byte(`[top] test = 15`), 0666) defer os.Remove("current.toml") @@ -175,7 +174,7 @@ func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) { func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} @@ -207,7 +206,7 @@ func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) { func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} @@ -239,7 +238,7 @@ func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) { func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") _ = os.Setenv("THE_TEST", "11") @@ -273,7 +272,7 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) + _ = os.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") _ = os.Setenv("THE_TEST", "11") diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index a297bd2d28..6f528f6368 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -2,7 +2,6 @@ package altsrc import ( "flag" - "io/ioutil" "os" "testing" @@ -12,7 +11,7 @@ import ( func TestCommandYamlFileTest(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) + _ = os.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml"} _ = set.Parse(test) @@ -42,7 +41,7 @@ func TestCommandYamlFileTest(t *testing.T) { func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) + _ = os.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") _ = os.Setenv("THE_TEST", "10") @@ -76,7 +75,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte(`top: + _ = os.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") @@ -111,7 +110,7 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) + _ = os.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml", "--test", "7"} @@ -143,7 +142,7 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) { func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte(`top: + _ = os.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") @@ -176,7 +175,7 @@ func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) + _ = os.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml"} @@ -208,7 +207,7 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) { func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte(`top: + _ = os.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") @@ -241,7 +240,7 @@ func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) + _ = os.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") _ = os.Setenv("THE_TEST", "11") @@ -275,7 +274,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *testing.T) { app := &cli.App{} set := flag.NewFlagSet("test", 0) - _ = ioutil.WriteFile("current.yaml", []byte(`top: + _ = os.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index 62e8965782..391069541f 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -2,7 +2,7 @@ package altsrc import ( "fmt" - "io/ioutil" + "io" "net/http" "net/url" "os" @@ -68,7 +68,7 @@ func loadDataFrom(filePath string) ([]byte, error) { if err != nil { return nil, err } - return ioutil.ReadAll(res.Body) + return io.ReadAll(res.Body) default: return nil, fmt.Errorf("scheme of %s is unsupported", filePath) } @@ -76,13 +76,13 @@ func loadDataFrom(filePath string) ([]byte, error) { if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } - return ioutil.ReadFile(filePath) + return os.ReadFile(filePath) } else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") { // on Windows systems u.Path is always empty, so we need to check the string directly. if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } - return ioutil.ReadFile(filePath) + return os.ReadFile(filePath) } return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) diff --git a/app_test.go b/app_test.go index ccd63b9ef8..5952886622 100644 --- a/app_test.go +++ b/app_test.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "reflect" "strconv" @@ -1278,7 +1277,7 @@ func TestApp_BeforeFunc(t *testing.T) { Flags: []Flag{ &StringFlag{Name: "opt"}, }, - Writer: ioutil.Discard, + Writer: io.Discard, } // run with the Before() func succeeding @@ -1369,7 +1368,7 @@ func TestApp_BeforeAfterFuncShellCompletion(t *testing.T) { Flags: []Flag{ &StringFlag{Name: "opt"}, }, - Writer: ioutil.Discard, + Writer: io.Discard, } // run with the Before() func succeeding @@ -1487,7 +1486,7 @@ func TestAppNoHelpFlag(t *testing.T) { HelpFlag = nil - app := &App{Writer: ioutil.Discard} + app := &App{Writer: io.Discard} err := app.Run([]string{"test", "-h"}) if err != flag.ErrHelp { @@ -1718,7 +1717,7 @@ func TestApp_OrderOfOperations(t *testing.T) { counts.OnUsageError = counts.Total return errors.New("hay OnUsageError") }, - Writer: ioutil.Discard, + Writer: io.Discard, } beforeNoError := func(c *Context) error { @@ -2308,7 +2307,7 @@ func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { Action: func(c *Context) error { return nil }, Before: func(c *Context) error { return fmt.Errorf("before error") }, After: func(c *Context) error { return fmt.Errorf("after error") }, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"foo"}) @@ -2458,7 +2457,7 @@ func (c *customBoolFlag) IsSet() bool { func TestCustomFlagsUnused(t *testing.T) { app := &App{ Flags: []Flag{&customBoolFlag{"custom"}}, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"foo"}) @@ -2470,7 +2469,7 @@ func TestCustomFlagsUnused(t *testing.T) { func TestCustomFlagsUsed(t *testing.T) { app := &App{ Flags: []Flag{&customBoolFlag{"custom"}}, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"foo", "--custom=bar"}) @@ -2481,7 +2480,7 @@ func TestCustomFlagsUsed(t *testing.T) { func TestCustomHelpVersionFlags(t *testing.T) { app := &App{ - Writer: ioutil.Discard, + Writer: io.Discard, } // Be sure to reset the global flags @@ -2573,7 +2572,7 @@ func TestShellCompletionForIncompleteFlags(t *testing.T) { Action: func(ctx *Context) error { return fmt.Errorf("should not get here") }, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"", "--test-completion", "--" + "generate-bash-completion"}) if err != nil { @@ -2636,7 +2635,7 @@ func TestWhenExitSubCommandWithCodeThenAppQuitUnexpectedly(t *testing.T) { func newTestApp() *App { a := NewApp() - a.Writer = ioutil.Discard + a.Writer = io.Discard return a } diff --git a/command_test.go b/command_test.go index 70caee750d..53ca54bd21 100644 --- a/command_test.go +++ b/command_test.go @@ -5,7 +5,7 @@ import ( "errors" "flag" "fmt" - "io/ioutil" + "io" "reflect" "strings" "testing" @@ -27,7 +27,7 @@ func TestCommandFlagParsing(t *testing.T) { } for _, c := range cases { - app := &App{Writer: ioutil.Discard} + app := &App{Writer: io.Discard} set := flag.NewFlagSet("test", 0) _ = set.Parse(c.testArgs) @@ -118,7 +118,7 @@ func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { }, }, }, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"foo", "bar"}) @@ -271,7 +271,7 @@ func TestCommand_OnUsageError_WithSubcommand(t *testing.T) { func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { app := &App{ - ErrWriter: ioutil.Discard, + ErrWriter: io.Discard, Commands: []*Command{ { Name: "bar", @@ -281,7 +281,7 @@ func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { Name: "baz", Usage: "this is for testing", Action: func(c *Context) error { - if c.App.ErrWriter != ioutil.Discard { + if c.App.ErrWriter != io.Discard { return fmt.Errorf("ErrWriter not passed") } @@ -325,7 +325,7 @@ func TestCommandSkipFlagParsing(t *testing.T) { }, }, }, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run(c.testArgs) @@ -406,7 +406,7 @@ func TestCommand_NoVersionFlagOnCommands(t *testing.T) { func TestCommand_CanAddVFlagOnCommands(t *testing.T) { app := &App{ Version: "some version", - Writer: ioutil.Discard, + Writer: io.Discard, Commands: []*Command{ { Name: "bar", diff --git a/docs/v1/examples/version-flag.md b/docs/v1/examples/version-flag.md index dc089a16ae..a3e695a2ef 100644 --- a/docs/v1/examples/version-flag.md +++ b/docs/v1/examples/version-flag.md @@ -94,7 +94,6 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "time" @@ -119,7 +118,7 @@ func init() { cli.OsExiter = func(c int) { fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c) } - cli.ErrWriter = ioutil.Discard + cli.ErrWriter = io.Discard cli.FlagStringer = func(fl cli.Flag) string { return fmt.Sprintf("\t\t%s", fl.GetName()) } diff --git a/docs/v2/examples/full-api-example.md b/docs/v2/examples/full-api-example.md index 289cffeb34..6a33096350 100644 --- a/docs/v2/examples/full-api-example.md +++ b/docs/v2/examples/full-api-example.md @@ -16,10 +16,9 @@ package main import ( "errors" - "flag" + "flag" "fmt" "io" - "io/ioutil" "os" "time" @@ -43,7 +42,7 @@ func init() { cli.OsExiter = func(cCtx int) { fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", cCtx) } - cli.ErrWriter = ioutil.Discard + cli.ErrWriter = io.Discard cli.FlagStringer = func(fl cli.Flag) string { return fmt.Sprintf("\t\t%s", fl.Names()[0]) } @@ -58,7 +57,7 @@ func (w *hexWriter) Write(p []byte) (int, error) { fmt.Printf("\n") return len(p), nil -} +} type genericType struct { s string diff --git a/fish_test.go b/fish_test.go index af1a14c441..d28b0d4202 100644 --- a/fish_test.go +++ b/fish_test.go @@ -2,7 +2,7 @@ package cli import ( "bytes" - "io/ioutil" + "os" "testing" ) @@ -135,7 +135,7 @@ Should be a part of the same code block } func expectFileContent(t *testing.T, file, got string) { - data, err := ioutil.ReadFile(file) + data, err := os.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) diff --git a/flag.go b/flag.go index fc3744ec8e..4d04de3da8 100644 --- a/flag.go +++ b/flag.go @@ -4,7 +4,7 @@ import ( "errors" "flag" "fmt" - "io/ioutil" + "io" "os" "regexp" "runtime" @@ -178,7 +178,7 @@ func flagSet(name string, flags []Flag, spec separatorSpec) (*flag.FlagSet, erro return nil, err } } - set.SetOutput(ioutil.Discard) + set.SetOutput(io.Discard) return set, nil } @@ -384,7 +384,7 @@ func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhe } for _, fileVar := range strings.Split(filePath, ",") { if fileVar != "" { - if data, err := ioutil.ReadFile(fileVar); err == nil { + if data, err := os.ReadFile(fileVar); err == nil { return string(data), fmt.Sprintf("file %q", filePath), true } } diff --git a/flag_test.go b/flag_test.go index 60370aceb2..21ded7545c 100644 --- a/flag_test.go +++ b/flag_test.go @@ -4,7 +4,6 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "reflect" "regexp" @@ -2904,7 +2903,7 @@ func TestParseGenericFromEnvCascade(t *testing.T) { } func TestFlagFromFile(t *testing.T) { - temp, err := ioutil.TempFile("", "urfave_cli_test") + temp, err := os.CreateTemp("", "urfave_cli_test") if err != nil { t.Error(err) return @@ -3071,7 +3070,7 @@ func TestTimestampFlagApplyValue(t *testing.T) { func TestTimestampFlagApply_Fail_Parse_Wrong_Layout(t *testing.T) { fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "randomlayout"} set := flag.NewFlagSet("test", 0) - set.SetOutput(ioutil.Discard) + set.SetOutput(io.Discard) _ = fl.Apply(set) err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"}) @@ -3081,7 +3080,7 @@ func TestTimestampFlagApply_Fail_Parse_Wrong_Layout(t *testing.T) { func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) { fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "Jan 2, 2006 at 3:04pm (MST)"} set := flag.NewFlagSet("test", 0) - set.SetOutput(ioutil.Discard) + set.SetOutput(io.Discard) _ = fl.Apply(set) err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"}) @@ -3175,7 +3174,7 @@ func TestFlagDefaultValue(t *testing.T) { } for i, v := range cases { set := flag.NewFlagSet("test", 0) - set.SetOutput(ioutil.Discard) + set.SetOutput(io.Discard) _ = v.flag.Apply(set) if err := set.Parse(v.toParse); err != nil { t.Error(err) @@ -3353,7 +3352,7 @@ func TestFlagDefaultValueWithEnv(t *testing.T) { os.Setenv(key, val) } set := flag.NewFlagSet("test", 0) - set.SetOutput(ioutil.Discard) + set.SetOutput(io.Discard) if err := v.flag.Apply(set); err != nil { t.Fatal(err) } @@ -3414,7 +3413,7 @@ func TestFlagValue(t *testing.T) { } for i, v := range cases { set := flag.NewFlagSet("test", 0) - set.SetOutput(ioutil.Discard) + set.SetOutput(io.Discard) _ = v.flag.Apply(set) if err := set.Parse(v.toParse); err != nil { t.Error(err) diff --git a/help_test.go b/help_test.go index 8e9396dc8f..f508fe965e 100644 --- a/help_test.go +++ b/help_test.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "runtime" "strings" @@ -986,7 +985,7 @@ App UsageText`, func TestHideHelpCommand(t *testing.T) { app := &App{ HideHelpCommand: true, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"foo", "help"}) @@ -1006,7 +1005,7 @@ func TestHideHelpCommand(t *testing.T) { func TestHideHelpCommand_False(t *testing.T) { app := &App{ HideHelpCommand: false, - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"foo", "help"}) @@ -1024,7 +1023,7 @@ func TestHideHelpCommand_WithHideHelp(t *testing.T) { app := &App{ HideHelp: true, // effective (hides both command and flag) HideHelpCommand: true, // ignored - Writer: ioutil.Discard, + Writer: io.Discard, } err := app.Run([]string{"foo", "help"}) @@ -1053,7 +1052,7 @@ func newContextFromStringSlice(ss []string) *Context { func TestHideHelpCommand_RunAsSubcommand(t *testing.T) { app := &App{ HideHelpCommand: true, - Writer: ioutil.Discard, + Writer: io.Discard, Commands: []*Command{ { Name: "dummy", @@ -1078,7 +1077,7 @@ func TestHideHelpCommand_RunAsSubcommand(t *testing.T) { func TestHideHelpCommand_RunAsSubcommand_False(t *testing.T) { app := &App{ HideHelpCommand: false, - Writer: ioutil.Discard, + Writer: io.Discard, Commands: []*Command{ { Name: "dummy", @@ -1099,7 +1098,7 @@ func TestHideHelpCommand_RunAsSubcommand_False(t *testing.T) { func TestHideHelpCommand_WithSubcommands(t *testing.T) { app := &App{ - Writer: ioutil.Discard, + Writer: io.Discard, Commands: []*Command{ { Name: "dummy",