From ef9430e77eb1e7e05daecdfaa9ec75fa43b79e7e Mon Sep 17 00:00:00 2001 From: Ally Dale Date: Mon, 8 Feb 2021 20:10:28 +0800 Subject: [PATCH 01/45] fix #1238: accept multi-value input on sclice flags --- app_test.go | 34 ++++++++++++++++++++++++++++++ docs/v2/manual.md | 46 +++++++++++++++++++++++++++++++++++++++++ flag.go | 4 ++++ flag_float64_slice.go | 12 ++++++----- flag_int64_slice.go | 14 +++++++------ flag_int_slice.go | 14 +++++++------ flag_string_slice.go | 6 ++++-- flag_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 159 insertions(+), 19 deletions(-) diff --git a/app_test.go b/app_test.go index 7c38f60..0b8b4bf 100644 --- a/app_test.go +++ b/app_test.go @@ -390,6 +390,40 @@ func ExampleApp_Run_zshComplete() { // h:Shows a list of commands or help for one command } +func ExampleApp_Run_sliceValues() { + // set args for examples sake + os.Args = []string{"multi_values", + "--stringSclice", "parsed1,parsed2", "--stringSclice", "parsed3,parsed4", + "--float64Sclice", "13.3,14.4", "--float64Sclice", "15.5,16.6", + "--int64Sclice", "13,14", "--int64Sclice", "15,16", + "--intSclice", "13,14", "--intSclice", "15,16", + } + app := NewApp() + app.Name = "multi_values" + app.Flags = []Flag{ + &StringSliceFlag{Name: "stringSclice"}, + &Float64SliceFlag{Name: "float64Sclice"}, + &Int64SliceFlag{Name: "int64Sclice"}, + &IntSliceFlag{Name: "intSclice"}, + } + app.Action = func(ctx *Context) error { + for i, v := range ctx.FlagNames() { + fmt.Printf("%d-%s %#v\n", i, v, ctx.Value(v)) + } + err := ctx.Err() + fmt.Println("error:", err) + return err + } + + _ = app.Run(os.Args) + // Output: + // 0-float64Sclice cli.Float64Slice{slice:[]float64{13.3, 14.4, 15.5, 16.6}, hasBeenSet:true} + // 1-int64Sclice cli.Int64Slice{slice:[]int64{13, 14, 15, 16}, hasBeenSet:true} + // 2-intSclice cli.IntSlice{slice:[]int{13, 14, 15, 16}, hasBeenSet:true} + // 3-stringSclice cli.StringSlice{slice:[]string{"parsed1", "parsed2", "parsed3", "parsed4"}, hasBeenSet:true} + // error: +} + func TestApp_Run(t *testing.T) { s := "" diff --git a/docs/v2/manual.md b/docs/v2/manual.md index af09010..8f7cbbb 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -14,6 +14,7 @@ cli v2 manual + [Values from the Environment](#values-from-the-environment) + [Values from files](#values-from-files) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) + + [Muti values](#multi-values) + [Required Flags](#required-flags) + [Default Values for help output](#default-values-for-help-output) + [Precedence](#precedence) @@ -660,6 +661,51 @@ func main() { } ``` +#### Multi values + +Slice flags(Float64SliceFlag, Int64SliceFlag, IntSliceFlag, StringSliceFlag) are designed to accept multi values. + +Here is a sample of setting multi values for slice flags: + + +``` +package main + +import ( + "fmt" + "os" + "github.com/urfave/cli/v2" +) + +func main() { + os.Args = []string{"multi_values", + "--stringSclice", "parsed1,parsed2", "--stringSclice", "parsed3,parsed4", + "--float64Sclice", "13.3,14.4", "--float64Sclice", "15.5,16.6", + "--int64Sclice", "13,14", "--int64Sclice", "15,16", + "--intSclice", "13,14", "--intSclice", "15,16", + } + app := cli.NewApp() + app.Name = "multi_values" + app.Flags = []cli.Flag{ + &cli.StringSliceFlag{Name: "stringSclice"}, + &cli.Float64SliceFlag{Name: "float64Sclice"}, + &cli.Int64SliceFlag{Name: "int64Sclice"}, + &cli.IntSliceFlag{Name: "intSclice"}, + } + app.Action = func(ctx *cli.Context) error { + for i, v := range ctx.FlagNames() { + fmt.Printf("%d-%s %#v\n", i, v, ctx.Value(v)) + } + return ctx.Err() + } + + _ = app.Run(os.Args) +} + +``` + #### Required Flags You can make a flag required by setting the `Required` field to `true`. If a user diff --git a/flag.go b/flag.go index aff8d5b..bca41cc 100644 --- a/flag.go +++ b/flag.go @@ -390,3 +390,7 @@ func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool) } return "", false } + +func flagSplitMultiValues(val string) []string { + return strings.Split(val, ",") +} diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 706ee6c..8ee1204 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -33,12 +33,14 @@ func (f *Float64Slice) Set(value string) error { return nil } - tmp, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } + for _, s := range flagSplitMultiValues(value) { + tmp, err := strconv.ParseFloat(strings.TrimSpace(s), 64) + if err != nil { + return err + } - f.slice = append(f.slice, tmp) + f.slice = append(f.slice, tmp) + } return nil } diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 2c9a15a..2edc3c6 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -33,12 +33,14 @@ func (i *Int64Slice) Set(value string) error { return nil } - tmp, err := strconv.ParseInt(value, 0, 64) - if err != nil { - return err - } + for _, s := range flagSplitMultiValues(value) { + tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) + if err != nil { + return err + } - i.slice = append(i.slice, tmp) + i.slice = append(i.slice, tmp) + } return nil } @@ -123,7 +125,7 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { f.Value = &Int64Slice{} - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := f.Value.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int64 slice value for flag %s: %s", val, f.Name, err) } diff --git a/flag_int_slice.go b/flag_int_slice.go index a73ca6b..8861649 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -44,12 +44,14 @@ func (i *IntSlice) Set(value string) error { return nil } - tmp, err := strconv.ParseInt(value, 0, 64) - if err != nil { - return err - } + for _, s := range flagSplitMultiValues(value) { + tmp, err := strconv.ParseInt(strings.TrimSpace(s), 0, 64) + if err != nil { + return err + } - i.slice = append(i.slice, int(tmp)) + i.slice = append(i.slice, int(tmp)) + } return nil } @@ -134,7 +136,7 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok { f.Value = &IntSlice{} - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := f.Value.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as int slice value for flag %s: %s", val, f.Name, err) } diff --git a/flag_string_slice.go b/flag_string_slice.go index 3549703..92ded57 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -32,7 +32,9 @@ func (s *StringSlice) Set(value string) error { return nil } - s.slice = append(s.slice, value) + for _, t := range flagSplitMultiValues(value) { + s.slice = append(s.slice, strings.TrimSpace(t)) + } return nil } @@ -132,7 +134,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { destination = f.Destination } - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := destination.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as string value for flag %s: %s", val, f.Name, err) } diff --git a/flag_test.go b/flag_test.go index b3b0d7c..1afc631 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1973,3 +1973,51 @@ func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) { err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"}) expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\"")) } + +type flagValueTestCase struct { + name string + flag Flag + toParse []string + expect string +} + +func TestFlagValue(t *testing.T) { + cases := []*flagValueTestCase{ + &flagValueTestCase{ + name: "stringSclice", + flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")}, + toParse: []string{"--flag", "parsed,parsed2", "--flag", "parsed3,parsed4"}, + expect: `[parsed parsed2 parsed3 parsed4]`, + }, + &flagValueTestCase{ + name: "float64Sclice", + flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)}, + toParse: []string{"--flag", "13.3,14.4", "--flag", "15.5,16.6"}, + expect: `[]float64{13.3, 14.4, 15.5, 16.6}`, + }, + &flagValueTestCase{ + name: "int64Sclice", + flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)}, + toParse: []string{"--flag", "13,14", "--flag", "15,16"}, + expect: `[]int64{13, 14, 15, 16}`, + }, + &flagValueTestCase{ + name: "intSclice", + flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)}, + toParse: []string{"--flag", "13,14", "--flag", "15,16"}, + expect: `[]int{13, 14, 15, 16}`, + }, + } + for i, v := range cases { + set := flag.NewFlagSet("test", 0) + set.SetOutput(ioutil.Discard) + _ = v.flag.Apply(set) + if err := set.Parse(v.toParse); err != nil { + t.Error(err) + } + f := set.Lookup("flag") + if got := f.Value.String(); got != v.expect { + t.Errorf("TestFlagValue %d-%s\nexpect:%s\ngot:%s", i, v.name, v.expect, got) + } + } +} From 06f6815b8d881483b4ce75de2471fa7d2b724903 Mon Sep 17 00:00:00 2001 From: Ally Dale Date: Mon, 8 Feb 2021 22:41:34 +0800 Subject: [PATCH 02/45] revert docs/v2/manual.md --- docs/v2/manual.md | 46 ------------------------------------------- flag_float64_slice.go | 2 +- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/docs/v2/manual.md b/docs/v2/manual.md index 8f7cbbb..af09010 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -14,7 +14,6 @@ cli v2 manual + [Values from the Environment](#values-from-the-environment) + [Values from files](#values-from-files) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) - + [Muti values](#multi-values) + [Required Flags](#required-flags) + [Default Values for help output](#default-values-for-help-output) + [Precedence](#precedence) @@ -661,51 +660,6 @@ func main() { } ``` -#### Multi values - -Slice flags(Float64SliceFlag, Int64SliceFlag, IntSliceFlag, StringSliceFlag) are designed to accept multi values. - -Here is a sample of setting multi values for slice flags: - - -``` -package main - -import ( - "fmt" - "os" - "github.com/urfave/cli/v2" -) - -func main() { - os.Args = []string{"multi_values", - "--stringSclice", "parsed1,parsed2", "--stringSclice", "parsed3,parsed4", - "--float64Sclice", "13.3,14.4", "--float64Sclice", "15.5,16.6", - "--int64Sclice", "13,14", "--int64Sclice", "15,16", - "--intSclice", "13,14", "--intSclice", "15,16", - } - app := cli.NewApp() - app.Name = "multi_values" - app.Flags = []cli.Flag{ - &cli.StringSliceFlag{Name: "stringSclice"}, - &cli.Float64SliceFlag{Name: "float64Sclice"}, - &cli.Int64SliceFlag{Name: "int64Sclice"}, - &cli.IntSliceFlag{Name: "intSclice"}, - } - app.Action = func(ctx *cli.Context) error { - for i, v := range ctx.FlagNames() { - fmt.Printf("%d-%s %#v\n", i, v, ctx.Value(v)) - } - return ctx.Err() - } - - _ = app.Run(os.Args) -} - -``` - #### Required Flags You can make a flag required by setting the `Required` field to `true`. If a user diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 8ee1204..343c9b0 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -125,7 +125,7 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { if val != "" { f.Value = &Float64Slice{} - for _, s := range strings.Split(val, ",") { + for _, s := range flagSplitMultiValues(val) { if err := f.Value.Set(strings.TrimSpace(s)); err != nil { return fmt.Errorf("could not parse %q as float64 slice value for flag %s: %s", f.Value, f.Name, err) } From e7157a87d96483a94e51dc53f6db7c400a85f74d Mon Sep 17 00:00:00 2001 From: jolheiser Date: Mon, 18 Apr 2022 20:58:38 -0500 Subject: [PATCH 03/45] Exclude hidden sub-command flags from docs Signed-off-by: jolheiser --- docs.go | 4 ++-- docs_test.go | 5 +++++ fish.go | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs.go b/docs.go index 9f82fc6..e6f0265 100644 --- a/docs.go +++ b/docs.go @@ -80,14 +80,14 @@ func prepareCommands(commands []*Command, level int) []string { usageText, ) - flags := prepareArgsWithValues(command.Flags) + flags := prepareArgsWithValues(command.VisibleFlags()) if len(flags) > 0 { prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n")) } coms = append(coms, prepared) - // recursevly iterate subcommands + // recursively iterate subcommands if len(command.Subcommands) > 0 { coms = append( coms, diff --git a/docs_test.go b/docs_test.go index adccbbb..ad714e5 100644 --- a/docs_test.go +++ b/docs_test.go @@ -103,6 +103,11 @@ Should be a part of the same code block Aliases: []string{"s"}, Usage: "some usage text", }, + &StringFlag{ + Name: "sub-command-hidden-flag", + Usage: "some hidden usage text", + Hidden: true, + }, }, Name: "sub-usage", Usage: "standard usage text", diff --git a/fish.go b/fish.go index 588e070..eec3253 100644 --- a/fish.go +++ b/fish.go @@ -95,7 +95,7 @@ func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, pr completions = append(completions, completion.String()) completions = append( completions, - a.prepareFishFlags(command.Flags, command.Names())..., + a.prepareFishFlags(command.VisibleFlags(), command.Names())..., ) // recursevly iterate subcommands From baf8ae98deb3bae30567f621d5167694da2a1402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 15:48:26 +0200 Subject: [PATCH 04/45] BoolFlag.ValueFromContext() as convenient accessor --- flag_bool.go | 5 +++++ flag_test.go | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/flag_bool.go b/flag_bool.go index b8e625a..bdca2b5 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -102,6 +102,11 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *BoolFlag) ValueFromContext(ctx *Context) bool { + return ctx.Bool(f.Name) +} + // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { diff --git a/flag_test.go b/flag_test.go index 44c3500..53cb60b 100644 --- a/flag_test.go +++ b/flag_test.go @@ -51,6 +51,17 @@ func TestBoolFlagApply_SetsAllNames(t *testing.T) { expect(t, v, true) } +func TestBoolFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("trueflag", true, "doc") + set.Bool("falseflag", false, "doc") + ctx := NewContext(nil, set, nil) + tf := &BoolFlag{Name: "trueflag"} + ff := &BoolFlag{Name: "falseflag"} + expect(t, tf.ValueFromContext(ctx), true) + expect(t, ff.ValueFromContext(ctx), false) +} + func TestFlagsFromEnv(t *testing.T) { newSetFloat64Slice := func(defaults ...float64) Float64Slice { s := NewFloat64Slice(defaults...) From 9eae255aac5411488cdebaf71efe73440fcdf929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 15:50:13 +0200 Subject: [PATCH 05/45] DurationFlag.ValueFromContext() as convenient accessor --- flag_duration.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_duration.go b/flag_duration.go index e8ca15e..1de10b1 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -101,6 +101,11 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *DurationFlag) ValueFromContext(ctx *Context) time.Duration { + return ctx.Duration(f.Name) +} + // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { diff --git a/flag_test.go b/flag_test.go index 53cb60b..2f41f42 100644 --- a/flag_test.go +++ b/flag_test.go @@ -826,6 +826,14 @@ func TestDurationFlagApply_SetsAllNames(t *testing.T) { expect(t, v, time.Hour*30) } +func TestDurationFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Duration("myflag", 42*time.Second, "doc") + ctx := NewContext(nil, set, nil) + f := &DurationFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), 42*time.Second) +} + var intSliceFlagTests = []struct { name string aliases []string From 5047beb00185b71caa446147381f89a3178f575b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 15:55:37 +0200 Subject: [PATCH 06/45] Float64Flag.ValueFromContext() as convenient accessor --- flag_float64.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_float64.go b/flag_float64.go index 0ac5b43..f2eb040 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -101,6 +101,11 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *Float64Flag) ValueFromContext(ctx *Context) float64 { + return ctx.Float64(f.Name) +} + // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { diff --git a/flag_test.go b/flag_test.go index 2f41f42..edf9e8e 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1068,6 +1068,14 @@ func TestFloat64FlagApply_SetsAllNames(t *testing.T) { expect(t, v, float64(43.33333)) } +func TestFloat64FlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Float64("myflag", 1.23, "doc") + ctx := NewContext(nil, set, nil) + f := &Float64Flag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), 1.23) +} + var float64SliceFlagTests = []struct { name string aliases []string From 2f92fc644ca176d529d751f5e19675bdebb85d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 16:04:42 +0200 Subject: [PATCH 07/45] Float64SliceFlag.ValueFromContext() as convenient accessor --- flag_float64_slice.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 984f77f..c987688 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -175,6 +175,11 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *Float64SliceFlag) ValueFromContext(ctx *Context) []float64 { + return ctx.Float64Slice(f.Name) +} + // Float64Slice looks up the value of a local Float64SliceFlag, returns // nil if not found func (c *Context) Float64Slice(name string) []float64 { diff --git a/flag_test.go b/flag_test.go index edf9e8e..ce9b5a3 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1117,6 +1117,14 @@ func TestFloat64SliceFlagWithEnvVarHelpOutput(t *testing.T) { } } +func TestFloat64SliceFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Var(NewFloat64Slice(1.23, 4.56), "myflag", "doc") + ctx := NewContext(nil, set, nil) + f := &Float64SliceFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), []float64{1.23, 4.56}) +} + var genericFlagTests = []struct { name string value Generic From 8bd5fb2390f6b8a4f2cba2376302cce8a545d5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 16:10:09 +0200 Subject: [PATCH 08/45] GenericFlag.ValueFromContext() as convenient accessor --- flag_generic.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_generic.go b/flag_generic.go index d159507..483c7d1 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -104,6 +104,11 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *GenericFlag) ValueFromContext(ctx *Context) interface{} { + return ctx.Generic(f.Name) +} + // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { diff --git a/flag_test.go b/flag_test.go index ce9b5a3..7fa6673 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1173,6 +1173,14 @@ func TestGenericFlagApply_SetsAllNames(t *testing.T) { expect(t, err, nil) } +func TestGenericFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Var(&Parser{"abc", "def"}, "myflag", "doc") + ctx := NewContext(nil, set, nil) + f := &GenericFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), &Parser{"abc", "def"}) +} + func TestParseMultiString(t *testing.T) { _ = (&App{ Flags: []Flag{ From bf18c00347867c9f8be0e872a82b4e1baed24d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 16:12:11 +0200 Subject: [PATCH 09/45] IntFlag.ValueFromContext() as convenient accessor --- flag_int.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_int.go b/flag_int.go index 62c0848..642a68b 100644 --- a/flag_int.go +++ b/flag_int.go @@ -102,6 +102,11 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *IntFlag) ValueFromContext(ctx *Context) int { + return ctx.Int(f.Name) +} + // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { diff --git a/flag_test.go b/flag_test.go index 7fa6673..86d281e 100644 --- a/flag_test.go +++ b/flag_test.go @@ -663,6 +663,14 @@ func TestIntFlagApply_SetsAllNames(t *testing.T) { expect(t, v, 5) } +func TestIntFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 42, "doc") + ctx := NewContext(nil, set, nil) + f := &IntFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), 42) +} + var int64FlagTests = []struct { name string expected string From 18b44dfb291d23ff4db2f388e746d88e6f7a310e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 16:45:10 +0200 Subject: [PATCH 10/45] Int64Flag.ValueFromContext() as convenient accessor --- flag_int64.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_int64.go b/flag_int64.go index 2f0be7a..6b40374 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -101,6 +101,11 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *Int64Flag) ValueFromContext(ctx *Context) int64 { + return ctx.Int64(f.Name) +} + // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { diff --git a/flag_test.go b/flag_test.go index 86d281e..0b343e9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -709,6 +709,14 @@ func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { } } +func TestInt64FlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int64("myflag", 42, "doc") + ctx := NewContext(nil, set, nil) + f := &Int64Flag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), int64(42)) +} + var uintFlagTests = []struct { name string expected string From dcc47855b8d7685e47644ee6966df6dc7de8dfa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 16:45:10 +0200 Subject: [PATCH 11/45] Int64SliceFlag.ValueFromContext() as convenient accessor --- flag_int64_slice.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_int64_slice.go b/flag_int64_slice.go index a53b185..29e8dc4 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -174,6 +174,11 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *Int64SliceFlag) ValueFromContext(ctx *Context) []int64 { + return ctx.Int64Slice(f.Name) +} + // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { diff --git a/flag_test.go b/flag_test.go index 0b343e9..d510235 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1035,6 +1035,14 @@ func TestInt64SliceFlag_ReturnNil(t *testing.T) { } } +func TestInt64SliceFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Var(NewInt64Slice(1, 2, 3), "myflag", "doc") + ctx := NewContext(nil, set, nil) + f := &Int64SliceFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), []int64{1, 2, 3}) +} + var float64FlagTests = []struct { name string expected string From 6d7f8590089276c1f11e91f93830f6ada78a035c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 16:49:38 +0200 Subject: [PATCH 12/45] IntSliceFlag.ValueFromContext() as convenient accessor --- flag_int_slice.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_int_slice.go b/flag_int_slice.go index 5f3bd88..4d1741c 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -185,6 +185,11 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *IntSliceFlag) ValueFromContext(ctx *Context) []int { + return ctx.IntSlice(f.Name) +} + // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { diff --git a/flag_test.go b/flag_test.go index d510235..e80231d 100644 --- a/flag_test.go +++ b/flag_test.go @@ -939,6 +939,14 @@ func TestIntSliceFlag_SetFromParentContext(t *testing.T) { } } +func TestIntSliceFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Var(NewIntSlice(1, 2, 3), "myflag", "doc") + ctx := NewContext(nil, set, nil) + f := &IntSliceFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), []int{1, 2, 3}) +} + var int64SliceFlagTests = []struct { name string aliases []string From 660184dd92d1f13f1156dfceb1f22df327dfbd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 16:59:19 +0200 Subject: [PATCH 13/45] PathFlag.ValueFromContext() as convenient accessor --- flag_path.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_path.go b/flag_path.go index 4010e84..40c9009 100644 --- a/flag_path.go +++ b/flag_path.go @@ -96,6 +96,11 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *PathFlag) ValueFromContext(ctx *Context) string { + return ctx.Path(f.Name) +} + // Path looks up the value of a local PathFlag, returns // "" if not found func (c *Context) Path(name string) string { diff --git a/flag_test.go b/flag_test.go index e80231d..1ff56f8 100644 --- a/flag_test.go +++ b/flag_test.go @@ -501,6 +501,14 @@ func TestPathFlagApply_SetsAllNames(t *testing.T) { expect(t, v, "/path/to/file/PATH") } +func TestPathFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.String("myflag", "/my/path", "doc") + ctx := NewContext(nil, set, nil) + f := &PathFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), "/my/path") +} + var envHintFlagTests = []struct { name string env string From ce4d9279c42ef762256900e52a58f8cc3ad48b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 17:02:18 +0200 Subject: [PATCH 14/45] StringFlag.ValueFromContext() as convenient accessor --- flag_string.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_string.go b/flag_string.go index cd3c7df..7464a28 100644 --- a/flag_string.go +++ b/flag_string.go @@ -97,6 +97,11 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *StringFlag) ValueFromContext(ctx *Context) string { + return ctx.String(f.Name) +} + // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { diff --git a/flag_test.go b/flag_test.go index 1ff56f8..aa9fbec 100644 --- a/flag_test.go +++ b/flag_test.go @@ -450,6 +450,14 @@ func TestStringFlagApply_SetsAllNames(t *testing.T) { expect(t, v, "YUUUU") } +func TestStringFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.String("myflag", "foobar", "doc") + ctx := NewContext(nil, set, nil) + f := &StringFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), "foobar") +} + var pathFlagTests = []struct { name string aliases []string From 6b336c478f198630e67e5adeb53fe8da24640165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 17:04:13 +0200 Subject: [PATCH 15/45] StringSliceFlag.ValueFromContext() as convenient accessor --- flag_string_slice.go | 5 +++++ flag_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/flag_string_slice.go b/flag_string_slice.go index 1664247..90e6ebd 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -186,6 +186,11 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *StringSliceFlag) ValueFromContext(ctx *Context) []string { + return ctx.StringSlice(f.Name) +} + // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { diff --git a/flag_test.go b/flag_test.go index aa9fbec..cda34be 100644 --- a/flag_test.go +++ b/flag_test.go @@ -630,6 +630,14 @@ func TestStringSliceFlagApply_DefaultValueWithDestination(t *testing.T) { expect(t, defValue, fl.Destination.Value()) } +func TestStringSliceFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Var(NewStringSlice("a", "b", "c"), "myflag", "doc") + ctx := NewContext(nil, set, nil) + f := &StringSliceFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), []string{"a", "b", "c"}) +} + var intFlagTests = []struct { name string expected string From 889c7b5d7a4deb4f9aaaeed3a113933910253d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 17:07:12 +0200 Subject: [PATCH 16/45] TimestampFlag.ValueFromContext() as convenient accessor --- flag_test.go | 9 +++++++++ flag_timestamp.go | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/flag_test.go b/flag_test.go index cda34be..11b3265 100644 --- a/flag_test.go +++ b/flag_test.go @@ -2264,6 +2264,15 @@ func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) { expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\"")) } +func TestTimestampFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + now := time.Now() + set.Var(NewTimestamp(now), "myflag", "doc") + ctx := NewContext(nil, set, nil) + f := &TimestampFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), &now) +} + type flagDefaultTestCase struct { name string flag Flag diff --git a/flag_timestamp.go b/flag_timestamp.go index ed06418..a3b230e 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -164,6 +164,11 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { return nil } +// ValueFromContext returns the flag’s value in the given Context. +func (f *TimestampFlag) ValueFromContext(ctx *Context) *time.Time { + return ctx.Timestamp(f.Name) +} + // Timestamp gets the timestamp from a flag name func (c *Context) Timestamp(name string) *time.Time { if fs := c.lookupFlagSet(name); fs != nil { From 1f621059d32f9f1b4f66b8c719016eb119f0d816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 17:09:09 +0200 Subject: [PATCH 17/45] UintFlag.ValueFromContext() as convenient accessor --- flag_test.go | 8 ++++++++ flag_uint.go | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/flag_test.go b/flag_test.go index 11b3265..9e6b335 100644 --- a/flag_test.go +++ b/flag_test.go @@ -779,6 +779,14 @@ func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { } } +func TestUintFlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Uint("myflag", 42, "doc") + ctx := NewContext(nil, set, nil) + f := &UintFlag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), uint(42)) +} + var uint64FlagTests = []struct { name string expected string diff --git a/flag_uint.go b/flag_uint.go index dd10e1c..b074c4e 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -101,6 +101,11 @@ func (f *UintFlag) GetEnvVars() []string { return f.EnvVars } +// ValueFromContext returns the flag’s value in the given Context. +func (f *UintFlag) ValueFromContext(ctx *Context) uint { + return ctx.Uint(f.Name) +} + // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { From ca7f26ecb04f8092897b05cdc2ccb03827a15da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Fri, 22 Apr 2022 17:11:41 +0200 Subject: [PATCH 18/45] Uint64Flag.ValueFromContext() as convenient accessor --- flag_test.go | 8 ++++++++ flag_uint64.go | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/flag_test.go b/flag_test.go index 9e6b335..4862c03 100644 --- a/flag_test.go +++ b/flag_test.go @@ -825,6 +825,14 @@ func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { } } +func TestUint64FlagValueFromContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Uint64("myflag", 42, "doc") + ctx := NewContext(nil, set, nil) + f := &Uint64Flag{Name: "myflag"} + expect(t, f.ValueFromContext(ctx), uint64(42)) +} + var durationFlagTests = []struct { name string expected string diff --git a/flag_uint64.go b/flag_uint64.go index 017db53..e79b1a7 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -101,6 +101,11 @@ func (f *Uint64Flag) GetEnvVars() []string { return f.EnvVars } +// ValueFromContext returns the flag’s value in the given Context. +func (f *Uint64Flag) ValueFromContext(ctx *Context) uint64 { + return ctx.Uint64(f.Name) +} + // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { From 156d47e6964c769561edf7946aa168be6d4d4661 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 28 Mar 2021 08:18:46 -0400 Subject: [PATCH 19/45] Fix: (issue#1254) short options handling needs to proceed from last terminated error --- flag_test.go | 31 +++++++++++++++++++++++++++++++ parse.go | 5 +++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/flag_test.go b/flag_test.go index c563d6f..f662fee 100644 --- a/flag_test.go +++ b/flag_test.go @@ -2059,3 +2059,34 @@ func TestTimestampFlagApply_WithDestination(t *testing.T) { expect(t, err, nil) expect(t, *fl.Destination.timestamp, expectedResult) } + +// Test issue #1254 +// StringSlice() with UseShortOptionHandling causes duplicated entries, depending on the ordering of the flags +func TestSliceShortOptionHandle(t *testing.T) { + _ = (&App{ + Commands: []*Command{ + { + Name: "foobar", + UseShortOptionHandling: true, + Action: func(ctx *Context) error { + if ctx.Bool("i") != true { + t.Errorf("bool i not set") + } + if ctx.Bool("t") != true { + t.Errorf("bool i not set") + } + ss := ctx.StringSlice("net") + if !reflect.DeepEqual(ss, []string{"foo"}) { + t.Errorf("Got different slice(%v) than expected", ss) + } + return nil + }, + Flags: []Flag{ + &StringSliceFlag{Name: "net"}, + &BoolFlag{Name: "i"}, + &BoolFlag{Name: "t"}, + }, + }, + }, + }).Run([]string{"run", "foobar", "--net=foo", "-it"}) +} diff --git a/parse.go b/parse.go index 7df1729..57df48b 100644 --- a/parse.go +++ b/parse.go @@ -46,8 +46,9 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple return err } - // swap current argument with the split version - args = append(args[:i], append(shortOpts, args[i+1:]...)...) + // Start processing only from failed argument and not + // from beginning + args = append(shortOpts, args[i+1:]...) argsWereSplit = true break } From dd7065671f833817d58c8f4b9a4c0f46cc4e51c7 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Wed, 28 Apr 2021 20:44:02 -0400 Subject: [PATCH 20/45] Rebase from master --- parse.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parse.go b/parse.go index 57df48b..7df1729 100644 --- a/parse.go +++ b/parse.go @@ -46,9 +46,8 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComple return err } - // Start processing only from failed argument and not - // from beginning - args = append(shortOpts, args[i+1:]...) + // swap current argument with the split version + args = append(args[:i], append(shortOpts, args[i+1:]...)...) argsWereSplit = true break } From 8c5f1fb359a8f7c2995e823b7248a0ba0b46149f Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 23 Apr 2022 19:30:34 -0400 Subject: [PATCH 21/45] Changes from code review --- flag_test.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/flag_test.go b/flag_test.go index f662fee..ecb7847 100644 --- a/flag_test.go +++ b/flag_test.go @@ -2063,17 +2063,19 @@ func TestTimestampFlagApply_WithDestination(t *testing.T) { // Test issue #1254 // StringSlice() with UseShortOptionHandling causes duplicated entries, depending on the ordering of the flags func TestSliceShortOptionHandle(t *testing.T) { - _ = (&App{ + wasCalled := false + err := (&App{ Commands: []*Command{ { Name: "foobar", UseShortOptionHandling: true, Action: func(ctx *Context) error { + wasCalled = true if ctx.Bool("i") != true { - t.Errorf("bool i not set") + t.Error("bool i not set") } if ctx.Bool("t") != true { - t.Errorf("bool i not set") + t.Error("bool i not set") } ss := ctx.StringSlice("net") if !reflect.DeepEqual(ss, []string{"foo"}) { @@ -2089,4 +2091,10 @@ func TestSliceShortOptionHandle(t *testing.T) { }, }, }).Run([]string{"run", "foobar", "--net=foo", "-it"}) + if err != nil { + t.Fatal(err) + } + if !wasCalled { + t.Fatal("Action callback was never called") + } } From 9ce946162f52dff23413c1cb98d817962cf0eb9d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 23 Apr 2022 22:07:16 -0400 Subject: [PATCH 22/45] Unshadow `context` package and consistently name `*cli.Context` vars and method receivers `cCtx` --- altsrc/flag.go | 54 ++++++++++---------- altsrc/json_source_context.go | 6 +-- altsrc/toml_file_loader.go | 8 +-- altsrc/yaml_file_loader.go | 8 +-- app.go | 90 +++++++++++++++++----------------- app_test.go | 8 +-- command.go | 36 +++++++------- command_test.go | 6 +-- context.go | 56 ++++++++++----------- flag_bool.go | 4 +- flag_duration.go | 4 +- flag_float64.go | 4 +- flag_float64_slice.go | 4 +- flag_generic.go | 4 +- flag_int.go | 4 +- flag_int64.go | 4 +- flag_int64_slice.go | 4 +- flag_int_slice.go | 4 +- flag_path.go | 4 +- flag_string.go | 4 +- flag_string_slice.go | 4 +- flag_timestamp.go | 4 +- flag_uint.go | 4 +- flag_uint64.go | 4 +- funcs.go | 4 +- help.go | 92 +++++++++++++++++------------------ 26 files changed, 214 insertions(+), 214 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 31b8a04..4b5588a 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -13,18 +13,18 @@ import ( // allows a value to be set on the existing parsed flags. type FlagInputSourceExtension interface { cli.Flag - ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error } // ApplyInputSourceValues iterates over all provided flags and // executes ApplyInputSourceValue on flags implementing the // FlagInputSourceExtension interface to initialize these flags // to an alternate input source. -func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error { +func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error { for _, f := range flags { inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension) if isType { - err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext) + err := inputSourceExtendedFlag.ApplyInputSourceValue(cCtx, inputSourceContext) if err != nil { return err } @@ -38,34 +38,34 @@ func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSource // input source based on the func provided. If there is no error it will then apply the new input source to any flags // that are supported by the input source func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc { - return func(context *cli.Context) error { + return func(cCtx *cli.Context) error { inputSource, err := createInputSource() if err != nil { return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error()) } - return ApplyInputSourceValues(context, inputSource, flags) + return ApplyInputSourceValues(cCtx, inputSource, flags) } } // InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new // input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is // no error it will then apply the new input source to any flags that are supported by the input source -func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFunc { - return func(context *cli.Context) error { - inputSource, err := createInputSource(context) +func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc { + return func(cCtx *cli.Context) error { + inputSource, err := createInputSource(cCtx) if err != nil { return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error()) } - return ApplyInputSourceValues(context, inputSource, flags) + return ApplyInputSourceValues(cCtx, inputSource, flags) } } // ApplyInputSourceValue applies a generic value to the flagSet if required -func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { return err @@ -82,9 +82,9 @@ func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourc } // ApplyInputSourceValue applies a StringSlice value to the flagSet if required -func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { return err @@ -104,9 +104,9 @@ func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputS } // ApplyInputSourceValue applies a IntSlice value if required -func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { return err @@ -126,9 +126,9 @@ func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour } // ApplyInputSourceValue applies a Bool value to the flagSet if required -func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { + if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { return err @@ -144,9 +144,9 @@ func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo } // ApplyInputSourceValue applies a String value to the flagSet if required -func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.String(f.StringFlag.Name) if err != nil { return err @@ -162,9 +162,9 @@ func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSource } // ApplyInputSourceValue applies a Path value to the flagSet if required -func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.String(f.PathFlag.Name) if err != nil { return err @@ -190,9 +190,9 @@ func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo } // ApplyInputSourceValue applies a int value to the flagSet if required -func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Int(f.IntFlag.Name) if err != nil { return err @@ -208,9 +208,9 @@ func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCon } // ApplyInputSourceValue applies a Duration value to the flagSet if required -func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { return err @@ -226,9 +226,9 @@ func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour } // ApplyInputSourceValue applies a Float64 value to the flagSet if required -func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { +func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { if f.set != nil { - if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { + if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { return err diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 6e7bf11..9307a35 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -16,9 +16,9 @@ import ( // variables from a file containing JSON data with the file name defined // by the given flag. func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) { - return func(context *cli.Context) (InputSourceContext, error) { - if context.IsSet(flag) { - return NewJSONSourceFromFile(context.String(flag)) + return func(cCtx *cli.Context) (InputSourceContext, error) { + if cCtx.IsSet(flag) { + return NewJSONSourceFromFile(cCtx.String(flag)) } return defaultInputSource() diff --git a/altsrc/toml_file_loader.go b/altsrc/toml_file_loader.go index 9b86ee1..dfc9b7b 100644 --- a/altsrc/toml_file_loader.go +++ b/altsrc/toml_file_loader.go @@ -85,10 +85,10 @@ func NewTomlSourceFromFile(file string) (InputSourceContext, error) { } // NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. -func NewTomlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { - return func(context *cli.Context) (InputSourceContext, error) { - if context.IsSet(flagFileName) { - filePath := context.String(flagFileName) +func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) { + return func(cCtx *cli.Context) (InputSourceContext, error) { + if cCtx.IsSet(flagFileName) { + filePath := cCtx.String(flagFileName) return NewTomlSourceFromFile(filePath) } diff --git a/altsrc/yaml_file_loader.go b/altsrc/yaml_file_loader.go index a49df56..4ace1f2 100644 --- a/altsrc/yaml_file_loader.go +++ b/altsrc/yaml_file_loader.go @@ -31,10 +31,10 @@ func NewYamlSourceFromFile(file string) (InputSourceContext, error) { } // NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context. -func NewYamlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { - return func(context *cli.Context) (InputSourceContext, error) { - if context.IsSet(flagFileName) { - filePath := context.String(flagFileName) +func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) { + return func(cCtx *cli.Context) (InputSourceContext, error) { + if cCtx.IsSet(flagFileName) { + filePath := cCtx.String(flagFileName) return NewYamlSourceFromFile(filePath) } diff --git a/app.go b/app.go index 6abcc08..52ef1b0 100644 --- a/app.go +++ b/app.go @@ -245,48 +245,48 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { err = parseIter(set, a, arguments[1:], shellComplete) nerr := normalizeFlags(a.Flags, set) - context := NewContext(a, set, &Context{Context: ctx}) + cCtx := NewContext(a, set, &Context{Context: ctx}) if nerr != nil { _, _ = fmt.Fprintln(a.Writer, nerr) - _ = ShowAppHelp(context) + _ = ShowAppHelp(cCtx) return nerr } - context.shellComplete = shellComplete + cCtx.shellComplete = shellComplete - if checkCompletions(context) { + if checkCompletions(cCtx) { return nil } if err != nil { if a.OnUsageError != nil { - err := a.OnUsageError(context, err, false) - a.handleExitCoder(context, err) + err := a.OnUsageError(cCtx, err, false) + a.handleExitCoder(cCtx, err) return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - _ = ShowAppHelp(context) + _ = ShowAppHelp(cCtx) return err } - if !a.HideHelp && checkHelp(context) { - _ = ShowAppHelp(context) + if !a.HideHelp && checkHelp(cCtx) { + _ = ShowAppHelp(cCtx) return nil } - if !a.HideVersion && checkVersion(context) { - ShowVersion(context) + if !a.HideVersion && checkVersion(cCtx) { + ShowVersion(cCtx) return nil } - cerr := context.checkRequiredFlags(a.Flags) + cerr := cCtx.checkRequiredFlags(a.Flags) if cerr != nil { - _ = ShowAppHelp(context) + _ = ShowAppHelp(cCtx) return cerr } if a.After != nil { defer func() { - if afterErr := a.After(context); afterErr != nil { + if afterErr := a.After(cCtx); afterErr != nil { if err != nil { err = newMultiError(err, afterErr) } else { @@ -297,20 +297,20 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { } if a.Before != nil { - beforeErr := a.Before(context) + beforeErr := a.Before(cCtx) if beforeErr != nil { - a.handleExitCoder(context, beforeErr) + a.handleExitCoder(cCtx, beforeErr) err = beforeErr return err } } - args := context.Args() + args := cCtx.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { - return c.Run(context) + return c.Run(cCtx) } } @@ -319,9 +319,9 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { } // Run default Action - err = a.Action(context) + err = a.Action(cCtx) - a.handleExitCoder(context, err) + a.handleExitCoder(cCtx, err) return err } @@ -359,55 +359,55 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete) nerr := normalizeFlags(a.Flags, set) - context := NewContext(a, set, ctx) + cCtx := NewContext(a, set, ctx) if nerr != nil { _, _ = fmt.Fprintln(a.Writer, nerr) _, _ = fmt.Fprintln(a.Writer) if len(a.Commands) > 0 { - _ = ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(cCtx) } else { - _ = ShowCommandHelp(ctx, context.Args().First()) + _ = ShowCommandHelp(ctx, cCtx.Args().First()) } return nerr } - if checkCompletions(context) { + if checkCompletions(cCtx) { return nil } if err != nil { if a.OnUsageError != nil { - err = a.OnUsageError(context, err, true) - a.handleExitCoder(context, err) + err = a.OnUsageError(cCtx, err, true) + a.handleExitCoder(cCtx, err) return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - _ = ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(cCtx) return err } if len(a.Commands) > 0 { - if checkSubcommandHelp(context) { + if checkSubcommandHelp(cCtx) { return nil } } else { - if checkCommandHelp(ctx, context.Args().First()) { + if checkCommandHelp(ctx, cCtx.Args().First()) { return nil } } - cerr := context.checkRequiredFlags(a.Flags) + cerr := cCtx.checkRequiredFlags(a.Flags) if cerr != nil { - _ = ShowSubcommandHelp(context) + _ = ShowSubcommandHelp(cCtx) return cerr } if a.After != nil { defer func() { - afterErr := a.After(context) + afterErr := a.After(cCtx) if afterErr != nil { - a.handleExitCoder(context, err) + a.handleExitCoder(cCtx, err) if err != nil { err = newMultiError(err, afterErr) } else { @@ -418,27 +418,27 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { } if a.Before != nil { - beforeErr := a.Before(context) + beforeErr := a.Before(cCtx) if beforeErr != nil { - a.handleExitCoder(context, beforeErr) + a.handleExitCoder(cCtx, beforeErr) err = beforeErr return err } } - args := context.Args() + args := cCtx.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { - return c.Run(context) + return c.Run(cCtx) } } // Run default Action - err = a.Action(context) + err = a.Action(cCtx) - a.handleExitCoder(context, err) + a.handleExitCoder(cCtx, err) return err } @@ -498,9 +498,9 @@ func (a *App) appendCommand(c *Command) { } } -func (a *App) handleExitCoder(context *Context, err error) { +func (a *App) handleExitCoder(cCtx *Context, err error) { if a.ExitErrHandler != nil { - a.ExitErrHandler(context, err) + a.ExitErrHandler(cCtx, err) } else { HandleExitCoder(err) } @@ -525,14 +525,14 @@ func (a *Author) String() string { // HandleAction attempts to figure out which Action signature was used. If // it's an ActionFunc or a func with the legacy signature for Action, the func // is run! -func HandleAction(action interface{}, context *Context) (err error) { +func HandleAction(action interface{}, cCtx *Context) (err error) { switch a := action.(type) { case ActionFunc: - return a(context) + return a(cCtx) case func(*Context) error: - return a(context) + return a(cCtx) case func(*Context): // deprecated function signature - a(context) + a(cCtx) return nil } diff --git a/app_test.go b/app_test.go index 76e211d..0829ac6 100644 --- a/app_test.go +++ b/app_test.go @@ -445,14 +445,14 @@ func TestApp_Setup_defaultsWriter(t *testing.T) { } func TestApp_RunAsSubcommandParseFlags(t *testing.T) { - var context *Context + var cCtx *Context a := &App{ Commands: []*Command{ { Name: "foo", Action: func(c *Context) error { - context = c + cCtx = c return nil }, Flags: []Flag{ @@ -468,8 +468,8 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { } _ = a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) - expect(t, context.Args().Get(0), "abcd") - expect(t, context.String("lang"), "spanish") + expect(t, cCtx.Args().Get(0), "abcd") + expect(t, cCtx.String("lang"), "spanish") } func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { diff --git a/command.go b/command.go index 3477686..ba0169b 100644 --- a/command.go +++ b/command.go @@ -105,39 +105,39 @@ func (c *Command) Run(ctx *Context) (err error) { set, err := c.parseFlags(ctx.Args(), ctx.shellComplete) - context := NewContext(ctx.App, set, ctx) - context.Command = c - if checkCommandCompletions(context, c.Name) { + cCtx := NewContext(ctx.App, set, ctx) + cCtx.Command = c + if checkCommandCompletions(cCtx, c.Name) { return nil } if err != nil { if c.OnUsageError != nil { - err = c.OnUsageError(context, err, false) - context.App.handleExitCoder(context, err) + err = c.OnUsageError(cCtx, err, false) + cCtx.App.handleExitCoder(cCtx, err) return err } - _, _ = fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) - _, _ = fmt.Fprintln(context.App.Writer) - _ = ShowCommandHelp(context, c.Name) + _, _ = fmt.Fprintln(cCtx.App.Writer, "Incorrect Usage:", err.Error()) + _, _ = fmt.Fprintln(cCtx.App.Writer) + _ = ShowCommandHelp(cCtx, c.Name) return err } - if checkCommandHelp(context, c.Name) { + if checkCommandHelp(cCtx, c.Name) { return nil } - cerr := context.checkRequiredFlags(c.Flags) + cerr := cCtx.checkRequiredFlags(c.Flags) if cerr != nil { - _ = ShowCommandHelp(context, c.Name) + _ = ShowCommandHelp(cCtx, c.Name) return cerr } if c.After != nil { defer func() { - afterErr := c.After(context) + afterErr := c.After(cCtx) if afterErr != nil { - context.App.handleExitCoder(context, err) + cCtx.App.handleExitCoder(cCtx, err) if err != nil { err = newMultiError(err, afterErr) } else { @@ -148,9 +148,9 @@ func (c *Command) Run(ctx *Context) (err error) { } if c.Before != nil { - err = c.Before(context) + err = c.Before(cCtx) if err != nil { - context.App.handleExitCoder(context, err) + cCtx.App.handleExitCoder(cCtx, err) return err } } @@ -159,11 +159,11 @@ func (c *Command) Run(ctx *Context) (err error) { c.Action = helpSubcommand.Action } - context.Command = c - err = c.Action(context) + cCtx.Command = c + err = c.Action(cCtx) if err != nil { - context.App.handleExitCoder(context, err) + cCtx.App.handleExitCoder(cCtx, err) } return err } diff --git a/command_test.go b/command_test.go index 6add442..9dfd46f 100644 --- a/command_test.go +++ b/command_test.go @@ -30,7 +30,7 @@ func TestCommandFlagParsing(t *testing.T) { set := flag.NewFlagSet("test", 0) _ = set.Parse(c.testArgs) - context := NewContext(app, set, nil) + cCtx := NewContext(app, set, nil) command := Command{ Name: "test-cmd", @@ -41,10 +41,10 @@ func TestCommandFlagParsing(t *testing.T) { SkipFlagParsing: c.skipFlagParsing, } - err := command.Run(context) + err := command.Run(cCtx) expect(t, err, c.expectedErr) - expect(t, context.Args().Slice(), c.testArgs) + expect(t, cCtx.Args().Slice(), c.testArgs) } } diff --git a/context.go b/context.go index da090e8..6b497ed 100644 --- a/context.go +++ b/context.go @@ -40,18 +40,18 @@ func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { } // NumFlags returns the number of flags set -func (c *Context) NumFlags() int { - return c.flagSet.NFlag() +func (cCtx *Context) NumFlags() int { + return cCtx.flagSet.NFlag() } // Set sets a context flag to a value. -func (c *Context) Set(name, value string) error { - return c.flagSet.Set(name, value) +func (cCtx *Context) Set(name, value string) error { + return cCtx.flagSet.Set(name, value) } // IsSet determines if the flag was actually set -func (c *Context) IsSet(name string) bool { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) IsSet(name string) bool { + if fs := cCtx.lookupFlagSet(name); fs != nil { isSet := false fs.Visit(func(f *flag.Flag) { if f.Name == name { @@ -62,7 +62,7 @@ func (c *Context) IsSet(name string) bool { return true } - f := c.lookupFlag(name) + f := cCtx.lookupFlag(name) if f == nil { return false } @@ -74,28 +74,28 @@ func (c *Context) IsSet(name string) bool { } // LocalFlagNames returns a slice of flag names used in this context. -func (c *Context) LocalFlagNames() []string { +func (cCtx *Context) LocalFlagNames() []string { var names []string - c.flagSet.Visit(makeFlagNameVisitor(&names)) + cCtx.flagSet.Visit(makeFlagNameVisitor(&names)) return names } // FlagNames returns a slice of flag names used by the this context and all of // its parent contexts. -func (c *Context) FlagNames() []string { +func (cCtx *Context) FlagNames() []string { var names []string - for _, ctx := range c.Lineage() { - ctx.flagSet.Visit(makeFlagNameVisitor(&names)) + for _, pCtx := range cCtx.Lineage() { + pCtx.flagSet.Visit(makeFlagNameVisitor(&names)) } return names } // Lineage returns *this* context and all of its ancestor contexts in order from // child to parent -func (c *Context) Lineage() []*Context { +func (cCtx *Context) Lineage() []*Context { var lineage []*Context - for cur := c; cur != nil; cur = cur.parentContext { + for cur := cCtx; cur != nil; cur = cur.parentContext { lineage = append(lineage, cur) } @@ -103,26 +103,26 @@ func (c *Context) Lineage() []*Context { } // Value returns the value of the flag corresponding to `name` -func (c *Context) Value(name string) interface{} { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Value(name string) interface{} { + if fs := cCtx.lookupFlagSet(name); fs != nil { return fs.Lookup(name).Value.(flag.Getter).Get() } return nil } // Args returns the command line arguments associated with the context. -func (c *Context) Args() Args { - ret := args(c.flagSet.Args()) +func (cCtx *Context) Args() Args { + ret := args(cCtx.flagSet.Args()) return &ret } // NArg returns the number of the command line arguments. -func (c *Context) NArg() int { - return c.Args().Len() +func (cCtx *Context) NArg() int { + return cCtx.Args().Len() } -func (ctx *Context) lookupFlag(name string) Flag { - for _, c := range ctx.Lineage() { +func (cCtx *Context) lookupFlag(name string) Flag { + for _, c := range cCtx.Lineage() { if c.Command == nil { continue } @@ -136,8 +136,8 @@ func (ctx *Context) lookupFlag(name string) Flag { } } - if ctx.App != nil { - for _, f := range ctx.App.Flags { + if cCtx.App != nil { + for _, f := range cCtx.App.Flags { for _, n := range f.Names() { if n == name { return f @@ -149,8 +149,8 @@ func (ctx *Context) lookupFlag(name string) Flag { return nil } -func (ctx *Context) lookupFlagSet(name string) *flag.FlagSet { - for _, c := range ctx.Lineage() { +func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet { + for _, c := range cCtx.Lineage() { if c.flagSet == nil { continue } @@ -162,7 +162,7 @@ func (ctx *Context) lookupFlagSet(name string) *flag.FlagSet { return nil } -func (context *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { +func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { @@ -174,7 +174,7 @@ func (context *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { flagName = key } - if context.IsSet(strings.TrimSpace(key)) { + if cCtx.IsSet(strings.TrimSpace(key)) { flagPresent = true } } diff --git a/flag_bool.go b/flag_bool.go index b8e625a..6e832f9 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -104,8 +104,8 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error { // Bool looks up the value of a local BoolFlag, returns // false if not found -func (c *Context) Bool(name string) bool { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Bool(name string) bool { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupBool(name, fs) } return false diff --git a/flag_duration.go b/flag_duration.go index e8ca15e..d30bd1d 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -103,8 +103,8 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error { // Duration looks up the value of a local DurationFlag, returns // 0 if not found -func (c *Context) Duration(name string) time.Duration { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Duration(name string) time.Duration { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupDuration(name, fs) } return 0 diff --git a/flag_float64.go b/flag_float64.go index 0ac5b43..f11bef3 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -103,8 +103,8 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error { // Float64 looks up the value of a local Float64Flag, returns // 0 if not found -func (c *Context) Float64(name string) float64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Float64(name string) float64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupFloat64(name, fs) } return 0 diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 984f77f..d573ae5 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -177,8 +177,8 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { // Float64Slice looks up the value of a local Float64SliceFlag, returns // nil if not found -func (c *Context) Float64Slice(name string) []float64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Float64Slice(name string) []float64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupFloat64Slice(name, fs) } return nil diff --git a/flag_generic.go b/flag_generic.go index d159507..e44e7d7 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -106,8 +106,8 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error { // Generic looks up the value of a local GenericFlag, returns // nil if not found -func (c *Context) Generic(name string) interface{} { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Generic(name string) interface{} { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupGeneric(name, fs) } return nil diff --git a/flag_int.go b/flag_int.go index 62c0848..2c4e602 100644 --- a/flag_int.go +++ b/flag_int.go @@ -104,8 +104,8 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error { // Int looks up the value of a local IntFlag, returns // 0 if not found -func (c *Context) Int(name string) int { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Int(name string) int { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt(name, fs) } return 0 diff --git a/flag_int64.go b/flag_int64.go index 2f0be7a..f21e7e4 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -103,8 +103,8 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error { // Int64 looks up the value of a local Int64Flag, returns // 0 if not found -func (c *Context) Int64(name string) int64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Int64(name string) int64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt64(name, fs) } return 0 diff --git a/flag_int64_slice.go b/flag_int64_slice.go index a53b185..16f5665 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -176,8 +176,8 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found -func (c *Context) Int64Slice(name string) []int64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Int64Slice(name string) []int64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupInt64Slice(name, fs) } return nil diff --git a/flag_int_slice.go b/flag_int_slice.go index 5f3bd88..b003229 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -187,8 +187,8 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found -func (c *Context) IntSlice(name string) []int { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) IntSlice(name string) []int { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupIntSlice(name, fs) } return nil diff --git a/flag_path.go b/flag_path.go index 4010e84..de19eea 100644 --- a/flag_path.go +++ b/flag_path.go @@ -98,8 +98,8 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error { // Path looks up the value of a local PathFlag, returns // "" if not found -func (c *Context) Path(name string) string { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Path(name string) string { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupPath(name, fs) } diff --git a/flag_string.go b/flag_string.go index cd3c7df..936ce72 100644 --- a/flag_string.go +++ b/flag_string.go @@ -99,8 +99,8 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error { // String looks up the value of a local StringFlag, returns // "" if not found -func (c *Context) String(name string) string { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) String(name string) string { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupString(name, fs) } return "" diff --git a/flag_string_slice.go b/flag_string_slice.go index 1664247..3879235 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -188,8 +188,8 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found -func (c *Context) StringSlice(name string) []string { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) StringSlice(name string) []string { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupStringSlice(name, fs) } return nil diff --git a/flag_timestamp.go b/flag_timestamp.go index ed06418..dcb9d84 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -165,8 +165,8 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { } // Timestamp gets the timestamp from a flag name -func (c *Context) Timestamp(name string) *time.Time { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Timestamp(name string) *time.Time { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupTimestamp(name, fs) } return nil diff --git a/flag_uint.go b/flag_uint.go index dd10e1c..e9ce9fe 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -103,8 +103,8 @@ func (f *UintFlag) GetEnvVars() []string { // Uint looks up the value of a local UintFlag, returns // 0 if not found -func (c *Context) Uint(name string) uint { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Uint(name string) uint { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUint(name, fs) } return 0 diff --git a/flag_uint64.go b/flag_uint64.go index 017db53..2a2669f 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -103,8 +103,8 @@ func (f *Uint64Flag) GetEnvVars() []string { // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found -func (c *Context) Uint64(name string) uint64 { - if fs := c.lookupFlagSet(name); fs != nil { +func (cCtx *Context) Uint64(name string) uint64 { + if fs := cCtx.lookupFlagSet(name); fs != nil { return lookupUint64(name, fs) } return 0 diff --git a/funcs.go b/funcs.go index 842b4aa..0a9b22c 100644 --- a/funcs.go +++ b/funcs.go @@ -21,11 +21,11 @@ type CommandNotFoundFunc func(*Context, string) // customized usage error messages. This function is able to replace the // original error messages. If this function is not set, the "Incorrect usage" // is displayed and the execution is interrupted. -type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error +type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error // ExitErrHandlerFunc is executed if provided in order to handle exitError values // returned by Actions and Before/After functions. -type ExitErrHandlerFunc func(context *Context, err error) +type ExitErrHandlerFunc func(cCtx *Context, err error) // FlagStringFunc is used by the help generation to display a flag, which is // expected to be a single line. diff --git a/help.go b/help.go index fad990a..2f8156f 100644 --- a/help.go +++ b/help.go @@ -15,13 +15,13 @@ var helpCommand = &Command{ Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", - Action: func(c *Context) error { - args := c.Args() + Action: func(cCtx *Context) error { + args := cCtx.Args() if args.Present() { - return ShowCommandHelp(c, args.First()) + return ShowCommandHelp(cCtx, args.First()) } - _ = ShowAppHelp(c) + _ = ShowAppHelp(cCtx) return nil }, } @@ -31,13 +31,13 @@ var helpSubcommand = &Command{ Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", - Action: func(c *Context) error { - args := c.Args() + Action: func(cCtx *Context) error { + args := cCtx.Args() if args.Present() { - return ShowCommandHelp(c, args.First()) + return ShowCommandHelp(cCtx, args.First()) } - return ShowSubcommandHelp(c) + return ShowSubcommandHelp(cCtx) }, } @@ -71,30 +71,30 @@ func ShowAppHelpAndExit(c *Context, exitCode int) { } // ShowAppHelp is an action that displays the help. -func ShowAppHelp(c *Context) error { - tpl := c.App.CustomAppHelpTemplate +func ShowAppHelp(cCtx *Context) error { + tpl := cCtx.App.CustomAppHelpTemplate if tpl == "" { tpl = AppHelpTemplate } - if c.App.ExtraInfo == nil { - HelpPrinter(c.App.Writer, tpl, c.App) + if cCtx.App.ExtraInfo == nil { + HelpPrinter(cCtx.App.Writer, tpl, cCtx.App) return nil } customAppData := func() map[string]interface{} { return map[string]interface{}{ - "ExtraInfo": c.App.ExtraInfo, + "ExtraInfo": cCtx.App.ExtraInfo, } } - HelpPrinterCustom(c.App.Writer, tpl, c.App, customAppData()) + HelpPrinterCustom(cCtx.App.Writer, tpl, cCtx.App, customAppData()) return nil } // DefaultAppComplete prints the list of subcommands as the default app completion method -func DefaultAppComplete(c *Context) { - DefaultCompleteWithFlags(nil)(c) +func DefaultAppComplete(cCtx *Context) { + DefaultCompleteWithFlags(nil)(cCtx) } func printCommandSuggestions(commands []*Command, writer io.Writer) { @@ -159,30 +159,30 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { } } -func DefaultCompleteWithFlags(cmd *Command) func(c *Context) { - return func(c *Context) { +func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) { + return func(cCtx *Context) { if len(os.Args) > 2 { lastArg := os.Args[len(os.Args)-2] if strings.HasPrefix(lastArg, "-") { if cmd != nil { - printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer) + printFlagSuggestions(lastArg, cmd.Flags, cCtx.App.Writer) return } - printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer) + printFlagSuggestions(lastArg, cCtx.App.Flags, cCtx.App.Writer) return } } if cmd != nil { - printCommandSuggestions(cmd.Subcommands, c.App.Writer) + printCommandSuggestions(cmd.Subcommands, cCtx.App.Writer) return } - printCommandSuggestions(c.App.Commands, c.App.Writer) + printCommandSuggestions(cCtx.App.Commands, cCtx.App.Writer) } } @@ -228,32 +228,32 @@ func ShowSubcommandHelpAndExit(c *Context, exitCode int) { } // ShowSubcommandHelp prints help for the given subcommand -func ShowSubcommandHelp(c *Context) error { - if c == nil { +func ShowSubcommandHelp(cCtx *Context) error { + if cCtx == nil { return nil } - if c.Command != nil { - return ShowCommandHelp(c, c.Command.Name) + if cCtx.Command != nil { + return ShowCommandHelp(cCtx, cCtx.Command.Name) } - return ShowCommandHelp(c, "") + return ShowCommandHelp(cCtx, "") } // ShowVersion prints the version number of the App -func ShowVersion(c *Context) { - VersionPrinter(c) +func ShowVersion(cCtx *Context) { + VersionPrinter(cCtx) } -func printVersion(c *Context) { - _, _ = fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) +func printVersion(cCtx *Context) { + _, _ = fmt.Fprintf(cCtx.App.Writer, "%v version %v\n", cCtx.App.Name, cCtx.App.Version) } // ShowCompletions prints the lists of commands within a given context -func ShowCompletions(c *Context) { - a := c.App +func ShowCompletions(cCtx *Context) { + a := cCtx.App if a != nil && a.BashComplete != nil { - a.BashComplete(c) + a.BashComplete(cCtx) } } @@ -304,20 +304,20 @@ func printHelp(out io.Writer, templ string, data interface{}) { HelpPrinterCustom(out, templ, data, nil) } -func checkVersion(c *Context) bool { +func checkVersion(cCtx *Context) bool { found := false for _, name := range VersionFlag.Names() { - if c.Bool(name) { + if cCtx.Bool(name) { found = true } } return found } -func checkHelp(c *Context) bool { +func checkHelp(cCtx *Context) bool { found := false for _, name := range HelpFlag.Names() { - if c.Bool(name) { + if cCtx.Bool(name) { found = true } } @@ -333,9 +333,9 @@ func checkCommandHelp(c *Context, name string) bool { return false } -func checkSubcommandHelp(c *Context) bool { - if c.Bool("h") || c.Bool("help") { - _ = ShowSubcommandHelp(c) +func checkSubcommandHelp(cCtx *Context) bool { + if cCtx.Bool("h") || cCtx.Bool("help") { + _ = ShowSubcommandHelp(cCtx) return true } @@ -357,20 +357,20 @@ func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { return true, arguments[:pos] } -func checkCompletions(c *Context) bool { - if !c.shellComplete { +func checkCompletions(cCtx *Context) bool { + if !cCtx.shellComplete { return false } - if args := c.Args(); args.Present() { + if args := cCtx.Args(); args.Present() { name := args.First() - if cmd := c.App.Command(name); cmd != nil { + if cmd := cCtx.App.Command(name); cmd != nil { // let the command handle the completion return false } } - ShowCompletions(c) + ShowCompletions(cCtx) return true } From f1ce5c74b0c8646817acc6df26190a7ada66dfce Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 08:10:33 -0400 Subject: [PATCH 23/45] Cleaning up some release-related metadata --- LICENSE | 2 +- README.md | 4 ++++ docs/CHANGELOG.md | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 42a597e..2c84c78 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016 Jeremy Saenz & Contributors +Copyright (c) 2022 urfave/cli maintainers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5b8d2b6..79791ad 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,7 @@ export PATH=$PATH:$GOPATH/bin cli is tested against multiple versions of Go on Linux, and against the latest released version of Go on OS X and Windows. This project uses Github Actions for builds. To see our currently supported go versions and platforms, look at the [./.github/workflows/cli.yml](https://github.com/urfave/cli/blob/main/.github/workflows/cli.yml). + +## License + +See [`LICENSE`](./LICENSE) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e957d94..6dfff88 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,3 +1,9 @@ +> This document is no longer being actively maintained. Please see the +> [releases page](https://github.com/urfave/cli/releases) for all release notes +> and related hypermedia for releases > 1.22.5+, 2.3.0+ and more. + +--- + # Change Log **ATTN**: This project uses [semantic versioning](http://semver.org/). From de589511d80635bab63a0b822fc598aa7b56b961 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 08:14:05 -0400 Subject: [PATCH 24/45] Nit updates to changelog admonition --- docs/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6dfff88..6aec50b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ -> This document is no longer being actively maintained. Please see the +> :warning: This document is no longer being actively maintained. Please see the > [releases page](https://github.com/urfave/cli/releases) for all release notes -> and related hypermedia for releases > 1.22.5+, 2.3.0+ and more. +> and related hypermedia for releases `>= 1.22.5`, `>= 2.3.0`. --- From 11b3a30b4ac662cd3c490f11117460b6fe34e7f6 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 09:17:59 -0400 Subject: [PATCH 25/45] Add a new document specifically for releasing --- docs/RELEASING.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/RELEASING.md diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..57d75a0 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,61 @@ +# Releasing urfave/cli + +Releasing small batches often is [backed by +research](https://itrevolution.com/accelerate-book/) as part of the +virtuous cycles that keep teams and products healthy. + +To that end, the overall goal of the release process is to send +changes out into the world as close to the time the commits were +merged to the `main` branch as possible. In this way, the community +of humans depending on this library are able to make use of the +changes they need **quickly**, which means they should'nt have to +maintain long-lived forks of the project, which means they can get +back to focusing on the work on which they want to focus. This also +means that the @urfave/cli team should be ablo to focus on +delivering a steadily improving product with significantly eased +ability to associate bugs and regressions with specific releases. + +## Process + +- Release versions follow [semantic versioning](https://semver.org/) +- Releases are associated with **signed, annotated git tags**[^1]. +- Release notes are **automatically generated**[^2]. + +In the `main` or `v1` branch, the current version is always +available via: + +```sh +git describe --always --dirty --tags +``` + +**NOTE**: if the version reported contains `-dirty`, this is +indicative of a "dirty" work tree, which is not a great state for +creating a new release tag. Seek help from @urfave/cli teammates. + +For example, given a described version of `v2.4.7-3-g68da1cd` and a +diff of `v2.4.7...` that contains only bug fixes, the next version +should be `v2.4.8`: + +```sh +git tag -a -s -m 'Release 2.4.8' v2.4.8 +git push origin v2.4.8 +``` + +The tag push will trigger a GitHub Actions workflow. The remaining +steps require human intervention through the GitHub web view +although [automated solutions +exist](https://github.com/softprops/action-gh-release) that may be +adopted in the future. + +- Open the [the new release page](https://github.com/urfave/cli/releases/new) +- At the top of the form, click on the `Choose a tag` select control and select `v2.4.8` +- In the `Write` tab below, click the `Auto-generate release notes` button +- At the bottom of the form, click the `Publish release` button +- :white_check_mark: you're done! + +[^1]: This was not always true. There are many **lightweight git + tags** present in the repository history. + +[^2]: This was not always true. The + [`docs/CHANGELOG.md`](./CHANGELOG.md) document used to be + manually maintained. From 7f37d1d13c1652b28b1074aa944d37a8af17da71 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Fri, 2 Apr 2021 20:19:13 -0400 Subject: [PATCH 26/45] Fix(1199). Allow -ve values for int, float & duration --- altsrc/flag.go | 20 +++++++------------- altsrc/flag_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 4b5588a..53382b2 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -197,10 +197,8 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex if err != nil { return err } - if value > 0 { - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) - } + for _, name := range f.Names() { + _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) } } } @@ -215,10 +213,8 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC if err != nil { return err } - if value > 0 { - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) - } + for _, name := range f.Names() { + _ = f.set.Set(name, value.String()) } } } @@ -233,11 +229,9 @@ func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo if err != nil { return err } - if value > 0 { - floatStr := float64ToString(value) - for _, name := range f.Names() { - _ = f.set.Set(name, floatStr) - } + floatStr := float64ToString(value) + for _, name := range f.Names() { + _ = f.set.Set(name, floatStr) } } } diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index 2048331..e20ab02 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -234,6 +234,15 @@ func TestIntApplyInputSourceMethodSet(t *testing.T) { expect(t, 15, c.Int("test")) } +func TestIntApplyInputSourceMethodSetNegativeValue(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), + FlagName: "test", + MapValue: -1, + }) + expect(t, -1, c.Int("test")) +} + func TestIntApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), @@ -264,6 +273,15 @@ func TestDurationApplyInputSourceMethodSet(t *testing.T) { expect(t, 30*time.Second, c.Duration("test")) } +func TestDurationApplyInputSourceMethodSetNegativeValue(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), + FlagName: "test", + MapValue: -30 * time.Second, + }) + expect(t, -30*time.Second, c.Duration("test")) +} + func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), @@ -294,6 +312,15 @@ func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { expect(t, 1.3, c.Float64("test")) } +func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), + FlagName: "test", + MapValue: -1.3, + }) + expect(t, -1.3, c.Float64("test")) +} + func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), From d198aed170178d0d875914288926e300bdf116c1 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 24 Apr 2022 12:06:28 -0400 Subject: [PATCH 27/45] Add test when flag is not set --- altsrc/flag_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index e20ab02..dcb8263 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -321,6 +321,15 @@ func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) { expect(t, -1.3, c.Float64("test")) } +func TestFloat64ApplyInputSourceMethodSetNegativeValueNotSet(t *testing.T) { + c := runTest(t, testApplyInputSource{ + Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test1"}), + FlagName: "test1", + // dont set map value + }) + expect(t, 0.0, c.Float64("test1")) +} + func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), From f6c020fa0907f08f6e44c9b2c60398f7e599e136 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 24 Apr 2022 12:59:12 -0400 Subject: [PATCH 28/45] Spelling fixes thanks to @asmaloney :bow: --- docs/RELEASING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 57d75a0..b10e4e0 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -8,10 +8,10 @@ To that end, the overall goal of the release process is to send changes out into the world as close to the time the commits were merged to the `main` branch as possible. In this way, the community of humans depending on this library are able to make use of the -changes they need **quickly**, which means they should'nt have to +changes they need **quickly**, which means they shouldn't have to maintain long-lived forks of the project, which means they can get back to focusing on the work on which they want to focus. This also -means that the @urfave/cli team should be ablo to focus on +means that the @urfave/cli team should be able to focus on delivering a steadily improving product with significantly eased ability to associate bugs and regressions with specific releases. From 835bd32714ec77ac75a7dd66f616ce94dcfc8ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilo=20Pr=C3=BCtz?= Date: Mon, 25 Apr 2022 07:59:10 +0200 Subject: [PATCH 29/45] =?UTF-8?q?rename=20flags=E2=80=99=20ValueFromContex?= =?UTF-8?q?t()=20to=20Get()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flag_bool.go | 4 ++-- flag_duration.go | 4 ++-- flag_float64.go | 4 ++-- flag_float64_slice.go | 4 ++-- flag_generic.go | 4 ++-- flag_int.go | 4 ++-- flag_int64.go | 4 ++-- flag_int64_slice.go | 4 ++-- flag_int_slice.go | 4 ++-- flag_path.go | 4 ++-- flag_string.go | 4 ++-- flag_string_slice.go | 4 ++-- flag_test.go | 32 ++++++++++++++++---------------- flag_timestamp.go | 4 ++-- flag_uint.go | 4 ++-- flag_uint64.go | 4 ++-- 16 files changed, 46 insertions(+), 46 deletions(-) diff --git a/flag_bool.go b/flag_bool.go index bdca2b5..3caeb08 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -102,8 +102,8 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *BoolFlag) ValueFromContext(ctx *Context) bool { +// Get returns the flag’s value in the given Context. +func (f *BoolFlag) Get(ctx *Context) bool { return ctx.Bool(f.Name) } diff --git a/flag_duration.go b/flag_duration.go index 1de10b1..3452aac 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -101,8 +101,8 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *DurationFlag) ValueFromContext(ctx *Context) time.Duration { +// Get returns the flag’s value in the given Context. +func (f *DurationFlag) Get(ctx *Context) time.Duration { return ctx.Duration(f.Name) } diff --git a/flag_float64.go b/flag_float64.go index f2eb040..fc1b3b9 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -101,8 +101,8 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *Float64Flag) ValueFromContext(ctx *Context) float64 { +// Get returns the flag’s value in the given Context. +func (f *Float64Flag) Get(ctx *Context) float64 { return ctx.Float64(f.Name) } diff --git a/flag_float64_slice.go b/flag_float64_slice.go index c987688..35cc535 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -175,8 +175,8 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *Float64SliceFlag) ValueFromContext(ctx *Context) []float64 { +// 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_generic.go b/flag_generic.go index 483c7d1..74c896e 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -104,8 +104,8 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *GenericFlag) ValueFromContext(ctx *Context) interface{} { +// Get returns the flag’s value in the given Context. +func (f *GenericFlag) Get(ctx *Context) interface{} { return ctx.Generic(f.Name) } diff --git a/flag_int.go b/flag_int.go index 642a68b..6929543 100644 --- a/flag_int.go +++ b/flag_int.go @@ -102,8 +102,8 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *IntFlag) ValueFromContext(ctx *Context) int { +// Get returns the flag’s value in the given Context. +func (f *IntFlag) Get(ctx *Context) int { return ctx.Int(f.Name) } diff --git a/flag_int64.go b/flag_int64.go index 6b40374..55d1214 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -101,8 +101,8 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *Int64Flag) ValueFromContext(ctx *Context) int64 { +// Get returns the flag’s value in the given Context. +func (f *Int64Flag) Get(ctx *Context) int64 { return ctx.Int64(f.Name) } diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 29e8dc4..212b47b 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -174,8 +174,8 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *Int64SliceFlag) ValueFromContext(ctx *Context) []int64 { +// 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 4d1741c..82c045d 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -185,8 +185,8 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *IntSliceFlag) ValueFromContext(ctx *Context) []int { +// 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_path.go b/flag_path.go index 40c9009..82e540b 100644 --- a/flag_path.go +++ b/flag_path.go @@ -96,8 +96,8 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *PathFlag) ValueFromContext(ctx *Context) string { +// Get returns the flag’s value in the given Context. +func (f *PathFlag) Get(ctx *Context) string { return ctx.Path(f.Name) } diff --git a/flag_string.go b/flag_string.go index 7464a28..258ca92 100644 --- a/flag_string.go +++ b/flag_string.go @@ -97,8 +97,8 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *StringFlag) ValueFromContext(ctx *Context) string { +// Get returns the flag’s value in the given Context. +func (f *StringFlag) Get(ctx *Context) string { return ctx.String(f.Name) } diff --git a/flag_string_slice.go b/flag_string_slice.go index 90e6ebd..79257b6 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -186,8 +186,8 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *StringSliceFlag) ValueFromContext(ctx *Context) []string { +// 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 4862c03..07f6f89 100644 --- a/flag_test.go +++ b/flag_test.go @@ -58,8 +58,8 @@ func TestBoolFlagValueFromContext(t *testing.T) { ctx := NewContext(nil, set, nil) tf := &BoolFlag{Name: "trueflag"} ff := &BoolFlag{Name: "falseflag"} - expect(t, tf.ValueFromContext(ctx), true) - expect(t, ff.ValueFromContext(ctx), false) + expect(t, tf.Get(ctx), true) + expect(t, ff.Get(ctx), false) } func TestFlagsFromEnv(t *testing.T) { @@ -455,7 +455,7 @@ func TestStringFlagValueFromContext(t *testing.T) { set.String("myflag", "foobar", "doc") ctx := NewContext(nil, set, nil) f := &StringFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), "foobar") + expect(t, f.Get(ctx), "foobar") } var pathFlagTests = []struct { @@ -514,7 +514,7 @@ func TestPathFlagValueFromContext(t *testing.T) { set.String("myflag", "/my/path", "doc") ctx := NewContext(nil, set, nil) f := &PathFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), "/my/path") + expect(t, f.Get(ctx), "/my/path") } var envHintFlagTests = []struct { @@ -635,7 +635,7 @@ func TestStringSliceFlagValueFromContext(t *testing.T) { set.Var(NewStringSlice("a", "b", "c"), "myflag", "doc") ctx := NewContext(nil, set, nil) f := &StringSliceFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), []string{"a", "b", "c"}) + expect(t, f.Get(ctx), []string{"a", "b", "c"}) } var intFlagTests = []struct { @@ -692,7 +692,7 @@ func TestIntFlagValueFromContext(t *testing.T) { set.Int("myflag", 42, "doc") ctx := NewContext(nil, set, nil) f := &IntFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), 42) + expect(t, f.Get(ctx), 42) } var int64FlagTests = []struct { @@ -738,7 +738,7 @@ func TestInt64FlagValueFromContext(t *testing.T) { set.Int64("myflag", 42, "doc") ctx := NewContext(nil, set, nil) f := &Int64Flag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), int64(42)) + expect(t, f.Get(ctx), int64(42)) } var uintFlagTests = []struct { @@ -784,7 +784,7 @@ func TestUintFlagValueFromContext(t *testing.T) { set.Uint("myflag", 42, "doc") ctx := NewContext(nil, set, nil) f := &UintFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), uint(42)) + expect(t, f.Get(ctx), uint(42)) } var uint64FlagTests = []struct { @@ -830,7 +830,7 @@ func TestUint64FlagValueFromContext(t *testing.T) { set.Uint64("myflag", 42, "doc") ctx := NewContext(nil, set, nil) f := &Uint64Flag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), uint64(42)) + expect(t, f.Get(ctx), uint64(42)) } var durationFlagTests = []struct { @@ -887,7 +887,7 @@ func TestDurationFlagValueFromContext(t *testing.T) { set.Duration("myflag", 42*time.Second, "doc") ctx := NewContext(nil, set, nil) f := &DurationFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), 42*time.Second) + expect(t, f.Get(ctx), 42*time.Second) } var intSliceFlagTests = []struct { @@ -984,7 +984,7 @@ func TestIntSliceFlagValueFromContext(t *testing.T) { set.Var(NewIntSlice(1, 2, 3), "myflag", "doc") ctx := NewContext(nil, set, nil) f := &IntSliceFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), []int{1, 2, 3}) + expect(t, f.Get(ctx), []int{1, 2, 3}) } var int64SliceFlagTests = []struct { @@ -1088,7 +1088,7 @@ func TestInt64SliceFlagValueFromContext(t *testing.T) { set.Var(NewInt64Slice(1, 2, 3), "myflag", "doc") ctx := NewContext(nil, set, nil) f := &Int64SliceFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), []int64{1, 2, 3}) + expect(t, f.Get(ctx), []int64{1, 2, 3}) } var float64FlagTests = []struct { @@ -1145,7 +1145,7 @@ func TestFloat64FlagValueFromContext(t *testing.T) { set.Float64("myflag", 1.23, "doc") ctx := NewContext(nil, set, nil) f := &Float64Flag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), 1.23) + expect(t, f.Get(ctx), 1.23) } var float64SliceFlagTests = []struct { @@ -1194,7 +1194,7 @@ func TestFloat64SliceFlagValueFromContext(t *testing.T) { set.Var(NewFloat64Slice(1.23, 4.56), "myflag", "doc") ctx := NewContext(nil, set, nil) f := &Float64SliceFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), []float64{1.23, 4.56}) + expect(t, f.Get(ctx), []float64{1.23, 4.56}) } var genericFlagTests = []struct { @@ -1250,7 +1250,7 @@ func TestGenericFlagValueFromContext(t *testing.T) { set.Var(&Parser{"abc", "def"}, "myflag", "doc") ctx := NewContext(nil, set, nil) f := &GenericFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), &Parser{"abc", "def"}) + expect(t, f.Get(ctx), &Parser{"abc", "def"}) } func TestParseMultiString(t *testing.T) { @@ -2286,7 +2286,7 @@ func TestTimestampFlagValueFromContext(t *testing.T) { set.Var(NewTimestamp(now), "myflag", "doc") ctx := NewContext(nil, set, nil) f := &TimestampFlag{Name: "myflag"} - expect(t, f.ValueFromContext(ctx), &now) + expect(t, f.Get(ctx), &now) } type flagDefaultTestCase struct { diff --git a/flag_timestamp.go b/flag_timestamp.go index a3b230e..872f855 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -164,8 +164,8 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { return nil } -// ValueFromContext returns the flag’s value in the given Context. -func (f *TimestampFlag) ValueFromContext(ctx *Context) *time.Time { +// Get returns the flag’s value in the given Context. +func (f *TimestampFlag) Get(ctx *Context) *time.Time { return ctx.Timestamp(f.Name) } diff --git a/flag_uint.go b/flag_uint.go index b074c4e..358e22f 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -101,8 +101,8 @@ func (f *UintFlag) GetEnvVars() []string { return f.EnvVars } -// ValueFromContext returns the flag’s value in the given Context. -func (f *UintFlag) ValueFromContext(ctx *Context) uint { +// Get returns the flag’s value in the given Context. +func (f *UintFlag) Get(ctx *Context) uint { return ctx.Uint(f.Name) } diff --git a/flag_uint64.go b/flag_uint64.go index e79b1a7..844dfc9 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -101,8 +101,8 @@ func (f *Uint64Flag) GetEnvVars() []string { return f.EnvVars } -// ValueFromContext returns the flag’s value in the given Context. -func (f *Uint64Flag) ValueFromContext(ctx *Context) uint64 { +// Get returns the flag’s value in the given Context. +func (f *Uint64Flag) Get(ctx *Context) uint64 { return ctx.Uint64(f.Name) } From aabfea87c811de4c6bf6dcd01f64309a8864fead Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 25 Apr 2022 10:04:40 -0700 Subject: [PATCH 30/45] Move some test helpers from docs_test to fish_test This is in preparation to make docs optional. Signed-off-by: Kir Kolyshkin --- docs_test.go | 123 --------------------------------------------------- fish_test.go | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 123 deletions(-) diff --git a/docs_test.go b/docs_test.go index adccbbb..b925cb9 100644 --- a/docs_test.go +++ b/docs_test.go @@ -1,133 +1,10 @@ package cli import ( - "bytes" "errors" - "io/ioutil" "testing" ) -func testApp() *App { - app := newTestApp() - app.Name = "greet" - app.Flags = []Flag{ - &StringFlag{ - Name: "socket", - Aliases: []string{"s"}, - Usage: "some 'usage' text", - Value: "value", - TakesFile: true, - }, - &StringFlag{Name: "flag", Aliases: []string{"fl", "f"}}, - &BoolFlag{ - Name: "another-flag", - Aliases: []string{"b"}, - Usage: "another usage text", - }, - &BoolFlag{ - Name: "hidden-flag", - Hidden: true, - }, - } - app.Commands = []*Command{{ - Aliases: []string{"c"}, - Flags: []Flag{ - &StringFlag{ - Name: "flag", - Aliases: []string{"fl", "f"}, - TakesFile: true, - }, - &BoolFlag{ - Name: "another-flag", - Aliases: []string{"b"}, - Usage: "another usage text", - }, - }, - Name: "config", - Usage: "another usage test", - Subcommands: []*Command{{ - Aliases: []string{"s", "ss"}, - Flags: []Flag{ - &StringFlag{Name: "sub-flag", Aliases: []string{"sub-fl", "s"}}, - &BoolFlag{ - Name: "sub-command-flag", - Aliases: []string{"s"}, - Usage: "some usage text", - }, - }, - Name: "sub-config", - Usage: "another usage test", - }}, - }, { - Aliases: []string{"i", "in"}, - Name: "info", - Usage: "retrieve generic information", - }, { - Name: "some-command", - }, { - Name: "hidden-command", - Hidden: true, - }, { - Aliases: []string{"u"}, - Flags: []Flag{ - &StringFlag{ - Name: "flag", - Aliases: []string{"fl", "f"}, - TakesFile: true, - }, - &BoolFlag{ - Name: "another-flag", - Aliases: []string{"b"}, - Usage: "another usage text", - }, - }, - Name: "usage", - Usage: "standard usage text", - UsageText: ` -Usage for the usage text -- formatted: Based on the specified ConfigMap and summon secrets.yml -- list: Inspect the environment for a specific process running on a Pod -- for_effect: Compare 'namespace' environment with 'local' - -` + "```" + ` -func() { ... } -` + "```" + ` - -Should be a part of the same code block -`, - Subcommands: []*Command{{ - Aliases: []string{"su"}, - Flags: []Flag{ - &BoolFlag{ - Name: "sub-command-flag", - Aliases: []string{"s"}, - Usage: "some usage text", - }, - }, - Name: "sub-usage", - Usage: "standard usage text", - UsageText: "Single line of UsageText", - }}, - }} - app.UsageText = "app [first_arg] [second_arg]" - app.Description = `Description of the application.` - app.Usage = "Some app" - app.Authors = []*Author{ - {Name: "Harrison", Email: "harrison@lolwut.com"}, - {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, - } - return app -} - -func expectFileContent(t *testing.T, file, got string) { - data, err := ioutil.ReadFile(file) - // Ignore windows line endings - // TODO: Replace with bytes.ReplaceAll when support for Go 1.11 is dropped - data = bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1) - expect(t, err, nil) - expect(t, got, string(data)) -} - func TestToMarkdownFull(t *testing.T) { // Given app := testApp() diff --git a/fish_test.go b/fish_test.go index 4ca8c47..af1a14c 100644 --- a/fish_test.go +++ b/fish_test.go @@ -1,6 +1,8 @@ package cli import ( + "bytes" + "io/ioutil" "testing" ) @@ -19,3 +21,124 @@ func TestFishCompletion(t *testing.T) { expect(t, err, nil) expectFileContent(t, "testdata/expected-fish-full.fish", res) } + +func testApp() *App { + app := newTestApp() + app.Name = "greet" + app.Flags = []Flag{ + &StringFlag{ + Name: "socket", + Aliases: []string{"s"}, + Usage: "some 'usage' text", + Value: "value", + TakesFile: true, + }, + &StringFlag{Name: "flag", Aliases: []string{"fl", "f"}}, + &BoolFlag{ + Name: "another-flag", + Aliases: []string{"b"}, + Usage: "another usage text", + }, + &BoolFlag{ + Name: "hidden-flag", + Hidden: true, + }, + } + app.Commands = []*Command{{ + Aliases: []string{"c"}, + Flags: []Flag{ + &StringFlag{ + Name: "flag", + Aliases: []string{"fl", "f"}, + TakesFile: true, + }, + &BoolFlag{ + Name: "another-flag", + Aliases: []string{"b"}, + Usage: "another usage text", + }, + }, + Name: "config", + Usage: "another usage test", + Subcommands: []*Command{{ + Aliases: []string{"s", "ss"}, + Flags: []Flag{ + &StringFlag{Name: "sub-flag", Aliases: []string{"sub-fl", "s"}}, + &BoolFlag{ + Name: "sub-command-flag", + Aliases: []string{"s"}, + Usage: "some usage text", + }, + }, + Name: "sub-config", + Usage: "another usage test", + }}, + }, { + Aliases: []string{"i", "in"}, + Name: "info", + Usage: "retrieve generic information", + }, { + Name: "some-command", + }, { + Name: "hidden-command", + Hidden: true, + }, { + Aliases: []string{"u"}, + Flags: []Flag{ + &StringFlag{ + Name: "flag", + Aliases: []string{"fl", "f"}, + TakesFile: true, + }, + &BoolFlag{ + Name: "another-flag", + Aliases: []string{"b"}, + Usage: "another usage text", + }, + }, + Name: "usage", + Usage: "standard usage text", + UsageText: ` +Usage for the usage text +- formatted: Based on the specified ConfigMap and summon secrets.yml +- list: Inspect the environment for a specific process running on a Pod +- for_effect: Compare 'namespace' environment with 'local' + +` + "```" + ` +func() { ... } +` + "```" + ` + +Should be a part of the same code block +`, + Subcommands: []*Command{{ + Aliases: []string{"su"}, + Flags: []Flag{ + &BoolFlag{ + Name: "sub-command-flag", + Aliases: []string{"s"}, + Usage: "some usage text", + }, + }, + Name: "sub-usage", + Usage: "standard usage text", + UsageText: "Single line of UsageText", + }}, + }} + app.UsageText = "app [first_arg] [second_arg]" + app.Description = `Description of the application.` + app.Usage = "Some app" + app.Authors = []*Author{ + {Name: "Harrison", Email: "harrison@lolwut.com"}, + {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, + } + return app +} + +func expectFileContent(t *testing.T, file, got string) { + data, err := ioutil.ReadFile(file) + // Ignore windows line endings + // TODO: Replace with bytes.ReplaceAll when support for Go 1.11 is dropped + data = bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1) + expect(t, err, nil) + expect(t, got, string(data)) +} From 4c7b46cb24c9af9b5d93bcc2316b2a877008b723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20B=C4=85k?= Date: Mon, 25 Apr 2022 19:11:11 +0200 Subject: [PATCH 31/45] fix: clean code --- flag.go | 22 +--------------------- flag_test.go | 18 +++++++++--------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/flag.go b/flag.go index 1907622..b85bafd 100644 --- a/flag.go +++ b/flag.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "io/ioutil" - "reflect" "regexp" "runtime" "strconv" @@ -244,7 +243,7 @@ func prefixedNames(names []string, placeholder string) string { func withEnvHint(envVars []string, str string) string { envText := "" - if envVars != nil && len(envVars) > 0 { + if len(envVars) > 0 { prefix := "$" suffix := "" sep := ", $" @@ -273,17 +272,6 @@ func flagNames(name string, aliases []string) []string { return ret } -func flagStringSliceField(f Flag, name string) []string { - fv := flagValue(f) - field := fv.FieldByName(name) - - if field.IsValid() { - return field.Interface().([]string) - } - - return []string{} -} - func withFileHint(filePath, str string) string { fileText := "" if filePath != "" { @@ -292,14 +280,6 @@ func withFileHint(filePath, str string) string { return str + fileText } -func flagValue(f Flag) reflect.Value { - fv := reflect.ValueOf(f) - for fv.Kind() == reflect.Ptr { - fv = reflect.Indirect(fv) - } - return fv -} - func formatDefault(format string) string { return " (default: " + format + ")" } diff --git a/flag_test.go b/flag_test.go index b9cbfc3..843f646 100644 --- a/flag_test.go +++ b/flag_test.go @@ -400,7 +400,7 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { } } -var prefixStringFlagTests = []struct { +var _ = []struct { name string aliases []string usage string @@ -490,7 +490,7 @@ func TestPathFlagApply_SetsAllNames(t *testing.T) { expect(t, v, "/path/to/file/PATH") } -var envHintFlagTests = []struct { +var _ = []struct { name string env string hinter FlagEnvHintFunc @@ -2174,43 +2174,43 @@ type flagDefaultTestCase struct { func TestFlagDefaultValue(t *testing.T) { cases := []*flagDefaultTestCase{ - &flagDefaultTestCase{ + { name: "stringSclice", flag: &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")}, toParse: []string{"--flag", "parsed"}, expect: `--flag value (default: "default1", "default2") (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "float64Sclice", flag: &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)}, toParse: []string{"--flag", "13.3"}, expect: `--flag value (default: 1.1, 2.2) (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "int64Sclice", flag: &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)}, toParse: []string{"--flag", "13"}, expect: `--flag value (default: 1, 2) (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "intSclice", flag: &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)}, toParse: []string{"--flag", "13"}, expect: `--flag value (default: 1, 2) (accepts multiple inputs)`, }, - &flagDefaultTestCase{ + { name: "string", flag: &StringFlag{Name: "flag", Value: "default"}, toParse: []string{"--flag", "parsed"}, expect: `--flag value (default: "default")`, }, - &flagDefaultTestCase{ + { name: "bool", flag: &BoolFlag{Name: "flag", Value: true}, toParse: []string{"--flag", "false"}, expect: `--flag (default: true)`, }, - &flagDefaultTestCase{ + { name: "uint64", flag: &Uint64Flag{Name: "flag", Value: 1}, toParse: []string{"--flag", "13"}, From 49e43beba32f7fb2007f557bb77129bf8a0e4300 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 25 Apr 2022 10:16:31 -0700 Subject: [PATCH 32/45] Add urfave_cli_no_docs build tag This removes the resulting binary dependency on cpuguy83/md2man and russross/blackfriday, which saves more than 400 KB (more than 300 KB once stripped) from the resulting binary. Document this in README. --- README.md | 10 ++++++++++ docs.go | 3 +++ docs_test.go | 3 +++ 3 files changed, 16 insertions(+) diff --git a/README.md b/README.md index 5b8d2b6..134e72e 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,16 @@ import ( ... ``` +### Build tags + +You can use the following build tags: + +#### `urfave_cli_no_docs` + +When set, this removes `ToMarkdown` and `ToMan` methods, so your application +won't be able to call those. This reduces the resulting binary size by about +300-400 KB (measured using Go 1.18.1 on Linux/amd64), due to less dependencies. + ### GOPATH Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can diff --git a/docs.go b/docs.go index 9f82fc6..ace6a6b 100644 --- a/docs.go +++ b/docs.go @@ -1,3 +1,6 @@ +//go:build !urfave_cli_no_docs +// +build !urfave_cli_no_docs + package cli import ( diff --git a/docs_test.go b/docs_test.go index b925cb9..12d5d3c 100644 --- a/docs_test.go +++ b/docs_test.go @@ -1,3 +1,6 @@ +//go:build !urfave_cli_no_docs +// +build !urfave_cli_no_docs + package cli import ( From b8cb475418d24c63ac616018ac4bdb5f61353f1f Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Mon, 25 Apr 2022 10:58:31 -0700 Subject: [PATCH 33/45] ci: test newly added tag We run test with the tag set (to make sure nothing is broken), and also the check-binary-size target (for informational purposes only). Signed-off-by: Kir Kolyshkin --- .github/workflows/cli.yml | 6 ++++++ internal/build/build.go | 20 +++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 0ab3baf..c60ed4c 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -37,12 +37,18 @@ jobs: - name: vet run: go run internal/build/build.go vet + - name: test with tags + run: go run internal/build/build.go -tags urfave_cli_no_docs test + - name: test run: go run internal/build/build.go test - name: check-binary-size run: go run internal/build/build.go check-binary-size + - name: check-binary-size with tags (informational only) + run: go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size || true + - name: Upload coverage to Codecov if: success() && matrix.go == '1.18.x' && matrix.os == 'ubuntu-latest' uses: codecov/codecov-action@v2 diff --git a/internal/build/build.go b/internal/build/build.go index 4cbaa68..dec0dfb 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -46,6 +46,12 @@ func main() { Action: checkBinarySizeActionFunc, }, } + app.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "tags", + Usage: "set build tags", + }, + } err := app.Run(os.Args) if err != nil { @@ -68,6 +74,8 @@ func VetActionFunc(_ *cli.Context) error { } func TestActionFunc(c *cli.Context) error { + tags := c.String("tags") + for _, pkg := range packages { var packageName string @@ -79,7 +87,7 @@ func TestActionFunc(c *cli.Context) error { coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg) - err := runCmd("go", "test", "-v", coverProfile, packageName) + err := runCmd("go", "test", "-tags", tags, "-v", coverProfile, packageName) if err != nil { return err } @@ -201,14 +209,16 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { mbStringFormatter = "%.1fMB" ) + tags := c.String("tags") + // get cli example size - cliSize, err := getSize(cliSourceFilePath, cliBuiltFilePath) + cliSize, err := getSize(cliSourceFilePath, cliBuiltFilePath, tags) if err != nil { return err } // get hello world size - helloSize, err := getSize(helloSourceFilePath, helloBuiltFilePath) + helloSize, err := getSize(helloSourceFilePath, helloBuiltFilePath, tags) if err != nil { return err } @@ -270,9 +280,9 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { return nil } -func getSize(sourcePath string, builtPath string) (size int64, err error) { +func getSize(sourcePath string, builtPath string, tags string) (size int64, err error) { // build example binary - err = runCmd("go", "build", "-o", builtPath, "-ldflags", "-s -w", sourcePath) + err = runCmd("go", "build", "-tags", tags, "-o", builtPath, "-ldflags", "-s -w", sourcePath) if err != nil { fmt.Println("issue getting size for example binary") return 0, err From e3aa8d3246f4e5d1e26e78c98c6cb5ebb6c3b668 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 20:16:01 -0400 Subject: [PATCH 34/45] Add failing test for #1373 --- altsrc/yaml_file_loader_test.go | 87 +++++++++++++++++++++++++++++++++ testdata/empty.yml | 1 + 2 files changed, 88 insertions(+) create mode 100644 altsrc/yaml_file_loader_test.go create mode 100644 testdata/empty.yml diff --git a/altsrc/yaml_file_loader_test.go b/altsrc/yaml_file_loader_test.go new file mode 100644 index 0000000..814586b --- /dev/null +++ b/altsrc/yaml_file_loader_test.go @@ -0,0 +1,87 @@ +package altsrc_test + +import ( + "fmt" + "log" + "os" + "time" + + "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/altsrc" +) + +func ExampleApp_Run_yamlFileLoaderDuration() { + execServe := func(c *cli.Context) error { + keepaliveInterval := c.Duration("keepalive-interval") + fmt.Printf("keepalive %s\n", keepaliveInterval) + return nil + } + + fileExists := func(filename string) bool { + stat, _ := os.Stat(filename) + return stat != nil + } + + // initConfigFileInputSource is like altsrc.InitInputSourceWithContext and altsrc.NewYamlSourceFromFlagFunc, but checks + // if the config flag is exists and only loads it if it does. If the flag is set and the file exists, it fails. + initConfigFileInputSource := func(configFlag string, flags []cli.Flag) cli.BeforeFunc { + return func(context *cli.Context) error { + configFile := context.String(configFlag) + if context.IsSet(configFlag) && !fileExists(configFile) { + return fmt.Errorf("config file %s does not exist", configFile) + } else if !context.IsSet(configFlag) && !fileExists(configFile) { + return nil + } + inputSource, err := altsrc.NewYamlSourceFromFile(configFile) + if err != nil { + return err + } + return altsrc.ApplyInputSourceValues(context, inputSource, flags) + } + } + + flagsServe := []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + EnvVars: []string{"CONFIG_FILE"}, + Value: "../testdata/empty.yml", + DefaultText: "../testdata/empty.yml", + Usage: "config file", + }, + altsrc.NewDurationFlag( + &cli.DurationFlag{ + Name: "keepalive-interval", + Aliases: []string{"k"}, + EnvVars: []string{"KEEPALIVE_INTERVAL"}, + Value: 45 * time.Second, + Usage: "interval of keepalive messages", + }, + ), + } + + cmdServe := &cli.Command{ + Name: "serve", + Usage: "Run the server", + UsageText: "serve [OPTIONS..]", + Action: execServe, + Flags: flagsServe, + Before: initConfigFileInputSource("config", flagsServe), + } + + c := &cli.App{ + Name: "cmd", + HideVersion: true, + UseShortOptionHandling: true, + Commands: []*cli.Command{ + cmdServe, + }, + } + + if err := c.Run([]string{"cmd", "serve", "--config", "../testdata/empty.yml"}); err != nil { + log.Fatal(err) + } + + // Output: + // keepalive 45s +} diff --git a/testdata/empty.yml b/testdata/empty.yml new file mode 100644 index 0000000..ab2fc5d --- /dev/null +++ b/testdata/empty.yml @@ -0,0 +1 @@ +# empty file \ No newline at end of file From efe04493fc5a4013f26ad0b1c37df4d6a769f7a8 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 20:43:43 -0400 Subject: [PATCH 35/45] Only apply altsrc input source values when set Closes #1373 --- altsrc/flag.go | 182 +++++++++++++++------------------ altsrc/input_source_context.go | 1 + altsrc/json_source_context.go | 5 + altsrc/map_input_source.go | 13 +++ 4 files changed, 101 insertions(+), 100 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index 53382b2..a90a52a 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -64,16 +64,14 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *c // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.Generic(f.GenericFlag.Name) - if err != nil { - return err - } - if value != nil { - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.GenericFlag.Name) { + value, err := isc.Generic(f.GenericFlag.Name) + if err != nil { + return err + } + if value != nil { + for _, name := range f.Names() { + _ = f.set.Set(name, value.String()) } } } @@ -83,19 +81,17 @@ func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.StringSlice(f.StringSliceFlag.Name) - if err != nil { - return err - } - if value != nil { - var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...)) - for _, name := range f.Names() { - underlyingFlag := f.set.Lookup(name) - if underlyingFlag != nil { - underlyingFlag.Value = &sliceValue - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.StringSliceFlag.Name) { + value, err := isc.StringSlice(f.StringSliceFlag.Name) + if err != nil { + return err + } + if value != nil { + var sliceValue cli.StringSlice = *(cli.NewStringSlice(value...)) + for _, name := range f.Names() { + underlyingFlag := f.set.Lookup(name) + if underlyingFlag != nil { + underlyingFlag.Value = &sliceValue } } } @@ -105,19 +101,17 @@ func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSour // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.IntSlice(f.IntSliceFlag.Name) - if err != nil { - return err - } - if value != nil { - var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...)) - for _, name := range f.Names() { - underlyingFlag := f.set.Lookup(name) - if underlyingFlag != nil { - underlyingFlag.Value = &sliceValue - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.IntSliceFlag.Name) { + value, err := isc.IntSlice(f.IntSliceFlag.Name) + if err != nil { + return err + } + if value != nil { + var sliceValue cli.IntSlice = *(cli.NewIntSlice(value...)) + for _, name := range f.Names() { + underlyingFlag := f.set.Lookup(name) + if underlyingFlag != nil { + underlyingFlag.Value = &sliceValue } } } @@ -127,16 +121,14 @@ func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) { - value, err := isc.Bool(f.BoolFlag.Name) - if err != nil { - return err - } - if value { - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatBool(value)) - } + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.BoolFlag.Name) { + value, err := isc.Bool(f.BoolFlag.Name) + if err != nil { + return err + } + if value { + for _, name := range f.Names() { + _ = f.set.Set(name, strconv.FormatBool(value)) } } } @@ -145,16 +137,14 @@ func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.String(f.StringFlag.Name) - if err != nil { - return err - } - if value != "" { - for _, name := range f.Names() { - _ = f.set.Set(name, value) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.StringFlag.Name) { + value, err := isc.String(f.StringFlag.Name) + if err != nil { + return err + } + if value != "" { + for _, name := range f.Names() { + _ = f.set.Set(name, value) } } } @@ -163,26 +153,24 @@ func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCon // ApplyInputSourceValue applies a Path value to the flagSet if required func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.String(f.PathFlag.Name) - if err != nil { - return err - } - if value != "" { - for _, name := range f.Names() { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.PathFlag.Name) { + value, err := isc.String(f.PathFlag.Name) + if err != nil { + return err + } + if value != "" { + for _, name := range f.Names() { - if !filepath.IsAbs(value) && isc.Source() != "" { - basePathAbs, err := filepath.Abs(isc.Source()) - if err != nil { - return err - } - - value = filepath.Join(filepath.Dir(basePathAbs), value) + if !filepath.IsAbs(value) && isc.Source() != "" { + basePathAbs, err := filepath.Abs(isc.Source()) + if err != nil { + return err } - _ = f.set.Set(name, value) + value = filepath.Join(filepath.Dir(basePathAbs), value) } + + _ = f.set.Set(name, value) } } } @@ -191,15 +179,13 @@ func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.Int(f.IntFlag.Name) - if err != nil { - return err - } - for _, name := range f.Names() { - _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.IntFlag.Name) { + value, err := isc.Int(f.IntFlag.Name) + if err != nil { + return err + } + for _, name := range f.Names() { + _ = f.set.Set(name, strconv.FormatInt(int64(value), 10)) } } return nil @@ -207,15 +193,13 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.Duration(f.DurationFlag.Name) - if err != nil { - return err - } - for _, name := range f.Names() { - _ = f.set.Set(name, value.String()) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.DurationFlag.Name) { + value, err := isc.Duration(f.DurationFlag.Name) + if err != nil { + return err + } + for _, name := range f.Names() { + _ = f.set.Set(name, value.String()) } } return nil @@ -223,16 +207,14 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil { - if !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) { - value, err := isc.Float64(f.Float64Flag.Name) - if err != nil { - return err - } - floatStr := float64ToString(value) - for _, name := range f.Names() { - _ = f.set.Set(name, floatStr) - } + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.Float64Flag.Name) { + value, err := isc.Float64(f.Float64Flag.Name) + if err != nil { + return err + } + floatStr := float64ToString(value) + for _, name := range f.Names() { + _ = f.set.Set(name, floatStr) } } return nil diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index a639d8b..467360c 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -14,6 +14,7 @@ import ( type InputSourceContext interface { Source() string + IsSet(name string) bool Int(name string) (int, error) Duration(name string) (time.Duration, error) Float64(name string) (float64, error) diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index 9307a35..d5f8cc1 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -184,6 +184,11 @@ func (x *jsonSource) Bool(name string) (bool, error) { return v, nil } +func (x *jsonSource) IsSet(name string) bool { + _, err := x.getValue(name) + return err == nil +} + func (x *jsonSource) getValue(key string) (interface{}, error) { return jsonGetValue(key, x.deserialized) } diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index 117461f..be94906 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -244,6 +244,19 @@ func (fsm *MapInputSource) Bool(name string) (bool, error) { return false, nil } +// IsSet returns the truth of the key's existence in the map +func (fsm *MapInputSource) IsSet(name string) bool { + if _, exists := fsm.valueMap[name]; exists { + return exists + } + + if _, exists := nestedVal(name, fsm.valueMap); exists { + return exists + } + + return false +} + func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error { valueType := reflect.TypeOf(value) valueTypeName := "" From fe1468cc86b1cab489b2038a7f83090198b72af8 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 20:49:31 -0400 Subject: [PATCH 36/45] Make the altsrc input source context `isSet` method private so that it doesn't extend the public surface area, instead focusing on being a bugfix _only_. --- altsrc/flag.go | 18 +++++++++--------- altsrc/input_source_context.go | 3 ++- altsrc/json_source_context.go | 2 +- altsrc/map_input_source.go | 3 +-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/altsrc/flag.go b/altsrc/flag.go index a90a52a..db95949 100644 --- a/altsrc/flag.go +++ b/altsrc/flag.go @@ -64,7 +64,7 @@ func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *c // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.GenericFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.GenericFlag.Name) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { return err @@ -81,7 +81,7 @@ func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCo // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.StringSliceFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.StringSliceFlag.Name) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { return err @@ -101,7 +101,7 @@ func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSour // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.IntSliceFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.IntSliceFlag.Name) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { return err @@ -121,7 +121,7 @@ func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.IsSet(f.BoolFlag.Name) { + if f.set != nil && !cCtx.IsSet(f.Name) && !isEnvVarSet(f.EnvVars) && isc.isSet(f.BoolFlag.Name) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { return err @@ -137,7 +137,7 @@ func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.StringFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.StringFlag.Name) { value, err := isc.String(f.StringFlag.Name) if err != nil { return err @@ -153,7 +153,7 @@ func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceCon // ApplyInputSourceValue applies a Path value to the flagSet if required func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.PathFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.PathFlag.Name) { value, err := isc.String(f.PathFlag.Name) if err != nil { return err @@ -179,7 +179,7 @@ func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceConte // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.IntFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.IntFlag.Name) { value, err := isc.Int(f.IntFlag.Name) if err != nil { return err @@ -193,7 +193,7 @@ func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContex // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.DurationFlag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.DurationFlag.Name) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { return err @@ -207,7 +207,7 @@ func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceC // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error { - if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.IsSet(f.Float64Flag.Name) { + if f.set != nil && !(cCtx.IsSet(f.Name) || isEnvVarSet(f.EnvVars)) && isc.isSet(f.Float64Flag.Name) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { return err diff --git a/altsrc/input_source_context.go b/altsrc/input_source_context.go index 467360c..d743253 100644 --- a/altsrc/input_source_context.go +++ b/altsrc/input_source_context.go @@ -14,7 +14,6 @@ import ( type InputSourceContext interface { Source() string - IsSet(name string) bool Int(name string) (int, error) Duration(name string) (time.Duration, error) Float64(name string) (float64, error) @@ -23,4 +22,6 @@ type InputSourceContext interface { IntSlice(name string) ([]int, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) + + isSet(name string) bool } diff --git a/altsrc/json_source_context.go b/altsrc/json_source_context.go index d5f8cc1..168b6da 100644 --- a/altsrc/json_source_context.go +++ b/altsrc/json_source_context.go @@ -184,7 +184,7 @@ func (x *jsonSource) Bool(name string) (bool, error) { return v, nil } -func (x *jsonSource) IsSet(name string) bool { +func (x *jsonSource) isSet(name string) bool { _, err := x.getValue(name) return err == nil } diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index be94906..cc11fc5 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -244,8 +244,7 @@ func (fsm *MapInputSource) Bool(name string) (bool, error) { return false, nil } -// IsSet returns the truth of the key's existence in the map -func (fsm *MapInputSource) IsSet(name string) bool { +func (fsm *MapInputSource) isSet(name string) bool { if _, exists := fsm.valueMap[name]; exists { return exists } From ac641ffda54727c6af36b80c0941f05886aed217 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 22:15:39 -0400 Subject: [PATCH 37/45] Add more test coverage around unset input source applying --- altsrc/flag_test.go | 259 ++++++++++++++++++++++++++++++++--------- altsrc/helpers_test.go | 7 +- 2 files changed, 206 insertions(+), 60 deletions(-) diff --git a/altsrc/flag_test.go b/altsrc/flag_test.go index dcb8263..e3725f7 100644 --- a/altsrc/flag_test.go +++ b/altsrc/flag_test.go @@ -26,29 +26,48 @@ type testApplyInputSource struct { MapValue interface{} } +type racyInputSource struct { + *MapInputSource +} + +func (ris *racyInputSource) isSet(name string) bool { + if _, ok := ris.MapInputSource.valueMap[name]; ok { + ris.MapInputSource.valueMap[name] = bogus{0} + } + return true +} + func TestGenericApplyInputSourceValue(t *testing.T) { v := &Parser{"abc", "def"} - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: v, - }) + } + c := runTest(t, tis) expect(t, v, c.Generic("test")) + + c = runRacyTest(t, tis) + refute(t, v, c.Generic("test")) } func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { p := &Parser{"abc", "def"} - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewGenericFlag(&cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: &Parser{"efg", "hig"}, ContextValueString: p.String(), - }) + } + c := runTest(t, tis) expect(t, p, c.Generic("test")) + + c = runRacyTest(t, tis) + refute(t, p, c.Generic("test")) } func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewGenericFlag(&cli.GenericFlag{ Name: "test", Value: &Parser{}, @@ -58,17 +77,25 @@ func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { MapValue: &Parser{"efg", "hij"}, EnvVarName: "TEST", EnvVarValue: "abc,def", - }) + } + c := runTest(t, tis) expect(t, &Parser{"abc", "def"}, c.Generic("test")) + + c = runRacyTest(t, tis) + refute(t, &Parser{"abc", "def"}, c.Generic("test")) } func TestStringSliceApplyInputSourceValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, - }) + } + c := runTest(t, tis) expect(t, c.StringSlice("test"), []string{"hello", "world"}) + + c = runRacyTest(t, tis) + refute(t, c.StringSlice("test"), []string{"hello", "world"}) } func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { @@ -82,112 +109,154 @@ func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { } func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringSliceFlag(&cli.StringSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, EnvVarName: "TEST", EnvVarValue: "oh,no", - }) + } + c := runTest(t, tis) expect(t, c.StringSlice("test"), []string{"oh", "no"}) + + c = runRacyTest(t, tis) + refute(t, c.StringSlice("test"), []string{"oh", "no"}) } func TestIntSliceApplyInputSourceValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, - }) + } + c := runTest(t, tis) expect(t, c.IntSlice("test"), []int{1, 2}) + + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test"), []int{1, 2}) } func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, ContextValueString: "3", - }) + } + c := runTest(t, tis) expect(t, c.IntSlice("test"), []int{3}) + + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test"), []int{3}) } func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntSliceFlag(&cli.IntSliceFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: []interface{}{1, 2}, EnvVarName: "TEST", EnvVarValue: "3,4", - }) + } + c := runTest(t, tis) expect(t, c.IntSlice("test"), []int{3, 4}) + + c = runRacyTest(t, tis) + refute(t, c.IntSlice("test"), []int{3, 4}) } func TestBoolApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: true, - }) + } + c := runTest(t, tis) expect(t, true, c.Bool("test")) + + c = runRacyTest(t, tis) + refute(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: false, ContextValueString: "true", - }) + } + c := runTest(t, tis) expect(t, true, c.Bool("test")) + + c = runRacyTest(t, tis) + refute(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewBoolFlag(&cli.BoolFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: false, EnvVarName: "TEST", EnvVarValue: "true", - }) + } + c := runTest(t, tis) expect(t, true, c.Bool("test")) + + c = runRacyTest(t, tis) + refute(t, true, c.Bool("test")) } func TestStringApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", - }) + } + c := runTest(t, tis) expect(t, "hello", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "hello", c.String("test")) } func TestStringApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringFlag(&cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", ContextValueString: "goodbye", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewStringFlag(&cli.StringFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", EnvVarValue: "goodbye", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } + func TestPathApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), FlagName: "test", MapValue: "hello", SourcePath: "/path/to/source/file", - }) + } + c := runTest(t, tis) expected := "/path/to/source/hello" if runtime.GOOS == "windows" { @@ -200,125 +269,176 @@ func TestPathApplyInputSourceMethodSet(t *testing.T) { } } expect(t, expected, c.String("test")) + + c = runRacyTest(t, tis) + refute(t, expected, c.String("test")) } func TestPathApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewPathFlag(&cli.PathFlag{Name: "test"}), FlagName: "test", MapValue: "hello", ContextValueString: "goodbye", SourcePath: "/path/to/source/file", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } func TestPathApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewPathFlag(&cli.PathFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", EnvVarValue: "goodbye", SourcePath: "/path/to/source/file", - }) + } + c := runTest(t, tis) expect(t, "goodbye", c.String("test")) + + c = runRacyTest(t, tis) + refute(t, "goodbye", c.String("test")) } func TestIntApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, - }) + } + c := runTest(t, tis) expect(t, 15, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, 15, c.Int("test")) } func TestIntApplyInputSourceMethodSetNegativeValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: -1, - }) + } + c := runTest(t, tis) expect(t, -1, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, -1, c.Int("test")) } func TestIntApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, ContextValueString: "7", - }) + } + c := runTest(t, tis) expect(t, 7, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, 7, c.Int("test")) } func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewIntFlag(&cli.IntFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 15, EnvVarName: "TEST", EnvVarValue: "12", - }) + } + c := runTest(t, tis) expect(t, 12, c.Int("test")) + + c = runRacyTest(t, tis) + refute(t, 12, c.Int("test")) } func TestDurationApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: 30 * time.Second, - }) + } + c := runTest(t, tis) expect(t, 30*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, 30*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodSetNegativeValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: -30 * time.Second, - }) + } + c := runTest(t, tis) expect(t, -30*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, -30*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: 30 * time.Second, ContextValueString: (15 * time.Second).String(), - }) + } + c := runTest(t, tis) expect(t, 15*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, 15*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewDurationFlag(&cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 30 * time.Second, EnvVarName: "TEST", EnvVarValue: (15 * time.Second).String(), - }) + } + c := runTest(t, tis) expect(t, 15*time.Second, c.Duration("test")) + + c = runRacyTest(t, tis) + refute(t, 15*time.Second, c.Duration("test")) } func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, - }) + } + c := runTest(t, tis) expect(t, 1.3, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, 1.3, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodSetNegativeValue(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: -1.3, - }) + } + c := runTest(t, tis) expect(t, -1.3, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, -1.3, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodSetNegativeValueNotSet(t *testing.T) { @@ -331,24 +451,32 @@ func TestFloat64ApplyInputSourceMethodSetNegativeValueNotSet(t *testing.T) { } func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, ContextValueString: fmt.Sprintf("%v", 1.4), - }) + } + c := runTest(t, tis) expect(t, 1.4, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, 1.4, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { - c := runTest(t, testApplyInputSource{ + tis := testApplyInputSource{ Flag: NewFloat64Flag(&cli.Float64Flag{Name: "test", EnvVars: []string{"TEST"}}), FlagName: "test", MapValue: 1.3, EnvVarName: "TEST", EnvVarValue: fmt.Sprintf("%v", 1.4), - }) + } + c := runTest(t, tis) expect(t, 1.4, c.Float64("test")) + + c = runRacyTest(t, tis) + refute(t, 1.4, c.Float64("test")) } func runTest(t *testing.T, test testApplyInputSource) *cli.Context { @@ -376,6 +504,19 @@ func runTest(t *testing.T, test testApplyInputSource) *cli.Context { return c } +func runRacyTest(t *testing.T, test testApplyInputSource) *cli.Context { + set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError) + c := cli.NewContext(nil, set, nil) + _ = test.Flag.ApplyInputSourceValue(c, &racyInputSource{ + MapInputSource: &MapInputSource{ + file: test.SourcePath, + valueMap: map[interface{}]interface{}{test.FlagName: test.MapValue}, + }, + }) + + return c +} + type Parser [2]string func (p *Parser) Set(value string) error { @@ -393,3 +534,5 @@ func (p *Parser) Set(value string) error { func (p *Parser) String() string { return fmt.Sprintf("%s,%s", p[0], p[1]) } + +type bogus [1]uint diff --git a/altsrc/helpers_test.go b/altsrc/helpers_test.go index 33e8a4b..1f8d5c2 100644 --- a/altsrc/helpers_test.go +++ b/altsrc/helpers_test.go @@ -22,7 +22,10 @@ func expect(t *testing.T, a interface{}, b interface{}) { } func refute(t *testing.T, a interface{}, b interface{}) { - if a == b { - t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + _, fn, line, _ := runtime.Caller(1) + fn = strings.Replace(fn, wd+"/", "", -1) + + if reflect.DeepEqual(a, b) { + t.Errorf("(%s:%d) Did not expect %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } From cdb1730b7da03b1d52af0a5cf20d3cf4422c191d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 25 Apr 2022 22:21:46 -0400 Subject: [PATCH 38/45] Drop extra if condition --- altsrc/map_input_source.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/altsrc/map_input_source.go b/altsrc/map_input_source.go index cc11fc5..e065c7c 100644 --- a/altsrc/map_input_source.go +++ b/altsrc/map_input_source.go @@ -249,11 +249,8 @@ func (fsm *MapInputSource) isSet(name string) bool { return exists } - if _, exists := nestedVal(name, fsm.valueMap); exists { - return exists - } - - return false + _, exists := nestedVal(name, fsm.valueMap) + return exists } func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error { From ed0033984bc6dbfc4a864d454f3e606978c5cc2b Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 1 May 2022 23:02:05 -0400 Subject: [PATCH 39/45] Generate flag types (again?) Closes #1381 --- .github/workflows/cli.yml | 5 + Makefile | 40 ++ cli.go | 2 +- docs/CONTRIBUTING.md | 110 ++++- flag-spec.yaml | 50 +++ flag.go | 2 +- flag_bool.go | 31 -- flag_duration.go | 31 -- flag_float64.go | 31 -- flag_float64_slice.go | 24 -- flag_generic.go | 31 -- flag_int.go | 31 -- flag_int64.go | 31 -- flag_int64_slice.go | 24 -- flag_int_slice.go | 24 -- flag_path.go | 31 +- flag_string.go | 32 -- flag_string_slice.go | 26 -- flag_timestamp.go | 32 -- flag_uint.go | 31 -- flag_uint64.go | 31 -- go.mod | 1 + go.sum | 4 +- internal/build/build.go | 131 +++--- internal/genflags/cmd/genflags/main.go | 161 ++++++++ internal/genflags/generated.gotmpl | 53 +++ internal/genflags/generated_test.gotmpl | 22 + internal/genflags/package.go | 34 ++ internal/genflags/package_test.go | 41 ++ internal/genflags/spec.go | 100 +++++ zz_generated.flags.go | 507 ++++++++++++++++++++++++ zz_generated.flags_test.go | 116 ++++++ 32 files changed, 1304 insertions(+), 516 deletions(-) create mode 100644 Makefile create mode 100644 flag-spec.yaml create mode 100644 internal/genflags/cmd/genflags/main.go create mode 100644 internal/genflags/generated.gotmpl create mode 100644 internal/genflags/generated_test.gotmpl create mode 100644 internal/genflags/package.go create mode 100644 internal/genflags/package_test.go create mode 100644 internal/genflags/spec.go create mode 100644 zz_generated.flags.go create mode 100644 zz_generated.flags_test.go diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index c60ed4c..84f9cae 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -87,3 +87,8 @@ jobs: - name: toc run: go run internal/build/build.go toc docs/v2/manual.md + + - name: diff check + run: | + git diff --exit-code + git diff --cached --exit-code diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dd6893a --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +# NOTE: this Makefile is meant to provide a simplified entry point for humans to +# run all of the critical steps to verify one's changes are harmonious in +# nature. Keeping target bodies to one line each and abstaining from make magic +# are very important so that maintainers and contributors can focus their +# attention on files that are primarily Go. + +.PHONY: all +all: generate lint tag-test test check-bin tag-check-bin gfmrun toc + +.PHONY: generate +generate: + go run internal/build/build.go generate + +.PHONY: lint +lint: + go run internal/build/build.go vet + +.PHONY: tag-test +tag-test: + go run internal/build/build.go -tags urfave_cli_no_docs test + +.PHONY: test +test: + go run internal/build/build.go test + +.PHONY: check-bin +check-bin: + go run internal/build/build.go check-binary-size + +.PHONY: tag-check-bin +tag-check-bin: + go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size + +.PHONY: gfmrun +gfmrun: + go run internal/build/build.go gfmrun docs/v2/manual.md + +.PHONY: toc +toc: + go run internal/build/build.go toc docs/v2/manual.md diff --git a/cli.go b/cli.go index 62a5bc2..2a11c5a 100644 --- a/cli.go +++ b/cli.go @@ -20,4 +20,4 @@ // } package cli -//go:generate go run flag-gen/main.go flag-gen/assets_vfsdata.go +//go:generate go run internal/genflags/cmd/genflags/main.go diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 41671a9..88c6b95 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,18 +1,102 @@ ## Contributing -Use @urfave/cli to ping the maintainers. +Welcome to the `urfave/cli` contributor docs! This goal of this document is to help those +interested in joining the 200+ humans who have contributed to this project over the years. -Feel free to put up a pull request to fix a bug or maybe add a feature. We will -give it a code review and make sure that it does not break backwards -compatibility. If collaborators agree that it is in line with -the vision of the project, we will work with you to get the code into -a mergeable state and merge it into the main branch. +> As a general guiding principle, the current maintainers may be notified via the +> @urfave/cli GitHub team. -If you have contributed something significant to the project, we will most -likely add you as a collaborator. As a collaborator you are given the ability -to merge others pull requests. It is very important that new code does not -break existing code, so be careful about what code you do choose to merge. +All of the current maintainers are *volunteers* who live in various timezones with +different scheduling needs, so please understand that your contribution or question may +not get a response for many days. -If you feel like you have contributed to the project but have not yet been added -as a collaborator, we probably forgot to add you :sweat_smile:. Please open an -issue! +### semantic versioning adherence + +The `urfave/cli` project strives to strictly adhere to semantic versioning. The active +development branches and the milestones and import paths to which they correspond are: + +#### `main` branch + + + +The majority of active development and issue management is targeting the `main` branch, +which **MUST** *only* receive bug fixes and feature *additions*. + +- :arrow_right: [`v2.x`](https://github.com/urfave/cli/milestone/16) +- :arrow_right: `github.com/urfave/cli/v2` + +#### `v1` branch + + + +The `v1` branch **MUST** only receive bug fixes in the `v1.22.x` series. There is no +strict rule regarding bug fixes to the `v2.x` series being backported to the `v1.22.x` +series. + +- :arrow_right: [`v1.22.x`](https://github.com/urfave/cli/milestone/11) +- :arrow_right: `github.com/urfave/cli` + +#### `v3-dev-main` branch + + + +The `v3-dev-branch` **MUST** receive all bug fixes and features added to the `main` branch +and **MAY** receive feature *removals* and other changes that are otherwise +*backward-incompatible* with the `v2.x` series. + +- :arrow_right: [`v3.x`](https://github.com/urfave/cli/milestone/5) +- unreleased / unsupported + +### development workflow + +Most of the tooling around the development workflow strives for effective +[dogfooding](https://en.wikipedia.org/wiki/Eating_your_own_dog_food). There is a top-level +`Makefile` that is maintained strictly for the purpose of easing verification of one's +development environment and any changes one may have introduced: + +```sh +make +``` + +Running the default `make` target (`all`) will ensure all of the critical steps are run to +verify one's changes are harmonious in nature. The same steps are also run during the +[continuous integration +phase](https://github.com/urfave/cli/blob/main/.github/workflows/cli.yml). + +#### generated code + +A significant portion of the project's source code is generated, with the goal being to +eliminate repetetive maintenance where other type-safe abstraction is impractical or +impossible with Go versions `< 1.18`. In a future where the eldest Go version supported is +`1.18.x`, there will likely be efforts to take advantage of +[generics](https://go.dev/doc/tutorial/generics). + +The built-in `go generate` command is used to run the commands specified in +`//go:generate` directives. Each such command runs a file that also supports a command +line help system which may be consulted for further information, e.g.: + +```sh +go run internal/genflags/cmd/genflags/main.go --help +``` + +### pull requests + +Please feel free to open a pull request to fix a bug or add a feature. The @urfave/cli +team will review it as soon as possible, giving special attention to maintaining backward +compatibility. If the @urfave/cli team agrees that your contribution is in line with the +vision of the project, they will work with you to get the code into a mergeable state, +merged, and then released. + +### granting of commit bit / admin mode + +Those with a history of contributing to this project will likely be invited to join the +@urfave/cli team. As a member of the @urfave/cli team, you will have the ability to fully +administer pull requests, issues, and other repository bits. + +If you feel that you should be a member of the @urfave/cli team but have not yet been +added, the most likely explanation is that this is an accidental oversight! :sweat_smile:. +Please open an issue! + + diff --git a/flag-spec.yaml b/flag-spec.yaml new file mode 100644 index 0000000..d85fa30 --- /dev/null +++ b/flag-spec.yaml @@ -0,0 +1,50 @@ +# NOTE: this file is used by the tool defined in +# ./internal/genflags/cmd/genflags/main.go which uses the +# `genflags.Spec` type that maps to this file structure. + +flag_types: + bool: {} + float64: {} + int64: {} + int: {} + time.Duration: {} + uint64: {} + uint: {} + + string: + struct_fields: + - { name: TakesFile, type: bool } + Generic: + struct_fields: + - { name: TakesFile, type: bool } + Path: + struct_fields: + - { name: TakesFile, type: bool } + + Float64Slice: + value_pointer: true + skip_interfaces: + - fmt.Stringer + Int64Slice: + value_pointer: true + skip_interfaces: + - fmt.Stringer + IntSlice: + value_pointer: true + skip_interfaces: + - fmt.Stringer + StringSlice: + value_pointer: true + skip_interfaces: + - fmt.Stringer + struct_fields: + - { name: TakesFile, type: bool } + Timestamp: + value_pointer: true + struct_fields: + - { name: Layout, type: string } + + # TODO: enable UintSlice + # UintSlice: {} + # TODO: enable Uint64Slice once #1334 lands + # Uint64Slice: {} diff --git a/flag.go b/flag.go index d2a33f0..0c61d00 100644 --- a/flag.go +++ b/flag.go @@ -258,7 +258,7 @@ func withEnvHint(envVars []string, str string) string { return str + envText } -func flagNames(name string, aliases []string) []string { +func FlagNames(name string, aliases []string) []string { var ret []string for _, part := range append([]string{name}, aliases...) { diff --git a/flag_bool.go b/flag_bool.go index 018f9af..f984acf 100644 --- a/flag_bool.go +++ b/flag_bool.go @@ -6,37 +6,6 @@ import ( "strconv" ) -// BoolFlag is a flag with type bool -type BoolFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value bool - DefaultText string - Destination *bool - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *BoolFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *BoolFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *BoolFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *BoolFlag) IsRequired() bool { return f.Required diff --git a/flag_duration.go b/flag_duration.go index 9c6dde7..a6677ad 100644 --- a/flag_duration.go +++ b/flag_duration.go @@ -6,37 +6,6 @@ import ( "time" ) -// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) -type DurationFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value time.Duration - DefaultText string - Destination *time.Duration - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *DurationFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *DurationFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *DurationFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *DurationFlag) IsRequired() bool { return f.Required diff --git a/flag_float64.go b/flag_float64.go index 62fdb13..62a1973 100644 --- a/flag_float64.go +++ b/flag_float64.go @@ -6,37 +6,6 @@ import ( "strconv" ) -// Float64Flag is a flag with type float64 -type Float64Flag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value float64 - DefaultText string - Destination *float64 - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *Float64Flag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *Float64Flag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *Float64Flag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *Float64Flag) IsRequired() bool { return f.Required diff --git a/flag_float64_slice.go b/flag_float64_slice.go index 8be586c..0f09c8c 100644 --- a/flag_float64_slice.go +++ b/flag_float64_slice.go @@ -75,36 +75,12 @@ func (f *Float64Slice) Get() interface{} { return *f } -// Float64SliceFlag is a flag with type *Float64Slice -type Float64SliceFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value *Float64Slice - DefaultText string - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *Float64SliceFlag) IsSet() bool { - return f.HasBeenSet -} - // String returns a readable representation of this value // (for usage defaults) func (f *Float64SliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyFloat64SliceFlag(f)) } -// Names returns the names of the flag -func (f *Float64SliceFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *Float64SliceFlag) IsRequired() bool { return f.Required diff --git a/flag_generic.go b/flag_generic.go index ae594d4..71e7173 100644 --- a/flag_generic.go +++ b/flag_generic.go @@ -11,37 +11,6 @@ type Generic interface { String() string } -// GenericFlag is a flag with type Generic -type GenericFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value Generic - DefaultText string - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *GenericFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *GenericFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *GenericFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *GenericFlag) IsRequired() bool { return f.Required diff --git a/flag_int.go b/flag_int.go index 63ec9a4..b68c3e8 100644 --- a/flag_int.go +++ b/flag_int.go @@ -6,37 +6,6 @@ import ( "strconv" ) -// IntFlag is a flag with type int -type IntFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value int - DefaultText string - Destination *int - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *IntFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *IntFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *IntFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *IntFlag) IsRequired() bool { return f.Required diff --git a/flag_int64.go b/flag_int64.go index 929e2d8..3f71134 100644 --- a/flag_int64.go +++ b/flag_int64.go @@ -6,37 +6,6 @@ import ( "strconv" ) -// Int64Flag is a flag with type int64 -type Int64Flag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value int64 - DefaultText string - Destination *int64 - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *Int64Flag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *Int64Flag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *Int64Flag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *Int64Flag) IsRequired() bool { return f.Required diff --git a/flag_int64_slice.go b/flag_int64_slice.go index 08f03dd..14d8c41 100644 --- a/flag_int64_slice.go +++ b/flag_int64_slice.go @@ -76,36 +76,12 @@ func (i *Int64Slice) Get() interface{} { return *i } -// Int64SliceFlag is a flag with type *Int64Slice -type Int64SliceFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value *Int64Slice - DefaultText string - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *Int64SliceFlag) IsSet() bool { - return f.HasBeenSet -} - // String returns a readable representation of this value // (for usage defaults) func (f *Int64SliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyInt64SliceFlag(f)) } -// Names returns the names of the flag -func (f *Int64SliceFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *Int64SliceFlag) IsRequired() bool { return f.Required diff --git a/flag_int_slice.go b/flag_int_slice.go index e5b9e21..4ed7f2c 100644 --- a/flag_int_slice.go +++ b/flag_int_slice.go @@ -87,36 +87,12 @@ func (i *IntSlice) Get() interface{} { return *i } -// IntSliceFlag is a flag with type *IntSlice -type IntSliceFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value *IntSlice - DefaultText string - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *IntSliceFlag) IsSet() bool { - return f.HasBeenSet -} - // String returns a readable representation of this value // (for usage defaults) func (f *IntSliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyIntSliceFlag(f)) } -// Names returns the names of the flag -func (f *IntSliceFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *IntSliceFlag) IsRequired() bool { return f.Required diff --git a/flag_path.go b/flag_path.go index a7a213e..095a596 100644 --- a/flag_path.go +++ b/flag_path.go @@ -5,36 +5,7 @@ import ( "fmt" ) -type PathFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value string - DefaultText string - Destination *string - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *PathFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *PathFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *PathFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} +type Path = string // IsRequired returns whether or not the flag is required func (f *PathFlag) IsRequired() bool { diff --git a/flag_string.go b/flag_string.go index 0365762..4831c17 100644 --- a/flag_string.go +++ b/flag_string.go @@ -5,38 +5,6 @@ import ( "fmt" ) -// StringFlag is a flag with type string -type StringFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value string - DefaultText string - Destination *string - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *StringFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *StringFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *StringFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *StringFlag) IsRequired() bool { return f.Required diff --git a/flag_string_slice.go b/flag_string_slice.go index 82aa1a4..9e69d00 100644 --- a/flag_string_slice.go +++ b/flag_string_slice.go @@ -70,38 +70,12 @@ func (s *StringSlice) Get() interface{} { return *s } -// StringSliceFlag is a flag with type *StringSlice -type StringSliceFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - TakesFile bool - Value *StringSlice - DefaultText string - HasBeenSet bool - Destination *StringSlice -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *StringSliceFlag) IsSet() bool { - return f.HasBeenSet -} - // String returns a readable representation of this value // (for usage defaults) func (f *StringSliceFlag) String() string { return withEnvHint(f.GetEnvVars(), stringifyStringSliceFlag(f)) } -// Names returns the names of the flag -func (f *StringSliceFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *StringSliceFlag) IsRequired() bool { return f.Required diff --git a/flag_timestamp.go b/flag_timestamp.go index 7bda8ee..32899f5 100644 --- a/flag_timestamp.go +++ b/flag_timestamp.go @@ -58,38 +58,6 @@ func (t *Timestamp) Get() interface{} { return *t } -// TimestampFlag is a flag with type time -type TimestampFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Layout string - Value *Timestamp - DefaultText string - HasBeenSet bool - Destination *Timestamp -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *TimestampFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *TimestampFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *TimestampFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *TimestampFlag) IsRequired() bool { return f.Required diff --git a/flag_uint.go b/flag_uint.go index 0fd0777..625f11c 100644 --- a/flag_uint.go +++ b/flag_uint.go @@ -6,37 +6,6 @@ import ( "strconv" ) -// UintFlag is a flag with type uint -type UintFlag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value uint - DefaultText string - Destination *uint - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *UintFlag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *UintFlag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *UintFlag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *UintFlag) IsRequired() bool { return f.Required diff --git a/flag_uint64.go b/flag_uint64.go index c8314ac..58969a5 100644 --- a/flag_uint64.go +++ b/flag_uint64.go @@ -6,37 +6,6 @@ import ( "strconv" ) -// Uint64Flag is a flag with type uint64 -type Uint64Flag struct { - Name string - Aliases []string - Usage string - EnvVars []string - FilePath string - Required bool - Hidden bool - Value uint64 - DefaultText string - Destination *uint64 - HasBeenSet bool -} - -// IsSet returns whether or not the flag has been set through env or file -func (f *Uint64Flag) IsSet() bool { - return f.HasBeenSet -} - -// String returns a readable representation of this value -// (for usage defaults) -func (f *Uint64Flag) String() string { - return FlagStringer(f) -} - -// Names returns the names of the flag -func (f *Uint64Flag) Names() []string { - return flagNames(f.Name, f.Aliases) -} - // IsRequired returns whether or not the flag is required func (f *Uint64Flag) IsRequired() bool { return f.Required diff --git a/go.mod b/go.mod index 7a4b88b..3455d26 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/BurntSushi/toml v1.1.0 github.com/cpuguy83/go-md2man/v2 v2.0.1 + golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index bde502b..a96d8e7 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/build/build.go b/internal/build/build.go index dec0dfb..0e9391b 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -6,7 +6,6 @@ import ( "bufio" "bytes" "fmt" - "io/ioutil" "log" "math" "os" @@ -16,9 +15,18 @@ import ( "github.com/urfave/cli/v2" ) -var packages = []string{"cli", "altsrc"} - func main() { + top, err := func() (string, error) { + if v, err := sh("git", "rev-parse", "--show-toplevel"); err == nil { + return strings.TrimSpace(v), nil + } + + return os.Getwd() + }() + if err != nil { + log.Fatal(err) + } + app := cli.NewApp() app.Name = "builder" @@ -45,20 +53,41 @@ func main() { Name: "check-binary-size", Action: checkBinarySizeActionFunc, }, + { + Name: "generate", + Action: GenerateActionFunc, + }, } app.Flags = []cli.Flag{ &cli.StringFlag{ Name: "tags", Usage: "set build tags", }, + &cli.PathFlag{ + Name: "top", + Value: top, + }, + &cli.StringSliceFlag{ + Name: "packages", + Value: cli.NewStringSlice("cli", "altsrc", "internal/build", "internal/genflags"), + }, } - err := app.Run(os.Args) - if err != nil { + if err := app.Run(os.Args); err != nil { log.Fatal(err) } } +func sh(exe string, args ...string) (string, error) { + cmd := exec.Command(exe, args...) + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + + fmt.Fprintf(os.Stderr, "# ---> %s\n", cmd) + outBytes, err := cmd.Output() + return string(outBytes), err +} + func runCmd(arg string, args ...string) error { cmd := exec.Command(arg, args...) @@ -66,78 +95,63 @@ func runCmd(arg string, args ...string) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + fmt.Fprintf(os.Stderr, "# ---> %s\n", cmd) return cmd.Run() } -func VetActionFunc(_ *cli.Context) error { - return runCmd("go", "vet") +func VetActionFunc(cCtx *cli.Context) error { + return runCmd("go", "vet", cCtx.Path("top")+"/...") } func TestActionFunc(c *cli.Context) error { tags := c.String("tags") - for _, pkg := range packages { - var packageName string + for _, pkg := range c.StringSlice("packages") { + packageName := "github.com/urfave/cli/v2" - if pkg == "cli" { - packageName = "github.com/urfave/cli/v2" - } else { + if pkg != "cli" { packageName = fmt.Sprintf("github.com/urfave/cli/v2/%s", pkg) } - coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg) - - err := runCmd("go", "test", "-tags", tags, "-v", coverProfile, packageName) - if err != nil { + if err := runCmd( + "go", "test", + "-tags", tags, + "-v", + "--coverprofile", pkg+".coverprofile", + "--covermode", "count", + "--cover", packageName, + packageName, + ); err != nil { return err } } - return testCleanup() + return testCleanup(c.StringSlice("packages")) } -func testCleanup() error { - var out bytes.Buffer +func testCleanup(packages []string) error { + out := &bytes.Buffer{} + + fmt.Fprintf(out, "mode: count\n") for _, pkg := range packages { - file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg)) + filename := pkg + ".coverprofile" + + lineBytes, err := os.ReadFile(filename) if err != nil { return err } - b, err := ioutil.ReadAll(file) - if err != nil { - return err - } + lines := strings.Split(string(lineBytes), "\n") - out.Write(b) - err = file.Close() - if err != nil { - return err - } + fmt.Fprintf(out, strings.Join(lines[1:], "\n")) - err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg)) - if err != nil { + if err := os.Remove(filename); err != nil { return err } } - outFile, err := os.Create("coverage.txt") - if err != nil { - return err - } - - _, err = out.WriteTo(outFile) - if err != nil { - return err - } - - err = outFile.Close() - if err != nil { - return err - } - - return nil + return os.WriteFile("coverage.txt", out.Bytes(), 0644) } func GfmrunActionFunc(c *cli.Context) error { @@ -179,17 +193,7 @@ func TocActionFunc(c *cli.Context) error { filename = "README.md" } - err := runCmd("markdown-toc", "-i", filename) - if err != nil { - return err - } - - err = runCmd("git", "diff", "--exit-code") - if err != nil { - return err - } - - return nil + return runCmd("markdown-toc", "-i", filename) } // checkBinarySizeActionFunc checks the size of an example binary to ensure that we are keeping size down @@ -201,7 +205,6 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { cliBuiltFilePath = "./internal/example-cli/built-example" helloSourceFilePath = "./internal/example-hello-world/example-hello-world.go" helloBuiltFilePath = "./internal/example-hello-world/built-example" - desiredMinBinarySize = 1.675 desiredMaxBinarySize = 2.2 badNewsEmoji = "🚨" goodNewsEmoji = "✨" @@ -209,8 +212,14 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { mbStringFormatter = "%.1fMB" ) + desiredMinBinarySize := 1.675 + tags := c.String("tags") + if strings.Contains(tags, "urfave_cli_no_docs") { + desiredMinBinarySize = 1.39 + } + // get cli example size cliSize, err := getSize(cliSourceFilePath, cliBuiltFilePath, tags) if err != nil { @@ -280,6 +289,10 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { return nil } +func GenerateActionFunc(cCtx *cli.Context) error { + return runCmd("go", "generate", cCtx.Path("top")+"/...") +} + func getSize(sourcePath string, builtPath string, tags string) (size int64, err error) { // build example binary err = runCmd("go", "build", "-tags", tags, "-o", builtPath, "-ldflags", "-s -w", sourcePath) diff --git a/internal/genflags/cmd/genflags/main.go b/internal/genflags/cmd/genflags/main.go new file mode 100644 index 0000000..f4aaeba --- /dev/null +++ b/internal/genflags/cmd/genflags/main.go @@ -0,0 +1,161 @@ +package main + +import ( + "bytes" + "context" + _ "embed" + "log" + "os" + "os/exec" + "os/signal" + "path/filepath" + "strings" + "syscall" + "text/template" + + "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/internal/genflags" + "gopkg.in/yaml.v2" +) + +const ( + defaultPackageName = "cli" +) + +func sh(ctx context.Context, exe string, args ...string) (string, error) { + cmd := exec.CommandContext(ctx, exe, args...) + cmd.Stderr = os.Stderr + outBytes, err := cmd.Output() + return string(outBytes), err +} + +func main() { + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + top := "../../" + if v, err := sh(ctx, "git", "rev-parse", "--show-toplevel"); err == nil { + top = strings.TrimSpace(v) + } + + app := &cli.App{ + Flags: []cli.Flag{ + &cli.PathFlag{ + Name: "flag-spec-yaml", + Aliases: []string{"f"}, + Value: filepath.Join(top, "flag-spec.yaml"), + }, + &cli.PathFlag{ + Name: "generated-output", + Aliases: []string{"o"}, + Value: filepath.Join(top, "zz_generated.flags.go"), + }, + &cli.PathFlag{ + Name: "generated-test-output", + Aliases: []string{"t"}, + Value: filepath.Join(top, "zz_generated.flags_test.go"), + }, + &cli.StringFlag{ + Name: "generated-package-name", + Aliases: []string{"p"}, + Value: defaultPackageName, + }, + &cli.StringFlag{ + Name: "generated-test-package-name", + Aliases: []string{"T"}, + Value: defaultPackageName + "_test", + }, + &cli.StringFlag{ + Name: "urfave-cli-namespace", + Aliases: []string{"n"}, + Value: "", + }, + &cli.StringFlag{ + Name: "urfave-cli-test-namespace", + Aliases: []string{"N"}, + Value: "cli.", + }, + }, + Action: runGenFlags, + } + + if err := app.RunContext(ctx, os.Args); err != nil { + log.Fatal(err) + } +} + +func runGenFlags(cCtx *cli.Context) error { + specBytes, err := os.ReadFile(cCtx.Path("flag-spec-yaml")) + if err != nil { + return err + } + + spec := &genflags.Spec{} + if err := yaml.Unmarshal(specBytes, spec); err != nil { + return err + } + + if cCtx.IsSet("generated-package-name") { + spec.PackageName = strings.TrimSpace(cCtx.String("generated-package-name")) + } + + if strings.TrimSpace(spec.PackageName) == "" { + spec.PackageName = defaultPackageName + } + + if cCtx.IsSet("generated-test-package-name") { + spec.TestPackageName = strings.TrimSpace(cCtx.String("generated-test-package-name")) + } + + if strings.TrimSpace(spec.TestPackageName) == "" { + spec.TestPackageName = defaultPackageName + "_test" + } + + if cCtx.IsSet("urfave-cli-namespace") { + spec.UrfaveCLINamespace = strings.TrimSpace(cCtx.String("urfave-cli-namespace")) + } + + if cCtx.IsSet("urfave-cli-test-namespace") { + spec.UrfaveCLITestNamespace = strings.TrimSpace(cCtx.String("urfave-cli-test-namespace")) + } else { + spec.UrfaveCLITestNamespace = "cli." + } + + genTmpl, err := template.New("gen").Parse(genflags.TemplateString) + if err != nil { + return err + } + + genTestTmpl, err := template.New("gen_test").Parse(genflags.TestTemplateString) + if err != nil { + return err + } + + genBuf := &bytes.Buffer{} + if err := genTmpl.Execute(genBuf, spec); err != nil { + return err + } + + genTestBuf := &bytes.Buffer{} + if err := genTestTmpl.Execute(genTestBuf, spec); err != nil { + return err + } + + if err := os.WriteFile(cCtx.Path("generated-output"), genBuf.Bytes(), 0644); err != nil { + return err + } + + if err := os.WriteFile(cCtx.Path("generated-test-output"), genTestBuf.Bytes(), 0644); err != nil { + return err + } + + if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-output")); err != nil { + return err + } + + if _, err := sh(cCtx.Context, "goimports", "-w", cCtx.Path("generated-test-output")); err != nil { + return err + } + + return nil +} diff --git a/internal/genflags/generated.gotmpl b/internal/genflags/generated.gotmpl new file mode 100644 index 0000000..681a101 --- /dev/null +++ b/internal/genflags/generated.gotmpl @@ -0,0 +1,53 @@ +// WARNING: this file is generated. DO NOT EDIT + +package {{.PackageName}} + +{{range .SortedFlagTypes}} +// {{.TypeName}} is a flag with type {{.GoType}} +type {{.TypeName}} struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value {{if .ValuePointer}}*{{end}}{{.GoType}} + Destination *{{.GoType}} + + Aliases []string + EnvVars []string + + {{range .StructFields}} + {{.Name}} {{.Type}} + {{end}} +} + +{{if .GenerateFmtStringerInterface}} +// String returns a readable representation of this value (for usage defaults) +func (f *{{.TypeName}}) String() string { + return {{$.UrfaveCLINamespace}}FlagStringer(f) +} +{{end}} + +{{if .GenerateFlagInterface}} +// IsSet returns whether or not the flag has been set through env or file +func (f *{{.TypeName}}) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *{{.TypeName}}) Names() []string { + return {{$.UrfaveCLINamespace}}FlagNames(f.Name, f.Aliases) +} + +{{end}}{{/* /if .GenerateFlagInterface */}} +{{end}}{{/* /range .SortedFlagTypes */}} + +// vim{{/* 👻 */}}:ro +{{/* +vim:filetype=gotexttmpl +*/}} diff --git a/internal/genflags/generated_test.gotmpl b/internal/genflags/generated_test.gotmpl new file mode 100644 index 0000000..84806f2 --- /dev/null +++ b/internal/genflags/generated_test.gotmpl @@ -0,0 +1,22 @@ +// WARNING: this file is generated. DO NOT EDIT + +package {{.TestPackageName}} + +{{range .SortedFlagTypes}} +{{if .GenerateFlagInterface}} +func Test{{.TypeName}}_SatisfiesFlagInterface(t *testing.T) { + var _ {{$.UrfaveCLITestNamespace}}Flag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} +} +{{end}} + +{{if .GenerateFmtStringerInterface}} +func Test{{.TypeName}}_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} +} +{{end}} +{{end}} + +// vim{{/* 👻 */}}:ro +{{/* +vim:filetype=gotexttmpl +*/}} diff --git a/internal/genflags/package.go b/internal/genflags/package.go new file mode 100644 index 0000000..4e5de41 --- /dev/null +++ b/internal/genflags/package.go @@ -0,0 +1,34 @@ +package genflags + +import ( + _ "embed" + "strings" + + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +var ( + //go:embed generated.gotmpl + TemplateString string + + //go:embed generated_test.gotmpl + TestTemplateString string + + titler = cases.Title(language.Und, cases.NoLower) +) + +func TypeName(goType string, fc *FlagTypeConfig) string { + if fc != nil && strings.TrimSpace(fc.TypeName) != "" { + return strings.TrimSpace(fc.TypeName) + } + + dotSplit := strings.Split(goType, ".") + goType = dotSplit[len(dotSplit)-1] + + if strings.HasPrefix(goType, "[]") { + return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag" + } + + return titler.String(goType) + "Flag" +} diff --git a/internal/genflags/package_test.go b/internal/genflags/package_test.go new file mode 100644 index 0000000..3920540 --- /dev/null +++ b/internal/genflags/package_test.go @@ -0,0 +1,41 @@ +package genflags_test + +import ( + "fmt" + "testing" + + "github.com/urfave/cli/v2/internal/genflags" +) + +func TestTypeName(t *testing.T) { + for _, tc := range []struct { + gt string + fc *genflags.FlagTypeConfig + expected string + }{ + {gt: "int", fc: nil, expected: "IntFlag"}, + {gt: "int", fc: &genflags.FlagTypeConfig{}, expected: "IntFlag"}, + {gt: "int", fc: &genflags.FlagTypeConfig{TypeName: "VeryIntyFlag"}, expected: "VeryIntyFlag"}, + {gt: "[]bool", fc: nil, expected: "BoolSliceFlag"}, + {gt: "[]bool", fc: &genflags.FlagTypeConfig{}, expected: "BoolSliceFlag"}, + {gt: "[]bool", fc: &genflags.FlagTypeConfig{TypeName: "ManyTruthsFlag"}, expected: "ManyTruthsFlag"}, + {gt: "time.Rumination", fc: nil, expected: "RuminationFlag"}, + {gt: "time.Rumination", fc: &genflags.FlagTypeConfig{}, expected: "RuminationFlag"}, + {gt: "time.Rumination", fc: &genflags.FlagTypeConfig{TypeName: "PonderFlag"}, expected: "PonderFlag"}, + } { + t.Run( + fmt.Sprintf("type=%s,cfg=%v", tc.gt, func() string { + if tc.fc != nil { + return tc.fc.TypeName + } + return "nil" + }()), + func(ct *testing.T) { + actual := genflags.TypeName(tc.gt, tc.fc) + if tc.expected != actual { + ct.Errorf("expected %q, got %q", tc.expected, actual) + } + }, + ) + } +} diff --git a/internal/genflags/spec.go b/internal/genflags/spec.go new file mode 100644 index 0000000..d9f18db --- /dev/null +++ b/internal/genflags/spec.go @@ -0,0 +1,100 @@ +package genflags + +import ( + "sort" + "strings" +) + +type Spec struct { + FlagTypes map[string]*FlagTypeConfig `yaml:"flag_types"` + PackageName string `yaml:"package_name"` + TestPackageName string `yaml:"test_package_name"` + UrfaveCLINamespace string `yaml:"urfave_cli_namespace"` + UrfaveCLITestNamespace string `yaml:"urfave_cli_test_namespace"` +} + +func (gfs *Spec) SortedFlagTypes() []*FlagType { + typeNames := []string{} + + for name := range gfs.FlagTypes { + if strings.HasPrefix(name, "[]") { + name = strings.TrimPrefix(name, "[]") + "Slice" + } + + typeNames = append(typeNames, name) + } + + sort.Strings(typeNames) + + ret := make([]*FlagType, len(typeNames)) + + for i, typeName := range typeNames { + ret[i] = &FlagType{ + GoType: typeName, + Config: gfs.FlagTypes[typeName], + } + } + + return ret +} + +type FlagTypeConfig struct { + SkipInterfaces []string `yaml:"skip_interfaces"` + StructFields []*FlagStructField `yaml:"struct_fields"` + TypeName string `yaml:"type_name"` + ValuePointer bool `yaml:"value_pointer"` +} + +type FlagStructField struct { + Name string + Type string +} + +type FlagType struct { + GoType string + Config *FlagTypeConfig +} + +func (ft *FlagType) StructFields() []*FlagStructField { + if ft.Config == nil || ft.Config.StructFields == nil { + return []*FlagStructField{} + } + + return ft.Config.StructFields +} + +func (ft *FlagType) ValuePointer() bool { + if ft.Config == nil { + return false + } + + return ft.Config.ValuePointer +} + +func (ft *FlagType) TypeName() string { + return TypeName(ft.GoType, ft.Config) +} + +func (ft *FlagType) GenerateFmtStringerInterface() bool { + return ft.skipInterfaceNamed("fmt.Stringer") +} + +func (ft *FlagType) GenerateFlagInterface() bool { + return ft.skipInterfaceNamed("Flag") +} + +func (ft *FlagType) skipInterfaceNamed(name string) bool { + if ft.Config == nil { + return true + } + + lowName := strings.ToLower(name) + + for _, interfaceName := range ft.Config.SkipInterfaces { + if strings.ToLower(interfaceName) == lowName { + return false + } + } + + return true +} diff --git a/zz_generated.flags.go b/zz_generated.flags.go new file mode 100644 index 0000000..eb980bc --- /dev/null +++ b/zz_generated.flags.go @@ -0,0 +1,507 @@ +// WARNING: this file is generated. DO NOT EDIT + +package cli + +import "time" + +// Float64SliceFlag is a flag with type Float64Slice +type Float64SliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Float64Slice + Destination *Float64Slice + + Aliases []string + EnvVars []string +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *Float64SliceFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *Float64SliceFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// GenericFlag is a flag with type Generic +type GenericFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value Generic + Destination *Generic + + Aliases []string + EnvVars []string + + TakesFile bool +} + +// String returns a readable representation of this value (for usage defaults) +func (f *GenericFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *GenericFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *GenericFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// Int64SliceFlag is a flag with type Int64Slice +type Int64SliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Int64Slice + Destination *Int64Slice + + Aliases []string + EnvVars []string +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *Int64SliceFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *Int64SliceFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// IntSliceFlag is a flag with type IntSlice +type IntSliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *IntSlice + Destination *IntSlice + + Aliases []string + EnvVars []string +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *IntSliceFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *IntSliceFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// PathFlag is a flag with type Path +type PathFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value Path + Destination *Path + + Aliases []string + EnvVars []string + + TakesFile bool +} + +// String returns a readable representation of this value (for usage defaults) +func (f *PathFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *PathFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *PathFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// StringSliceFlag is a flag with type StringSlice +type StringSliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *StringSlice + Destination *StringSlice + + Aliases []string + EnvVars []string + + TakesFile bool +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *StringSliceFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *StringSliceFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// TimestampFlag is a flag with type Timestamp +type TimestampFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Timestamp + Destination *Timestamp + + Aliases []string + EnvVars []string + + Layout string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *TimestampFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *TimestampFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *TimestampFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// BoolFlag is a flag with type bool +type BoolFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value bool + Destination *bool + + Aliases []string + EnvVars []string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *BoolFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *BoolFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *BoolFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// Float64Flag is a flag with type float64 +type Float64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value float64 + Destination *float64 + + Aliases []string + EnvVars []string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *Float64Flag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *Float64Flag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *Float64Flag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// IntFlag is a flag with type int +type IntFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value int + Destination *int + + Aliases []string + EnvVars []string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *IntFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *IntFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *IntFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// Int64Flag is a flag with type int64 +type Int64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value int64 + Destination *int64 + + Aliases []string + EnvVars []string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *Int64Flag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *Int64Flag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *Int64Flag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// StringFlag is a flag with type string +type StringFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value string + Destination *string + + Aliases []string + EnvVars []string + + TakesFile bool +} + +// String returns a readable representation of this value (for usage defaults) +func (f *StringFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *StringFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *StringFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// DurationFlag is a flag with type time.Duration +type DurationFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value time.Duration + Destination *time.Duration + + Aliases []string + EnvVars []string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *DurationFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *DurationFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *DurationFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// UintFlag is a flag with type uint +type UintFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value uint + Destination *uint + + Aliases []string + EnvVars []string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *UintFlag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *UintFlag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *UintFlag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// Uint64Flag is a flag with type uint64 +type Uint64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value uint64 + Destination *uint64 + + Aliases []string + EnvVars []string +} + +// String returns a readable representation of this value (for usage defaults) +func (f *Uint64Flag) String() string { + return FlagStringer(f) +} + +// IsSet returns whether or not the flag has been set through env or file +func (f *Uint64Flag) IsSet() bool { + return f.HasBeenSet +} + +// Names returns the names of the flag +func (f *Uint64Flag) Names() []string { + return FlagNames(f.Name, f.Aliases) +} + +// vim:ro diff --git a/zz_generated.flags_test.go b/zz_generated.flags_test.go new file mode 100644 index 0000000..791c61f --- /dev/null +++ b/zz_generated.flags_test.go @@ -0,0 +1,116 @@ +// WARNING: this file is generated. DO NOT EDIT + +package cli_test + +import ( + "fmt" + "testing" + + "github.com/urfave/cli/v2" +) + +func TestFloat64SliceFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.Float64SliceFlag{} +} + +func TestGenericFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.GenericFlag{} +} + +func TestGenericFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.GenericFlag{} +} + +func TestInt64SliceFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.Int64SliceFlag{} +} + +func TestIntSliceFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.IntSliceFlag{} +} + +func TestPathFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.PathFlag{} +} + +func TestPathFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.PathFlag{} +} + +func TestStringSliceFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.StringSliceFlag{} +} + +func TestTimestampFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.TimestampFlag{} +} + +func TestTimestampFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.TimestampFlag{} +} + +func TestBoolFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.BoolFlag{} +} + +func TestBoolFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.BoolFlag{} +} + +func TestFloat64Flag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.Float64Flag{} +} + +func TestFloat64Flag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.Float64Flag{} +} + +func TestIntFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.IntFlag{} +} + +func TestIntFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.IntFlag{} +} + +func TestInt64Flag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.Int64Flag{} +} + +func TestInt64Flag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.Int64Flag{} +} + +func TestStringFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.StringFlag{} +} + +func TestStringFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.StringFlag{} +} + +func TestDurationFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.DurationFlag{} +} + +func TestDurationFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.DurationFlag{} +} + +func TestUintFlag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.UintFlag{} +} + +func TestUintFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.UintFlag{} +} + +func TestUint64Flag_SatisfiesFlagInterface(t *testing.T) { + var _ cli.Flag = &cli.Uint64Flag{} +} + +func TestUint64Flag_SatisfiesFmtStringerInterface(t *testing.T) { + var _ fmt.Stringer = &cli.Uint64Flag{} +} + +// vim:ro From 2630f2642a16f987ece3fd7a2faf688945b5fda5 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 1 May 2022 23:16:59 -0400 Subject: [PATCH 40/45] Add missing go.sum entry plus minor touchups to genflags app --- go.sum | 1 + internal/genflags/cmd/genflags/main.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.sum b/go.sum index a96d8e7..e6383eb 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,7 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/internal/genflags/cmd/genflags/main.go b/internal/genflags/cmd/genflags/main.go index f4aaeba..cad2508 100644 --- a/internal/genflags/cmd/genflags/main.go +++ b/internal/genflags/cmd/genflags/main.go @@ -39,6 +39,8 @@ func main() { } app := &cli.App{ + Name: "genflags", + Usage: "Generate flag types for urfave/cli", Flags: []cli.Flag{ &cli.PathFlag{ Name: "flag-spec-yaml", From adc61ca06b8b8c4537776b5db1052842fa5e3fac Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 1 May 2022 23:18:16 -0400 Subject: [PATCH 41/45] Add another missing go.sum entry (?) --- go.sum | 1 + 1 file changed, 1 insertion(+) diff --git a/go.sum b/go.sum index e6383eb..2c20c28 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= From 71cd131794231860b15fb029002cd309db40c300 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 1 May 2022 23:36:59 -0400 Subject: [PATCH 42/45] Add more tests to flag generation code --- internal/genflags/generated_test.gotmpl | 9 +- internal/genflags/spec_test.go | 112 ++++++++++++++++++++++ zz_generated.flags_test.go | 119 ++++++++++++++++++------ 3 files changed, 212 insertions(+), 28 deletions(-) create mode 100644 internal/genflags/spec_test.go diff --git a/internal/genflags/generated_test.gotmpl b/internal/genflags/generated_test.gotmpl index 84806f2..44e9ad4 100644 --- a/internal/genflags/generated_test.gotmpl +++ b/internal/genflags/generated_test.gotmpl @@ -5,13 +5,18 @@ package {{.TestPackageName}} {{range .SortedFlagTypes}} {{if .GenerateFlagInterface}} func Test{{.TypeName}}_SatisfiesFlagInterface(t *testing.T) { - var _ {{$.UrfaveCLITestNamespace}}Flag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + var f {{$.UrfaveCLITestNamespace}}Flag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + + _ = f.IsSet() + _ = f.Names() } {{end}} {{if .GenerateFmtStringerInterface}} func Test{{.TypeName}}_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + var f fmt.Stringer = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + + _ = f.String() } {{end}} {{end}} diff --git a/internal/genflags/spec_test.go b/internal/genflags/spec_test.go new file mode 100644 index 0000000..25a9c8b --- /dev/null +++ b/internal/genflags/spec_test.go @@ -0,0 +1,112 @@ +package genflags_test + +import ( + "reflect" + "testing" + + "github.com/urfave/cli/v2/internal/genflags" +) + +func TestSpec_SortedFlagTypes(t *testing.T) { + spec := &genflags.Spec{ + FlagTypes: map[string]*genflags.FlagTypeConfig{ + "nerf": &genflags.FlagTypeConfig{}, + "gerf": nil, + }, + } + + actual := spec.SortedFlagTypes() + expected := []*genflags.FlagType{ + { + GoType: "gerf", + Config: nil, + }, + { + GoType: "nerf", + Config: &genflags.FlagTypeConfig{}, + }, + } + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %#v, got %#v", expected, actual) + } +} + +func genFlagType() *genflags.FlagType { + return &genflags.FlagType{ + GoType: "blerf", + Config: &genflags.FlagTypeConfig{ + SkipInterfaces: []string{"fmt.Stringer"}, + StructFields: []*genflags.FlagStructField{ + { + Name: "Foibles", + Type: "int", + }, + { + Name: "Hoopled", + Type: "bool", + }, + }, + TypeName: "YeOldeBlerfFlag", + ValuePointer: true, + }, + } +} + +func TestFlagType_StructFields(t *testing.T) { + ft := genFlagType() + + sf := ft.StructFields() + if 2 != len(sf) { + t.Errorf("expected 2 struct fields, got %v", len(sf)) + return + } + + if "Foibles" != sf[0].Name { + t.Errorf("expected struct field order to be retained") + } +} + +func TestFlagType_ValuePointer(t *testing.T) { + ft := genFlagType() + + if !ft.ValuePointer() { + t.Errorf("expected ValuePointer to be true") + return + } + + ft.Config = nil + + if ft.ValuePointer() { + t.Errorf("expected ValuePointer to be false") + } +} + +func TestFlagType_GenerateFmtStringerInterface(t *testing.T) { + ft := genFlagType() + + if ft.GenerateFmtStringerInterface() { + t.Errorf("expected GenerateFmtStringerInterface to be false") + return + } + + ft.Config = nil + + if !ft.GenerateFmtStringerInterface() { + t.Errorf("expected GenerateFmtStringerInterface to be true") + } +} + +func TestFlagType_GenerateFlagInterface(t *testing.T) { + ft := genFlagType() + + if !ft.GenerateFlagInterface() { + t.Errorf("expected GenerateFlagInterface to be true") + return + } + + ft.Config = nil + + if !ft.GenerateFlagInterface() { + t.Errorf("expected GenerateFlagInterface to be true") + } +} diff --git a/zz_generated.flags_test.go b/zz_generated.flags_test.go index 791c61f..b8363d4 100644 --- a/zz_generated.flags_test.go +++ b/zz_generated.flags_test.go @@ -10,107 +10,174 @@ import ( ) func TestFloat64SliceFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.Float64SliceFlag{} + var f cli.Flag = &cli.Float64SliceFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestGenericFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.GenericFlag{} + var f cli.Flag = &cli.GenericFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestGenericFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.GenericFlag{} + var f fmt.Stringer = &cli.GenericFlag{} + + _ = f.String() } func TestInt64SliceFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.Int64SliceFlag{} + var f cli.Flag = &cli.Int64SliceFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestIntSliceFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.IntSliceFlag{} + var f cli.Flag = &cli.IntSliceFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestPathFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.PathFlag{} + var f cli.Flag = &cli.PathFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestPathFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.PathFlag{} + var f fmt.Stringer = &cli.PathFlag{} + + _ = f.String() } func TestStringSliceFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.StringSliceFlag{} + var f cli.Flag = &cli.StringSliceFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestTimestampFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.TimestampFlag{} + var f cli.Flag = &cli.TimestampFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestTimestampFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.TimestampFlag{} + var f fmt.Stringer = &cli.TimestampFlag{} + + _ = f.String() } func TestBoolFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.BoolFlag{} + var f cli.Flag = &cli.BoolFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestBoolFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.BoolFlag{} + var f fmt.Stringer = &cli.BoolFlag{} + + _ = f.String() } func TestFloat64Flag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.Float64Flag{} + var f cli.Flag = &cli.Float64Flag{} + + _ = f.IsSet() + _ = f.Names() } func TestFloat64Flag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.Float64Flag{} + var f fmt.Stringer = &cli.Float64Flag{} + + _ = f.String() } func TestIntFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.IntFlag{} + var f cli.Flag = &cli.IntFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestIntFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.IntFlag{} + var f fmt.Stringer = &cli.IntFlag{} + + _ = f.String() } func TestInt64Flag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.Int64Flag{} + var f cli.Flag = &cli.Int64Flag{} + + _ = f.IsSet() + _ = f.Names() } func TestInt64Flag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.Int64Flag{} + var f fmt.Stringer = &cli.Int64Flag{} + + _ = f.String() } func TestStringFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.StringFlag{} + var f cli.Flag = &cli.StringFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestStringFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.StringFlag{} + var f fmt.Stringer = &cli.StringFlag{} + + _ = f.String() } func TestDurationFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.DurationFlag{} + var f cli.Flag = &cli.DurationFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestDurationFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.DurationFlag{} + var f fmt.Stringer = &cli.DurationFlag{} + + _ = f.String() } func TestUintFlag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.UintFlag{} + var f cli.Flag = &cli.UintFlag{} + + _ = f.IsSet() + _ = f.Names() } func TestUintFlag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.UintFlag{} + var f fmt.Stringer = &cli.UintFlag{} + + _ = f.String() } func TestUint64Flag_SatisfiesFlagInterface(t *testing.T) { - var _ cli.Flag = &cli.Uint64Flag{} + var f cli.Flag = &cli.Uint64Flag{} + + _ = f.IsSet() + _ = f.Names() } func TestUint64Flag_SatisfiesFmtStringerInterface(t *testing.T) { - var _ fmt.Stringer = &cli.Uint64Flag{} + var f fmt.Stringer = &cli.Uint64Flag{} + + _ = f.String() } // vim:ro From 3288bec5be9f32485e1e4876d0c7b05be8ccd67e Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Wed, 4 May 2022 10:32:29 -0400 Subject: [PATCH 43/45] Introduce a v2.x semver approval gate --- Makefile | 28 +- docs/CONTRIBUTING.md | 23 +- godoc-current.txt | 2090 +++++++++++++++++++++++++++++++++++++++ internal/build/build.go | 91 +- testdata/godoc-v2.x.txt | 2090 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 4299 insertions(+), 23 deletions(-) create mode 100644 godoc-current.txt create mode 100644 testdata/godoc-v2.x.txt diff --git a/Makefile b/Makefile index dd6893a..52e9204 100644 --- a/Makefile +++ b/Makefile @@ -5,30 +5,22 @@ # attention on files that are primarily Go. .PHONY: all -all: generate lint tag-test test check-bin tag-check-bin gfmrun toc +all: generate vet tag-test test check-binary-size tag-check-binary-size gfmrun toc v2diff -.PHONY: generate -generate: - go run internal/build/build.go generate - -.PHONY: lint -lint: - go run internal/build/build.go vet +# NOTE: this is a special catch-all rule to run any of the commands +# defined in internal/build/build.go with optional arguments passed +# via GFLAGS (global flags) and FLAGS (command-specific flags), e.g.: +# +# $ make test GFLAGS='--packages cli' +%: + go run internal/build/build.go $(GFLAGS) $* $(FLAGS) .PHONY: tag-test tag-test: go run internal/build/build.go -tags urfave_cli_no_docs test -.PHONY: test -test: - go run internal/build/build.go test - -.PHONY: check-bin -check-bin: - go run internal/build/build.go check-binary-size - -.PHONY: tag-check-bin -tag-check-bin: +.PHONY: tag-check-binary-size +tag-check-binary-size: go run internal/build/build.go -tags urfave_cli_no_docs check-binary-size .PHONY: gfmrun diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 88c6b95..37055ff 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -12,8 +12,9 @@ not get a response for many days. ### semantic versioning adherence -The `urfave/cli` project strives to strictly adhere to semantic versioning. The active -development branches and the milestones and import paths to which they correspond are: +The `urfave/cli` project strives to strictly adhere to [semantic +versioning](https://semver.org/spec/v2.0.0.html). The active development branches and the +milestones and import paths to which they correspond are: #### `main` branch @@ -25,6 +26,10 @@ which **MUST** *only* receive bug fixes and feature *additions*. - :arrow_right: [`v2.x`](https://github.com/urfave/cli/milestone/16) - :arrow_right: `github.com/urfave/cli/v2` +The `main` branch in particular includes tooling to help with keeping the `v2.x` series +backward compatible. More details on this process are in the development workflow section +below. + #### `v1` branch @@ -63,6 +68,20 @@ verify one's changes are harmonious in nature. The same steps are also run durin [continuous integration phase](https://github.com/urfave/cli/blob/main/.github/workflows/cli.yml). +In the event that the `v2diff` target exits non-zero, this is a signal that the public API +surface area has changed. If the changes adhere to semantic versioning, meaning they are +*additions* or *bug fixes*, then manually running the approval step will "promote" the +current `go doc` output: + +```sh +make v2approve +``` + +Because the `generate` step includes updating `godoc-current.txt` and +`testdata/godoc-v2.x.txt`, these changes *MUST* be part of any proposed pull request so +that reviewers have an opportunity to also make an informed decision about the "promotion" +step. + #### generated code A significant portion of the project's source code is generated, with the goal being to diff --git a/godoc-current.txt b/godoc-current.txt new file mode 100644 index 0000000..1a9ab23 --- /dev/null +++ b/godoc-current.txt @@ -0,0 +1,2090 @@ +package cli // import "github.com/urfave/cli/v2" + +Package cli provides a minimal framework for creating and organizing command +line Go applications. cli is designed to be easy to understand and write, +the most simple cli application can be written as follows: + + func main() { + (&cli.App{}).Run(os.Args) + } + +Of course this application does not do much, so let's make this an actual +application: + + func main() { + app := &cli.App{ + Name: "greet", + Usage: "say a greeting", + Action: func(c *cli.Context) error { + fmt.Println("Greetings") + return nil + }, + } + + app.Run(os.Args) + } + +VARIABLES + +var AppHelpTemplate = `NAME: + {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} + +USAGE: + {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + +VERSION: + {{.Version}}{{end}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description | nindent 3 | trim}}{{end}}{{if len .Authors}} + +AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: + {{range $index, $author := .Authors}}{{if $index}} + {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} + +COMMANDS:{{range .VisibleCategories}}{{if .Name}} + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + +GLOBAL OPTIONS: + {{range $index, $option := .VisibleFlags}}{{if $index}} + {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + +COPYRIGHT: + {{.Copyright}}{{end}} +` + AppHelpTemplate is the text template for the Default help topic. cli.go uses + text/template to render templates. You can render custom help text by + setting this variable. + +var CommandHelpTemplate = `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} + +CATEGORY: + {{.Category}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlags}} + +OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +` + CommandHelpTemplate is the text template for the command help topic. cli.go + uses text/template to render templates. You can render custom help text by + setting this variable. + +var ErrWriter io.Writer = os.Stderr + ErrWriter is used to write errors to the user. This can be anything + implementing the io.Writer interface and defaults to os.Stderr. + +var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion + +function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' + for i in (commandline -opc) + if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} + return 1 + end + end + return 0 +end + +{{ range $v := .Completions }}{{ $v }} +{{ end }}` +var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }} + +{{end}}# NAME + +{{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} + +# SYNOPSIS + +{{ .App.Name }} +{{ if .SynopsisArgs }} +` + "```" + ` +{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` +{{ end }}{{ if .App.Description }} +# DESCRIPTION + +{{ .App.Description }} +{{ end }} +**Usage**: + +` + "```" + `{{ if .App.UsageText }} +{{ .App.UsageText }} +{{ else }} +{{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +{{ end }}` + "```" + ` +{{ if .GlobalArgs }} +# GLOBAL OPTIONS +{{ range $v := .GlobalArgs }} +{{ $v }}{{ end }} +{{ end }}{{ if .Commands }} +# COMMANDS +{{ range $v := .Commands }} +{{ $v }}{{ end }}{{ end }}` +var OsExiter = os.Exit + OsExiter is the function used when the app exits. If not set defaults to + os.Exit. + +var SubcommandHelpTemplate = `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description | nindent 3 | trim}}{{end}} + +COMMANDS:{{range .VisibleCategories}}{{if .Name}} + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + +OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +` + SubcommandHelpTemplate is the text template for the subcommand help topic. + cli.go uses text/template to render templates. You can render custom help + text by setting this variable. + +var VersionPrinter = printVersion + VersionPrinter prints the version for the App + +var HelpPrinter helpPrinter = printHelp + HelpPrinter is a function that writes the help output. If not set + explicitly, this calls HelpPrinterCustom using only the default template + functions. + + If custom logic for printing help is required, this function can be + overridden. If the ExtraInfo field is defined on an App, this function + should not be modified, as HelpPrinterCustom will be used directly in order + to capture the extra information. + +var HelpPrinterCustom helpPrinterCustom = printHelpCustom + HelpPrinterCustom is a function that writes the help output. It is used as + the default implementation of HelpPrinter, and may be called directly if the + ExtraInfo field is set on an App. + + +FUNCTIONS + +func DefaultAppComplete(cCtx *Context) + DefaultAppComplete prints the list of subcommands as the default app + completion method + +func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) +func FlagNames(name string, aliases []string) []string +func HandleAction(action interface{}, cCtx *Context) (err error) + HandleAction attempts to figure out which Action signature was used. If it's + an ActionFunc or a func with the legacy signature for Action, the func is + run! + +func HandleExitCoder(err error) + HandleExitCoder handles errors implementing ExitCoder by printing their + message and calling OsExiter with the given exit code. + + If the given error instead implements MultiError, each error will be checked + for the ExitCoder interface, and OsExiter will be called with the last exit + code found, or exit code 1 if no ExitCoder is found. + + This function is the default error-handling behavior for an App. + +func ShowAppHelp(cCtx *Context) error + ShowAppHelp is an action that displays the help. + +func ShowAppHelpAndExit(c *Context, exitCode int) + ShowAppHelpAndExit - Prints the list of subcommands for the app and exits + with exit code. + +func ShowCommandCompletions(ctx *Context, command string) + ShowCommandCompletions prints the custom completions for a given command + +func ShowCommandHelp(ctx *Context, command string) error + ShowCommandHelp prints help for the given command + +func ShowCommandHelpAndExit(c *Context, command string, code int) + ShowCommandHelpAndExit - exits with code after showing help + +func ShowCompletions(cCtx *Context) + ShowCompletions prints the lists of commands within a given context + +func ShowSubcommandHelp(cCtx *Context) error + ShowSubcommandHelp prints help for the given subcommand + +func ShowSubcommandHelpAndExit(c *Context, exitCode int) + ShowSubcommandHelpAndExit - Prints help for the given subcommand and exits + with exit code. + +func ShowVersion(cCtx *Context) + ShowVersion prints the version number of the App + + +TYPES + +type ActionFunc func(*Context) error + ActionFunc is the action to execute when no subcommands are specified + +type AfterFunc func(*Context) error + AfterFunc is an action to execute after any subcommands are run, but after + the subcommand has finished it is run even if Action() panics + +type App struct { + // The name of the program. Defaults to path.Base(os.Args[0]) + Name string + // Full name of command for help, defaults to Name + HelpName string + // Description of the program. + Usage string + // Text to override the USAGE section of help + UsageText string + // Description of the program argument format. + ArgsUsage string + // Version of the program + Version string + // Description of the program + Description string + // List of commands to execute + Commands []*Command + // List of flags to parse + Flags []Flag + // Boolean to enable bash completion commands + EnableBashCompletion bool + // Boolean to hide built-in help command and help flag + HideHelp bool + // Boolean to hide built-in help command but keep help flag. + // Ignored if HideHelp is true. + HideHelpCommand bool + // Boolean to hide built-in version flag and the VERSION section of help + HideVersion bool + + // An action to execute when the shell completion flag is set + BashComplete BashCompleteFunc + // An action to execute before any subcommands are run, but after the context is ready + // If a non-nil error is returned, no subcommands are run + Before BeforeFunc + // An action to execute after any subcommands are run, but after the subcommand has finished + // It is run even if Action() panics + After AfterFunc + // The action to execute when no subcommands are specified + Action ActionFunc + // Execute this function if the proper command cannot be found + CommandNotFound CommandNotFoundFunc + // Execute this function if a usage error occurs + OnUsageError OnUsageErrorFunc + // Compilation date + Compiled time.Time + // List of all authors who contributed + Authors []*Author + // Copyright of the binary if any + Copyright string + // Reader reader to write input to (useful for tests) + Reader io.Reader + // Writer writer to write output to + Writer io.Writer + // ErrWriter writes error output + ErrWriter io.Writer + // ExitErrHandler processes any error encountered while running an App before + // it is returned to the caller. If no function is provided, HandleExitCoder + // is used as the default behavior. + ExitErrHandler ExitErrHandlerFunc + // Other custom info + Metadata map[string]interface{} + // Carries a function which returns app specific info. + ExtraInfo func() map[string]string + // CustomAppHelpTemplate the text template for app help topic. + // cli.go uses text/template to render templates. You can + // render custom help text by setting this variable. + CustomAppHelpTemplate string + // Boolean to enable short-option handling so user can combine several + // single-character bool arguments into one + // i.e. foobar -o -v -> foobar -ov + UseShortOptionHandling bool + + // Has unexported fields. +} + App is the main structure of a cli application. It is recommended that an + app be created with the cli.NewApp() function + +func NewApp() *App + NewApp creates a new cli Application with some reasonable defaults for Name, + Usage, Version and Action. + +func (a *App) Command(name string) *Command + Command returns the named command on App. Returns nil if the command does + not exist + +func (a *App) Run(arguments []string) (err error) + Run is the entry point to the cli app. Parses the arguments slice and routes + to the proper flag/args combination + +func (a *App) RunAndExitOnError() + RunAndExitOnError calls .Run() and exits non-zero if an error was returned + + Deprecated: instead you should return an error that fulfills cli.ExitCoder + to cli.App.Run. This will cause the application to exit with the given error + code in the cli.ExitCoder + +func (a *App) RunAsSubcommand(ctx *Context) (err error) + RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() + to generate command-specific flags + +func (a *App) RunContext(ctx context.Context, arguments []string) (err error) + RunContext is like Run except it takes a Context that will be passed to its + commands and sub-commands. Through this, you can propagate timeouts and + cancellation requests + +func (a *App) Setup() + Setup runs initialization code to ensure all data structures are ready for + `Run` or inspection prior to `Run`. It is internally called by `Run`, but + will return early if setup has already happened. + +func (a *App) ToFishCompletion() (string, error) + ToFishCompletion creates a fish completion string for the `*App` The + function errors if either parsing or writing of the string fails. + +func (a *App) ToMan() (string, error) + ToMan creates a man page string for the `*App` The function errors if either + parsing or writing of the string fails. + +func (a *App) ToManWithSection(sectionNumber int) (string, error) + ToMan creates a man page string with section number for the `*App` The + function errors if either parsing or writing of the string fails. + +func (a *App) ToMarkdown() (string, error) + ToMarkdown creates a markdown string for the `*App` The function errors if + either parsing or writing of the string fails. + +func (a *App) VisibleCategories() []CommandCategory + VisibleCategories returns a slice of categories and commands that are + Hidden=false + +func (a *App) VisibleCommands() []*Command + VisibleCommands returns a slice of the Commands with Hidden=false + +func (a *App) VisibleFlags() []Flag + VisibleFlags returns a slice of the Flags with Hidden=false + +type Args interface { + // Get returns the nth argument, or else a blank string + Get(n int) string + // First returns the first argument, or else a blank string + First() string + // Tail returns the rest of the arguments (not the first one) + // or else an empty string slice + Tail() []string + // Len returns the length of the wrapped slice + Len() int + // Present checks if there are any arguments present + Present() bool + // Slice returns a copy of the internal slice + Slice() []string +} + +type Author struct { + Name string // The Authors name + Email string // The Authors email +} + Author represents someone who has contributed to a cli project. + +func (a *Author) String() string + String makes Author comply to the Stringer interface, to allow an easy print + in the templating process + +type BashCompleteFunc func(*Context) + BashCompleteFunc is an action to execute when the shell completion flag is + set + +type BeforeFunc func(*Context) error + BeforeFunc is an action to execute before any subcommands are run, but after + the context is ready if a non-nil error is returned, no subcommands are run + +type BoolFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value bool + Destination *bool + + Aliases []string + EnvVars []string +} + BoolFlag is a flag with type bool + +func (f *BoolFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *BoolFlag) Get(ctx *Context) bool + Get returns the flag’s value in the given Context. + +func (f *BoolFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *BoolFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *BoolFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *BoolFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *BoolFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *BoolFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *BoolFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *BoolFlag) Names() []string + Names returns the names of the flag + +func (f *BoolFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *BoolFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Command struct { + // The name of the command + Name string + // A list of aliases for the command + Aliases []string + // A short description of the usage of this command + Usage string + // Custom text to show on USAGE section of help + UsageText string + // A longer explanation of how the command works + Description string + // A short description of the arguments of this command + ArgsUsage string + // The category the command is part of + Category string + // The function to call when checking for bash command completions + BashComplete BashCompleteFunc + // An action to execute before any sub-subcommands are run, but after the context is ready + // If a non-nil error is returned, no sub-subcommands are run + Before BeforeFunc + // An action to execute after any subcommands are run, but after the subcommand has finished + // It is run even if Action() panics + After AfterFunc + // The function to call when this command is invoked + Action ActionFunc + // Execute this function if a usage error occurs. + OnUsageError OnUsageErrorFunc + // List of child commands + Subcommands []*Command + // List of flags to parse + Flags []Flag + // Treat all flags as normal arguments if true + SkipFlagParsing bool + // Boolean to hide built-in help command and help flag + HideHelp bool + // Boolean to hide built-in help command but keep help flag + // Ignored if HideHelp is true. + HideHelpCommand bool + // Boolean to hide this command from help or completion + Hidden bool + // Boolean to enable short-option handling so user can combine several + // single-character bool arguments into one + // i.e. foobar -o -v -> foobar -ov + UseShortOptionHandling bool + + // Full name of command for help, defaults to full command name, including parent commands. + HelpName string + + // CustomHelpTemplate the text template for the command help topic. + // cli.go uses text/template to render templates. You can + // render custom help text by setting this variable. + CustomHelpTemplate string + // Has unexported fields. +} + Command is a subcommand for a cli.App. + +func (c *Command) FullName() string + FullName returns the full name of the command. For subcommands this ensures + that parent commands are part of the command path + +func (c *Command) HasName(name string) bool + HasName returns true if Command.Name matches given name + +func (c *Command) Names() []string + Names returns the names including short names and aliases. + +func (c *Command) Run(ctx *Context) (err error) + Run invokes the command given the context, parses ctx.Args() to generate + command-specific flags + +func (c *Command) VisibleFlags() []Flag + VisibleFlags returns a slice of the Flags with Hidden=false + +type CommandCategories interface { + // AddCommand adds a command to a category, creating a new category if necessary. + AddCommand(category string, command *Command) + // categories returns a copy of the category slice + Categories() []CommandCategory +} + CommandCategories interface allows for category manipulation + +type CommandCategory interface { + // Name returns the category name string + Name() string + // VisibleCommands returns a slice of the Commands with Hidden=false + VisibleCommands() []*Command +} + CommandCategory is a category containing commands. + +type CommandNotFoundFunc func(*Context, string) + CommandNotFoundFunc is executed if the proper command cannot be found + +type Commands []*Command + +type CommandsByName []*Command + +func (c CommandsByName) Len() int + +func (c CommandsByName) Less(i, j int) bool + +func (c CommandsByName) Swap(i, j int) + +type Context struct { + context.Context + App *App + Command *Command + + // Has unexported fields. +} + Context is a type that is passed through to each Handler action in a cli + application. Context can be used to retrieve context-specific args and + parsed command-line options. + +func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context + NewContext creates a new context. For use in when invoking an App or Command + action. + +func (cCtx *Context) Args() Args + Args returns the command line arguments associated with the context. + +func (cCtx *Context) Bool(name string) bool + Bool looks up the value of a local BoolFlag, returns false if not found + +func (cCtx *Context) Duration(name string) time.Duration + Duration looks up the value of a local DurationFlag, returns 0 if not found + +func (cCtx *Context) FlagNames() []string + FlagNames returns a slice of flag names used by the this context and all of + its parent contexts. + +func (cCtx *Context) Float64(name string) float64 + Float64 looks up the value of a local Float64Flag, returns 0 if not found + +func (cCtx *Context) Float64Slice(name string) []float64 + Float64Slice looks up the value of a local Float64SliceFlag, returns nil if + not found + +func (cCtx *Context) Generic(name string) interface{} + Generic looks up the value of a local GenericFlag, returns nil if not found + +func (cCtx *Context) Int(name string) int + Int looks up the value of a local IntFlag, returns 0 if not found + +func (cCtx *Context) Int64(name string) int64 + Int64 looks up the value of a local Int64Flag, returns 0 if not found + +func (cCtx *Context) Int64Slice(name string) []int64 + Int64Slice looks up the value of a local Int64SliceFlag, returns nil if not + found + +func (cCtx *Context) IntSlice(name string) []int + IntSlice looks up the value of a local IntSliceFlag, returns nil if not + found + +func (cCtx *Context) IsSet(name string) bool + IsSet determines if the flag was actually set + +func (cCtx *Context) Lineage() []*Context + Lineage returns *this* context and all of its ancestor contexts in order + from child to parent + +func (cCtx *Context) LocalFlagNames() []string + LocalFlagNames returns a slice of flag names used in this context. + +func (cCtx *Context) NArg() int + NArg returns the number of the command line arguments. + +func (cCtx *Context) NumFlags() int + NumFlags returns the number of flags set + +func (cCtx *Context) Path(name string) string + Path looks up the value of a local PathFlag, returns "" if not found + +func (cCtx *Context) Set(name, value string) error + Set sets a context flag to a value. + +func (cCtx *Context) String(name string) string + String looks up the value of a local StringFlag, returns "" if not found + +func (cCtx *Context) StringSlice(name string) []string + StringSlice looks up the value of a local StringSliceFlag, returns nil if + not found + +func (cCtx *Context) Timestamp(name string) *time.Time + Timestamp gets the timestamp from a flag name + +func (cCtx *Context) Uint(name string) uint + Uint looks up the value of a local UintFlag, returns 0 if not found + +func (cCtx *Context) Uint64(name string) uint64 + Uint64 looks up the value of a local Uint64Flag, returns 0 if not found + +func (cCtx *Context) Value(name string) interface{} + Value returns the value of the flag corresponding to `name` + +type DocGenerationFlag interface { + Flag + + // TakesValue returns true if the flag takes a value, otherwise false + TakesValue() bool + + // GetUsage returns the usage string for the flag + GetUsage() string + + // GetValue returns the flags value as string representation and an empty + // string if the flag takes no value at all. + GetValue() string + + // GetDefaultText returns the default text for this flag + GetDefaultText() string + + // GetEnvVars returns the env vars for this flag + GetEnvVars() []string +} + DocGenerationFlag is an interface that allows documentation generation for + the flag + +type DurationFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value time.Duration + Destination *time.Duration + + Aliases []string + EnvVars []string +} + DurationFlag is a flag with type time.Duration + +func (f *DurationFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *DurationFlag) Get(ctx *Context) time.Duration + Get returns the flag’s value in the given Context. + +func (f *DurationFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *DurationFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *DurationFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *DurationFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *DurationFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *DurationFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *DurationFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *DurationFlag) Names() []string + Names returns the names of the flag + +func (f *DurationFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *DurationFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type ErrorFormatter interface { + Format(s fmt.State, verb rune) +} + ErrorFormatter is the interface that will suitably format the error output + +type ExitCoder interface { + error + ExitCode() int +} + ExitCoder is the interface checked by `App` and `Command` for a custom exit + code + +func Exit(message interface{}, exitCode int) ExitCoder + Exit wraps a message and exit code into an error, which by default is + handled with a call to os.Exit during default error handling. + + This is the simplest way to trigger a non-zero exit code for an App without + having to call os.Exit manually. During testing, this behavior can be + avoided by overiding the ExitErrHandler function on an App or the + package-global OsExiter function. + +func NewExitError(message interface{}, exitCode int) ExitCoder + NewExitError calls Exit to create a new ExitCoder. + + Deprecated: This function is a duplicate of Exit and will eventually be + removed. + +type ExitErrHandlerFunc func(cCtx *Context, err error) + ExitErrHandlerFunc is executed if provided in order to handle exitError + values returned by Actions and Before/After functions. + +type Flag interface { + fmt.Stringer + // Apply Flag settings to the given flag set + Apply(*flag.FlagSet) error + Names() []string + IsSet() bool +} + Flag is a common interface related to parsing flags in cli. For more + advanced flag parsing techniques, it is recommended that this interface be + implemented. + +var BashCompletionFlag Flag = &BoolFlag{ + Name: "generate-bash-completion", + Hidden: true, +} + BashCompletionFlag enables bash-completion for all commands and subcommands + +var HelpFlag Flag = &BoolFlag{ + Name: "help", + Aliases: []string{"h"}, + Usage: "show help", +} + HelpFlag prints the help for all commands and subcommands. Set to nil to + disable the flag. The subcommand will still be added unless HideHelp or + HideHelpCommand is set to true. + +var VersionFlag Flag = &BoolFlag{ + Name: "version", + Aliases: []string{"v"}, + Usage: "print the version", +} + VersionFlag prints the version for the application + +type FlagEnvHintFunc func(envVars []string, str string) string + FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help + with the environment variable details. + +var FlagEnvHinter FlagEnvHintFunc = withEnvHint + FlagEnvHinter annotates flag help message with the environment variable + details. This is used by the default FlagStringer. + +type FlagFileHintFunc func(filePath, str string) string + FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help + with the file path details. + +var FlagFileHinter FlagFileHintFunc = withFileHint + FlagFileHinter annotates flag help message with the environment variable + details. This is used by the default FlagStringer. + +type FlagNamePrefixFunc func(fullName []string, placeholder string) string + FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix + text for a flag's full name. + +var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames + FlagNamePrefixer converts a full flag name and its placeholder into the help + message flag prefix. This is used by the default FlagStringer. + +type FlagStringFunc func(Flag) string + FlagStringFunc is used by the help generation to display a flag, which is + expected to be a single line. + +var FlagStringer FlagStringFunc = stringifyFlag + FlagStringer converts a flag definition to a string. This is used by help to + display a flag. + +type FlagsByName []Flag + FlagsByName is a slice of Flag. + +func (f FlagsByName) Len() int + +func (f FlagsByName) Less(i, j int) bool + +func (f FlagsByName) Swap(i, j int) + +type Float64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value float64 + Destination *float64 + + Aliases []string + EnvVars []string +} + Float64Flag is a flag with type float64 + +func (f *Float64Flag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Float64Flag) Get(ctx *Context) float64 + Get returns the flag’s value in the given Context. + +func (f *Float64Flag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Float64Flag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Float64Flag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Float64Flag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Float64Flag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Float64Flag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Float64Flag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Float64Flag) Names() []string + Names returns the names of the flag + +func (f *Float64Flag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Float64Flag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Float64Slice struct { + // Has unexported fields. +} + Float64Slice wraps []float64 to satisfy flag.Value + +func NewFloat64Slice(defaults ...float64) *Float64Slice + NewFloat64Slice makes a *Float64Slice with default values + +func (f *Float64Slice) Get() interface{} + Get returns the slice of float64s set by this flag + +func (f *Float64Slice) Serialize() string + Serialize allows Float64Slice to fulfill Serializer + +func (f *Float64Slice) Set(value string) error + Set parses the value into a float64 and appends it to the list of values + +func (f *Float64Slice) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Float64Slice) Value() []float64 + Value returns the slice of float64s set by this flag + +type Float64SliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Float64Slice + Destination *Float64Slice + + Aliases []string + EnvVars []string +} + Float64SliceFlag is a flag with type Float64Slice + +func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Float64SliceFlag) Get(ctx *Context) []float64 + Get returns the flag’s value in the given Context. + +func (f *Float64SliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Float64SliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Float64SliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Float64SliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Float64SliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Float64SliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Float64SliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Float64SliceFlag) Names() []string + Names returns the names of the flag + +func (f *Float64SliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Float64SliceFlag) TakesValue() bool + TakesValue returns true if the flag takes a value, otherwise false + +type Generic interface { + Set(value string) error + String() string +} + Generic is a generic parseable type identified by a specific flag + +type GenericFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value Generic + Destination *Generic + + Aliases []string + EnvVars []string + + TakesFile bool +} + GenericFlag is a flag with type Generic + +func (f GenericFlag) Apply(set *flag.FlagSet) error + 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) Get(ctx *Context) interface{} + Get returns the flag’s value in the given Context. + +func (f *GenericFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *GenericFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *GenericFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *GenericFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *GenericFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *GenericFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *GenericFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *GenericFlag) Names() []string + Names returns the names of the flag + +func (f *GenericFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *GenericFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Int64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value int64 + Destination *int64 + + Aliases []string + EnvVars []string +} + Int64Flag is a flag with type int64 + +func (f *Int64Flag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Int64Flag) Get(ctx *Context) int64 + Get returns the flag’s value in the given Context. + +func (f *Int64Flag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Int64Flag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Int64Flag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Int64Flag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Int64Flag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Int64Flag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Int64Flag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Int64Flag) Names() []string + Names returns the names of the flag + +func (f *Int64Flag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Int64Flag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Int64Slice struct { + // Has unexported fields. +} + Int64Slice wraps []int64 to satisfy flag.Value + +func NewInt64Slice(defaults ...int64) *Int64Slice + NewInt64Slice makes an *Int64Slice with default values + +func (i *Int64Slice) Get() interface{} + Get returns the slice of ints set by this flag + +func (i *Int64Slice) Serialize() string + Serialize allows Int64Slice to fulfill Serializer + +func (i *Int64Slice) Set(value string) error + Set parses the value into an integer and appends it to the list of values + +func (i *Int64Slice) String() string + String returns a readable representation of this value (for usage defaults) + +func (i *Int64Slice) Value() []int64 + Value returns the slice of ints set by this flag + +type Int64SliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Int64Slice + Destination *Int64Slice + + Aliases []string + EnvVars []string +} + Int64SliceFlag is a flag with type Int64Slice + +func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Int64SliceFlag) Get(ctx *Context) []int64 + Get returns the flag’s value in the given Context. + +func (f *Int64SliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Int64SliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f Int64SliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Int64SliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Int64SliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Int64SliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Int64SliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Int64SliceFlag) Names() []string + Names returns the names of the flag + +func (f *Int64SliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Int64SliceFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type IntFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value int + Destination *int + + Aliases []string + EnvVars []string +} + IntFlag is a flag with type int + +func (f *IntFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *IntFlag) Get(ctx *Context) int + Get returns the flag’s value in the given Context. + +func (f *IntFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *IntFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *IntFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *IntFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *IntFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *IntFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *IntFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *IntFlag) Names() []string + Names returns the names of the flag + +func (f *IntFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *IntFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type IntSlice struct { + // Has unexported fields. +} + IntSlice wraps []int to satisfy flag.Value + +func NewIntSlice(defaults ...int) *IntSlice + NewIntSlice makes an *IntSlice with default values + +func (i *IntSlice) Get() interface{} + Get returns the slice of ints set by this flag + +func (i *IntSlice) Serialize() string + Serialize allows IntSlice to fulfill Serializer + +func (i *IntSlice) Set(value string) error + Set parses the value into an integer and appends it to the list of values + +func (i *IntSlice) SetInt(value int) + TODO: Consistently have specific Set function for Int64 and Float64 ? SetInt + directly adds an integer to the list of values + +func (i *IntSlice) String() string + String returns a readable representation of this value (for usage defaults) + +func (i *IntSlice) Value() []int + Value returns the slice of ints set by this flag + +type IntSliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *IntSlice + Destination *IntSlice + + Aliases []string + EnvVars []string +} + IntSliceFlag is a flag with type IntSlice + +func (f *IntSliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *IntSliceFlag) Get(ctx *Context) []int + Get returns the flag’s value in the given Context. + +func (f *IntSliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *IntSliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f IntSliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *IntSliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *IntSliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *IntSliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *IntSliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *IntSliceFlag) Names() []string + Names returns the names of the flag + +func (f *IntSliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *IntSliceFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type MultiError interface { + error + Errors() []error +} + MultiError is an error that wraps multiple errors. + +type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error + OnUsageErrorFunc is executed if a usage error occurs. This is useful for + displaying customized usage error messages. This function is able to replace + the original error messages. If this function is not set, the "Incorrect + usage" is displayed and the execution is interrupted. + +type Path = string + +type PathFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value Path + Destination *Path + + Aliases []string + EnvVars []string + + TakesFile bool +} + PathFlag is a flag with type Path + +func (f *PathFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *PathFlag) Get(ctx *Context) string + Get returns the flag’s value in the given Context. + +func (f *PathFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *PathFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *PathFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *PathFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *PathFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *PathFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *PathFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *PathFlag) Names() []string + Names returns the names of the flag + +func (f *PathFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *PathFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type RequiredFlag interface { + Flag + + IsRequired() bool +} + RequiredFlag is an interface that allows us to mark flags as required it + allows flags required flags to be backwards compatible with the Flag + interface + +type Serializer interface { + Serialize() string +} + Serializer is used to circumvent the limitations of flag.FlagSet.Set + +type StringFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value string + Destination *string + + Aliases []string + EnvVars []string + + TakesFile bool +} + StringFlag is a flag with type string + +func (f *StringFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *StringFlag) Get(ctx *Context) string + Get returns the flag’s value in the given Context. + +func (f *StringFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *StringFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *StringFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *StringFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *StringFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *StringFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *StringFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *StringFlag) Names() []string + Names returns the names of the flag + +func (f *StringFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *StringFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type StringSlice struct { + // Has unexported fields. +} + StringSlice wraps a []string to satisfy flag.Value + +func NewStringSlice(defaults ...string) *StringSlice + NewStringSlice creates a *StringSlice with default values + +func (s *StringSlice) Get() interface{} + Get returns the slice of strings set by this flag + +func (s *StringSlice) Serialize() string + Serialize allows StringSlice to fulfill Serializer + +func (s *StringSlice) Set(value string) error + Set appends the string value to the list of values + +func (s *StringSlice) String() string + String returns a readable representation of this value (for usage defaults) + +func (s *StringSlice) Value() []string + Value returns the slice of strings set by this flag + +type StringSliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *StringSlice + Destination *StringSlice + + Aliases []string + EnvVars []string + + TakesFile bool +} + StringSliceFlag is a flag with type StringSlice + +func (f *StringSliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *StringSliceFlag) Get(ctx *Context) []string + Get returns the flag’s value in the given Context. + +func (f *StringSliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *StringSliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *StringSliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *StringSliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *StringSliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *StringSliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *StringSliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *StringSliceFlag) Names() []string + Names returns the names of the flag + +func (f *StringSliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *StringSliceFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Timestamp struct { + // Has unexported fields. +} + Timestamp wrap to satisfy golang's flag interface. + +func NewTimestamp(timestamp time.Time) *Timestamp + Timestamp constructor + +func (t *Timestamp) Get() interface{} + Get returns the flag structure + +func (t *Timestamp) Set(value string) error + Parses the string value to timestamp + +func (t *Timestamp) SetLayout(layout string) + Set the timestamp string layout for future parsing + +func (t *Timestamp) SetTimestamp(value time.Time) + Set the timestamp value directly + +func (t *Timestamp) String() string + String returns a readable representation of this value (for usage defaults) + +func (t *Timestamp) Value() *time.Time + Value returns the timestamp value stored in the flag + +type TimestampFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Timestamp + Destination *Timestamp + + Aliases []string + EnvVars []string + + Layout string +} + TimestampFlag is a flag with type Timestamp + +func (f *TimestampFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *TimestampFlag) Get(ctx *Context) *time.Time + Get returns the flag’s value in the given Context. + +func (f *TimestampFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *TimestampFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *TimestampFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *TimestampFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *TimestampFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *TimestampFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *TimestampFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *TimestampFlag) Names() []string + Names returns the names of the flag + +func (f *TimestampFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *TimestampFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Uint64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value uint64 + Destination *uint64 + + Aliases []string + EnvVars []string +} + Uint64Flag is a flag with type uint64 + +func (f *Uint64Flag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Uint64Flag) Get(ctx *Context) uint64 + Get returns the flag’s value in the given Context. + +func (f *Uint64Flag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Uint64Flag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Uint64Flag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Uint64Flag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Uint64Flag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Uint64Flag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Uint64Flag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Uint64Flag) Names() []string + Names returns the names of the flag + +func (f *Uint64Flag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Uint64Flag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type UintFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value uint + Destination *uint + + Aliases []string + EnvVars []string +} + UintFlag is a flag with type uint + +func (f *UintFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *UintFlag) Get(ctx *Context) uint + Get returns the flag’s value in the given Context. + +func (f *UintFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *UintFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *UintFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *UintFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *UintFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *UintFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *UintFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *UintFlag) Names() []string + Names returns the names of the flag + +func (f *UintFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *UintFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type VisibleFlag interface { + Flag + + // IsVisible returns true if the flag is not hidden, otherwise false + IsVisible() bool +} + VisibleFlag is an interface that allows to check if a flag is visible + +package altsrc // import "github.com/urfave/cli/v2/altsrc" + + +FUNCTIONS + +func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error + ApplyInputSourceValues iterates over all provided flags and executes + ApplyInputSourceValue on flags implementing the FlagInputSourceExtension + interface to initialize these flags to an alternate input source. + +func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc + InitInputSource is used to to setup an InputSourceContext on a cli.Command + Before method. It will create a new input source based on the func provided. + If there is no error it will then apply the new input source to any flags + that are supported by the input source + +func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc + InitInputSourceWithContext is used to to setup an InputSourceContext on a + cli.Command Before method. It will create a new input source based on the + func provided with potentially using existing cli.Context values to + initialize itself. If there is no error it will then apply the new input + source to any flags that are supported by the input source + +func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) + NewJSONSourceFromFlagFunc returns a func that takes a cli.Context and + returns an InputSourceContext suitable for retrieving config variables from + a file containing JSON data with the file name defined by the given flag. + +func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) + NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a + provided flag name and source context. + +func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) + NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a + provided flag name and source context. + + +TYPES + +type BoolFlag struct { + *cli.BoolFlag + // Has unexported fields. +} + BoolFlag is the flag type that wraps cli.BoolFlag to allow for other values + to be specified + +func NewBoolFlag(fl *cli.BoolFlag) *BoolFlag + NewBoolFlag creates a new BoolFlag + +func (f *BoolFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + BoolFlag.Apply + +func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Bool value to the flagSet if required + +type DurationFlag struct { + *cli.DurationFlag + // Has unexported fields. +} + DurationFlag is the flag type that wraps cli.DurationFlag to allow for other + values to be specified + +func NewDurationFlag(fl *cli.DurationFlag) *DurationFlag + NewDurationFlag creates a new DurationFlag + +func (f *DurationFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + DurationFlag.Apply + +func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Duration value to the flagSet if required + +type FlagInputSourceExtension interface { + cli.Flag + ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +} + FlagInputSourceExtension is an extension interface of cli.Flag that allows a + value to be set on the existing parsed flags. + +type Float64Flag struct { + *cli.Float64Flag + // Has unexported fields. +} + Float64Flag is the flag type that wraps cli.Float64Flag to allow for other + values to be specified + +func NewFloat64Flag(fl *cli.Float64Flag) *Float64Flag + NewFloat64Flag creates a new Float64Flag + +func (f *Float64Flag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Float64Flag.Apply + +func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Float64 value to the flagSet if required + +type Float64SliceFlag struct { + *cli.Float64SliceFlag + // Has unexported fields. +} + Float64SliceFlag is the flag type that wraps cli.Float64SliceFlag to allow + for other values to be specified + +func NewFloat64SliceFlag(fl *cli.Float64SliceFlag) *Float64SliceFlag + NewFloat64SliceFlag creates a new Float64SliceFlag + +func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Float64SliceFlag.Apply + +type GenericFlag struct { + *cli.GenericFlag + // Has unexported fields. +} + GenericFlag is the flag type that wraps cli.GenericFlag to allow for other + values to be specified + +func NewGenericFlag(fl *cli.GenericFlag) *GenericFlag + NewGenericFlag creates a new GenericFlag + +func (f *GenericFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + GenericFlag.Apply + +func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a generic value to the flagSet if required + +type InputSourceContext interface { + Source() string + + Int(name string) (int, error) + Duration(name string) (time.Duration, error) + Float64(name string) (float64, error) + String(name string) (string, error) + StringSlice(name string) ([]string, error) + IntSlice(name string) ([]int, error) + Generic(name string) (cli.Generic, error) + Bool(name string) (bool, error) + + // Has unexported methods. +} + InputSourceContext is an interface used to allow other input sources to be + implemented as needed. + + Source returns an identifier for the input source. In case of file source it + should return path to the file. + +func NewJSONSource(data []byte) (InputSourceContext, error) + NewJSONSource returns an InputSourceContext suitable for retrieving config + variables from raw JSON data. + +func NewJSONSourceFromFile(f string) (InputSourceContext, error) + NewJSONSourceFromFile returns an InputSourceContext suitable for retrieving + config variables from a file (or url) containing JSON data. + +func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) + NewJSONSourceFromReader returns an InputSourceContext suitable for + retrieving config variables from an io.Reader that returns JSON data. + +func NewTomlSourceFromFile(file string) (InputSourceContext, error) + NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. + +func NewYamlSourceFromFile(file string) (InputSourceContext, error) + NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. + +type Int64Flag struct { + *cli.Int64Flag + // Has unexported fields. +} + Int64Flag is the flag type that wraps cli.Int64Flag to allow for other + values to be specified + +func NewInt64Flag(fl *cli.Int64Flag) *Int64Flag + NewInt64Flag creates a new Int64Flag + +func (f *Int64Flag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Int64Flag.Apply + +type Int64SliceFlag struct { + *cli.Int64SliceFlag + // Has unexported fields. +} + Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow for + other values to be specified + +func NewInt64SliceFlag(fl *cli.Int64SliceFlag) *Int64SliceFlag + NewInt64SliceFlag creates a new Int64SliceFlag + +func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Int64SliceFlag.Apply + +type IntFlag struct { + *cli.IntFlag + // Has unexported fields. +} + IntFlag is the flag type that wraps cli.IntFlag to allow for other values to + be specified + +func NewIntFlag(fl *cli.IntFlag) *IntFlag + NewIntFlag creates a new IntFlag + +func (f *IntFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + IntFlag.Apply + +func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a int value to the flagSet if required + +type IntSliceFlag struct { + *cli.IntSliceFlag + // Has unexported fields. +} + IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow for other + values to be specified + +func NewIntSliceFlag(fl *cli.IntSliceFlag) *IntSliceFlag + NewIntSliceFlag creates a new IntSliceFlag + +func (f *IntSliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + IntSliceFlag.Apply + +func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a IntSlice value if required + +type MapInputSource struct { + // Has unexported fields. +} + MapInputSource implements InputSourceContext to return data from the map + that is loaded. + +func NewMapInputSource(file string, valueMap map[interface{}]interface{}) *MapInputSource + NewMapInputSource creates a new MapInputSource for implementing custom input + sources. + +func (fsm *MapInputSource) Bool(name string) (bool, error) + Bool returns an bool from the map otherwise returns false + +func (fsm *MapInputSource) Duration(name string) (time.Duration, error) + Duration returns a duration from the map if it exists otherwise returns 0 + +func (fsm *MapInputSource) Float64(name string) (float64, error) + Float64 returns an float64 from the map if it exists otherwise returns 0 + +func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) + Generic returns an cli.Generic from the map if it exists otherwise returns + nil + +func (fsm *MapInputSource) Int(name string) (int, error) + Int returns an int from the map if it exists otherwise returns 0 + +func (fsm *MapInputSource) IntSlice(name string) ([]int, error) + IntSlice returns an []int from the map if it exists otherwise returns nil + +func (fsm *MapInputSource) Source() string + Source returns the path of the source file + +func (fsm *MapInputSource) String(name string) (string, error) + String returns a string from the map if it exists otherwise returns an empty + string + +func (fsm *MapInputSource) StringSlice(name string) ([]string, error) + StringSlice returns an []string from the map if it exists otherwise returns + nil + +type PathFlag struct { + *cli.PathFlag + // Has unexported fields. +} + PathFlag is the flag type that wraps cli.PathFlag to allow for other values + to be specified + +func NewPathFlag(fl *cli.PathFlag) *PathFlag + NewPathFlag creates a new PathFlag + +func (f *PathFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + PathFlag.Apply + +func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Path value to the flagSet if required + +type StringFlag struct { + *cli.StringFlag + // Has unexported fields. +} + StringFlag is the flag type that wraps cli.StringFlag to allow for other + values to be specified + +func NewStringFlag(fl *cli.StringFlag) *StringFlag + NewStringFlag creates a new StringFlag + +func (f *StringFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + StringFlag.Apply + +func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a String value to the flagSet if required + +type StringSliceFlag struct { + *cli.StringSliceFlag + // Has unexported fields. +} + StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow for + other values to be specified + +func NewStringSliceFlag(fl *cli.StringSliceFlag) *StringSliceFlag + NewStringSliceFlag creates a new StringSliceFlag + +func (f *StringSliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + StringSliceFlag.Apply + +func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a StringSlice value to the flagSet if required + +type Uint64Flag struct { + *cli.Uint64Flag + // Has unexported fields. +} + Uint64Flag is the flag type that wraps cli.Uint64Flag to allow for other + values to be specified + +func NewUint64Flag(fl *cli.Uint64Flag) *Uint64Flag + NewUint64Flag creates a new Uint64Flag + +func (f *Uint64Flag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Uint64Flag.Apply + +type UintFlag struct { + *cli.UintFlag + // Has unexported fields. +} + UintFlag is the flag type that wraps cli.UintFlag to allow for other values + to be specified + +func NewUintFlag(fl *cli.UintFlag) *UintFlag + NewUintFlag creates a new UintFlag + +func (f *UintFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + UintFlag.Apply + diff --git a/internal/build/build.go b/internal/build/build.go index 0e9391b..929ab0f 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -10,11 +10,29 @@ import ( "math" "os" "os/exec" + "path/filepath" "strings" "github.com/urfave/cli/v2" ) +const ( + badNewsEmoji = "🚨" + goodNewsEmoji = "✨" + checksPassedEmoji = "✅" + + v2diffWarning = ` +# The unified diff above indicates that the public API surface area +# has changed. If you feel that the changes are acceptable and adhere +# to the semantic versioning promise of the v2.x series described in +# docs/CONTRIBUTING.md, please run the following command to promote +# the current go docs: +# +# make v2approve +# +` +) + func main() { top, err := func() (string, error) { if v, err := sh("git", "rev-parse", "--show-toplevel"); err == nil { @@ -57,6 +75,17 @@ func main() { Name: "generate", Action: GenerateActionFunc, }, + { + Name: "v2diff", + Flags: []cli.Flag{ + &cli.BoolFlag{Name: "color", Value: false}, + }, + Action: V2Diff, + }, + { + Name: "v2approve", + Action: V2Approve, + }, } app.Flags = []cli.Flag{ &cli.StringFlag{ @@ -206,9 +235,6 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { helloSourceFilePath = "./internal/example-hello-world/example-hello-world.go" helloBuiltFilePath = "./internal/example-hello-world/built-example" desiredMaxBinarySize = 2.2 - badNewsEmoji = "🚨" - goodNewsEmoji = "✨" - checksPassedEmoji = "✅" mbStringFormatter = "%.1fMB" ) @@ -290,9 +316,68 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) { } func GenerateActionFunc(cCtx *cli.Context) error { + top := cCtx.Path("top") + + cliDocs, err := sh("go", "doc", "-all", top) + if err != nil { + return err + } + + altsrcDocs, err := sh("go", "doc", "-all", filepath.Join(top, "altsrc")) + if err != nil { + return err + } + + if err := os.WriteFile( + filepath.Join(top, "godoc-current.txt"), + []byte(cliDocs+altsrcDocs), + 0644, + ); err != nil { + return err + } + return runCmd("go", "generate", cCtx.Path("top")+"/...") } +func V2Diff(cCtx *cli.Context) error { + os.Chdir(cCtx.Path("top")) + + err := runCmd( + "diff", + "--ignore-all-space", + "--minimal", + "--color="+func() string { + if cCtx.Bool("color") { + return "always" + } + return "auto" + }(), + "--unified", + "--label=a/godoc", + filepath.Join("testdata", "godoc-v2.x.txt"), + "--label=b/godoc", + "godoc-current.txt", + ) + + if err != nil { + fmt.Printf("# %v ---> Hey! <---\n", badNewsEmoji) + fmt.Println(strings.TrimSpace(v2diffWarning)) + } + + return err +} + +func V2Approve(cCtx *cli.Context) error { + top := cCtx.Path("top") + + return runCmd( + "cp", + "-v", + filepath.Join(top, "godoc-current.txt"), + filepath.Join(top, "testdata", "godoc-v2.x.txt"), + ) +} + func getSize(sourcePath string, builtPath string, tags string) (size int64, err error) { // build example binary err = runCmd("go", "build", "-tags", tags, "-o", builtPath, "-ldflags", "-s -w", sourcePath) diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt new file mode 100644 index 0000000..1a9ab23 --- /dev/null +++ b/testdata/godoc-v2.x.txt @@ -0,0 +1,2090 @@ +package cli // import "github.com/urfave/cli/v2" + +Package cli provides a minimal framework for creating and organizing command +line Go applications. cli is designed to be easy to understand and write, +the most simple cli application can be written as follows: + + func main() { + (&cli.App{}).Run(os.Args) + } + +Of course this application does not do much, so let's make this an actual +application: + + func main() { + app := &cli.App{ + Name: "greet", + Usage: "say a greeting", + Action: func(c *cli.Context) error { + fmt.Println("Greetings") + return nil + }, + } + + app.Run(os.Args) + } + +VARIABLES + +var AppHelpTemplate = `NAME: + {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} + +USAGE: + {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + +VERSION: + {{.Version}}{{end}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description | nindent 3 | trim}}{{end}}{{if len .Authors}} + +AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: + {{range $index, $author := .Authors}}{{if $index}} + {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} + +COMMANDS:{{range .VisibleCategories}}{{if .Name}} + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + +GLOBAL OPTIONS: + {{range $index, $option := .VisibleFlags}}{{if $index}} + {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} + +COPYRIGHT: + {{.Copyright}}{{end}} +` + AppHelpTemplate is the text template for the Default help topic. cli.go uses + text/template to render templates. You can render custom help text by + setting this variable. + +var CommandHelpTemplate = `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} + +CATEGORY: + {{.Category}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description | nindent 3 | trim}}{{end}}{{if .VisibleFlags}} + +OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +` + CommandHelpTemplate is the text template for the command help topic. cli.go + uses text/template to render templates. You can render custom help text by + setting this variable. + +var ErrWriter io.Writer = os.Stderr + ErrWriter is used to write errors to the user. This can be anything + implementing the io.Writer interface and defaults to os.Stderr. + +var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion + +function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' + for i in (commandline -opc) + if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} + return 1 + end + end + return 0 +end + +{{ range $v := .Completions }}{{ $v }} +{{ end }}` +var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }} + +{{end}}# NAME + +{{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} + +# SYNOPSIS + +{{ .App.Name }} +{{ if .SynopsisArgs }} +` + "```" + ` +{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` +{{ end }}{{ if .App.Description }} +# DESCRIPTION + +{{ .App.Description }} +{{ end }} +**Usage**: + +` + "```" + `{{ if .App.UsageText }} +{{ .App.UsageText }} +{{ else }} +{{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +{{ end }}` + "```" + ` +{{ if .GlobalArgs }} +# GLOBAL OPTIONS +{{ range $v := .GlobalArgs }} +{{ $v }}{{ end }} +{{ end }}{{ if .Commands }} +# COMMANDS +{{ range $v := .Commands }} +{{ $v }}{{ end }}{{ end }}` +var OsExiter = os.Exit + OsExiter is the function used when the app exits. If not set defaults to + os.Exit. + +var SubcommandHelpTemplate = `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} + +DESCRIPTION: + {{.Description | nindent 3 | trim}}{{end}} + +COMMANDS:{{range .VisibleCategories}}{{if .Name}} + {{.Name}}:{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} + {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} + +OPTIONS: + {{range .VisibleFlags}}{{.}} + {{end}}{{end}} +` + SubcommandHelpTemplate is the text template for the subcommand help topic. + cli.go uses text/template to render templates. You can render custom help + text by setting this variable. + +var VersionPrinter = printVersion + VersionPrinter prints the version for the App + +var HelpPrinter helpPrinter = printHelp + HelpPrinter is a function that writes the help output. If not set + explicitly, this calls HelpPrinterCustom using only the default template + functions. + + If custom logic for printing help is required, this function can be + overridden. If the ExtraInfo field is defined on an App, this function + should not be modified, as HelpPrinterCustom will be used directly in order + to capture the extra information. + +var HelpPrinterCustom helpPrinterCustom = printHelpCustom + HelpPrinterCustom is a function that writes the help output. It is used as + the default implementation of HelpPrinter, and may be called directly if the + ExtraInfo field is set on an App. + + +FUNCTIONS + +func DefaultAppComplete(cCtx *Context) + DefaultAppComplete prints the list of subcommands as the default app + completion method + +func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) +func FlagNames(name string, aliases []string) []string +func HandleAction(action interface{}, cCtx *Context) (err error) + HandleAction attempts to figure out which Action signature was used. If it's + an ActionFunc or a func with the legacy signature for Action, the func is + run! + +func HandleExitCoder(err error) + HandleExitCoder handles errors implementing ExitCoder by printing their + message and calling OsExiter with the given exit code. + + If the given error instead implements MultiError, each error will be checked + for the ExitCoder interface, and OsExiter will be called with the last exit + code found, or exit code 1 if no ExitCoder is found. + + This function is the default error-handling behavior for an App. + +func ShowAppHelp(cCtx *Context) error + ShowAppHelp is an action that displays the help. + +func ShowAppHelpAndExit(c *Context, exitCode int) + ShowAppHelpAndExit - Prints the list of subcommands for the app and exits + with exit code. + +func ShowCommandCompletions(ctx *Context, command string) + ShowCommandCompletions prints the custom completions for a given command + +func ShowCommandHelp(ctx *Context, command string) error + ShowCommandHelp prints help for the given command + +func ShowCommandHelpAndExit(c *Context, command string, code int) + ShowCommandHelpAndExit - exits with code after showing help + +func ShowCompletions(cCtx *Context) + ShowCompletions prints the lists of commands within a given context + +func ShowSubcommandHelp(cCtx *Context) error + ShowSubcommandHelp prints help for the given subcommand + +func ShowSubcommandHelpAndExit(c *Context, exitCode int) + ShowSubcommandHelpAndExit - Prints help for the given subcommand and exits + with exit code. + +func ShowVersion(cCtx *Context) + ShowVersion prints the version number of the App + + +TYPES + +type ActionFunc func(*Context) error + ActionFunc is the action to execute when no subcommands are specified + +type AfterFunc func(*Context) error + AfterFunc is an action to execute after any subcommands are run, but after + the subcommand has finished it is run even if Action() panics + +type App struct { + // The name of the program. Defaults to path.Base(os.Args[0]) + Name string + // Full name of command for help, defaults to Name + HelpName string + // Description of the program. + Usage string + // Text to override the USAGE section of help + UsageText string + // Description of the program argument format. + ArgsUsage string + // Version of the program + Version string + // Description of the program + Description string + // List of commands to execute + Commands []*Command + // List of flags to parse + Flags []Flag + // Boolean to enable bash completion commands + EnableBashCompletion bool + // Boolean to hide built-in help command and help flag + HideHelp bool + // Boolean to hide built-in help command but keep help flag. + // Ignored if HideHelp is true. + HideHelpCommand bool + // Boolean to hide built-in version flag and the VERSION section of help + HideVersion bool + + // An action to execute when the shell completion flag is set + BashComplete BashCompleteFunc + // An action to execute before any subcommands are run, but after the context is ready + // If a non-nil error is returned, no subcommands are run + Before BeforeFunc + // An action to execute after any subcommands are run, but after the subcommand has finished + // It is run even if Action() panics + After AfterFunc + // The action to execute when no subcommands are specified + Action ActionFunc + // Execute this function if the proper command cannot be found + CommandNotFound CommandNotFoundFunc + // Execute this function if a usage error occurs + OnUsageError OnUsageErrorFunc + // Compilation date + Compiled time.Time + // List of all authors who contributed + Authors []*Author + // Copyright of the binary if any + Copyright string + // Reader reader to write input to (useful for tests) + Reader io.Reader + // Writer writer to write output to + Writer io.Writer + // ErrWriter writes error output + ErrWriter io.Writer + // ExitErrHandler processes any error encountered while running an App before + // it is returned to the caller. If no function is provided, HandleExitCoder + // is used as the default behavior. + ExitErrHandler ExitErrHandlerFunc + // Other custom info + Metadata map[string]interface{} + // Carries a function which returns app specific info. + ExtraInfo func() map[string]string + // CustomAppHelpTemplate the text template for app help topic. + // cli.go uses text/template to render templates. You can + // render custom help text by setting this variable. + CustomAppHelpTemplate string + // Boolean to enable short-option handling so user can combine several + // single-character bool arguments into one + // i.e. foobar -o -v -> foobar -ov + UseShortOptionHandling bool + + // Has unexported fields. +} + App is the main structure of a cli application. It is recommended that an + app be created with the cli.NewApp() function + +func NewApp() *App + NewApp creates a new cli Application with some reasonable defaults for Name, + Usage, Version and Action. + +func (a *App) Command(name string) *Command + Command returns the named command on App. Returns nil if the command does + not exist + +func (a *App) Run(arguments []string) (err error) + Run is the entry point to the cli app. Parses the arguments slice and routes + to the proper flag/args combination + +func (a *App) RunAndExitOnError() + RunAndExitOnError calls .Run() and exits non-zero if an error was returned + + Deprecated: instead you should return an error that fulfills cli.ExitCoder + to cli.App.Run. This will cause the application to exit with the given error + code in the cli.ExitCoder + +func (a *App) RunAsSubcommand(ctx *Context) (err error) + RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() + to generate command-specific flags + +func (a *App) RunContext(ctx context.Context, arguments []string) (err error) + RunContext is like Run except it takes a Context that will be passed to its + commands and sub-commands. Through this, you can propagate timeouts and + cancellation requests + +func (a *App) Setup() + Setup runs initialization code to ensure all data structures are ready for + `Run` or inspection prior to `Run`. It is internally called by `Run`, but + will return early if setup has already happened. + +func (a *App) ToFishCompletion() (string, error) + ToFishCompletion creates a fish completion string for the `*App` The + function errors if either parsing or writing of the string fails. + +func (a *App) ToMan() (string, error) + ToMan creates a man page string for the `*App` The function errors if either + parsing or writing of the string fails. + +func (a *App) ToManWithSection(sectionNumber int) (string, error) + ToMan creates a man page string with section number for the `*App` The + function errors if either parsing or writing of the string fails. + +func (a *App) ToMarkdown() (string, error) + ToMarkdown creates a markdown string for the `*App` The function errors if + either parsing or writing of the string fails. + +func (a *App) VisibleCategories() []CommandCategory + VisibleCategories returns a slice of categories and commands that are + Hidden=false + +func (a *App) VisibleCommands() []*Command + VisibleCommands returns a slice of the Commands with Hidden=false + +func (a *App) VisibleFlags() []Flag + VisibleFlags returns a slice of the Flags with Hidden=false + +type Args interface { + // Get returns the nth argument, or else a blank string + Get(n int) string + // First returns the first argument, or else a blank string + First() string + // Tail returns the rest of the arguments (not the first one) + // or else an empty string slice + Tail() []string + // Len returns the length of the wrapped slice + Len() int + // Present checks if there are any arguments present + Present() bool + // Slice returns a copy of the internal slice + Slice() []string +} + +type Author struct { + Name string // The Authors name + Email string // The Authors email +} + Author represents someone who has contributed to a cli project. + +func (a *Author) String() string + String makes Author comply to the Stringer interface, to allow an easy print + in the templating process + +type BashCompleteFunc func(*Context) + BashCompleteFunc is an action to execute when the shell completion flag is + set + +type BeforeFunc func(*Context) error + BeforeFunc is an action to execute before any subcommands are run, but after + the context is ready if a non-nil error is returned, no subcommands are run + +type BoolFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value bool + Destination *bool + + Aliases []string + EnvVars []string +} + BoolFlag is a flag with type bool + +func (f *BoolFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *BoolFlag) Get(ctx *Context) bool + Get returns the flag’s value in the given Context. + +func (f *BoolFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *BoolFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *BoolFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *BoolFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *BoolFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *BoolFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *BoolFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *BoolFlag) Names() []string + Names returns the names of the flag + +func (f *BoolFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *BoolFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Command struct { + // The name of the command + Name string + // A list of aliases for the command + Aliases []string + // A short description of the usage of this command + Usage string + // Custom text to show on USAGE section of help + UsageText string + // A longer explanation of how the command works + Description string + // A short description of the arguments of this command + ArgsUsage string + // The category the command is part of + Category string + // The function to call when checking for bash command completions + BashComplete BashCompleteFunc + // An action to execute before any sub-subcommands are run, but after the context is ready + // If a non-nil error is returned, no sub-subcommands are run + Before BeforeFunc + // An action to execute after any subcommands are run, but after the subcommand has finished + // It is run even if Action() panics + After AfterFunc + // The function to call when this command is invoked + Action ActionFunc + // Execute this function if a usage error occurs. + OnUsageError OnUsageErrorFunc + // List of child commands + Subcommands []*Command + // List of flags to parse + Flags []Flag + // Treat all flags as normal arguments if true + SkipFlagParsing bool + // Boolean to hide built-in help command and help flag + HideHelp bool + // Boolean to hide built-in help command but keep help flag + // Ignored if HideHelp is true. + HideHelpCommand bool + // Boolean to hide this command from help or completion + Hidden bool + // Boolean to enable short-option handling so user can combine several + // single-character bool arguments into one + // i.e. foobar -o -v -> foobar -ov + UseShortOptionHandling bool + + // Full name of command for help, defaults to full command name, including parent commands. + HelpName string + + // CustomHelpTemplate the text template for the command help topic. + // cli.go uses text/template to render templates. You can + // render custom help text by setting this variable. + CustomHelpTemplate string + // Has unexported fields. +} + Command is a subcommand for a cli.App. + +func (c *Command) FullName() string + FullName returns the full name of the command. For subcommands this ensures + that parent commands are part of the command path + +func (c *Command) HasName(name string) bool + HasName returns true if Command.Name matches given name + +func (c *Command) Names() []string + Names returns the names including short names and aliases. + +func (c *Command) Run(ctx *Context) (err error) + Run invokes the command given the context, parses ctx.Args() to generate + command-specific flags + +func (c *Command) VisibleFlags() []Flag + VisibleFlags returns a slice of the Flags with Hidden=false + +type CommandCategories interface { + // AddCommand adds a command to a category, creating a new category if necessary. + AddCommand(category string, command *Command) + // categories returns a copy of the category slice + Categories() []CommandCategory +} + CommandCategories interface allows for category manipulation + +type CommandCategory interface { + // Name returns the category name string + Name() string + // VisibleCommands returns a slice of the Commands with Hidden=false + VisibleCommands() []*Command +} + CommandCategory is a category containing commands. + +type CommandNotFoundFunc func(*Context, string) + CommandNotFoundFunc is executed if the proper command cannot be found + +type Commands []*Command + +type CommandsByName []*Command + +func (c CommandsByName) Len() int + +func (c CommandsByName) Less(i, j int) bool + +func (c CommandsByName) Swap(i, j int) + +type Context struct { + context.Context + App *App + Command *Command + + // Has unexported fields. +} + Context is a type that is passed through to each Handler action in a cli + application. Context can be used to retrieve context-specific args and + parsed command-line options. + +func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context + NewContext creates a new context. For use in when invoking an App or Command + action. + +func (cCtx *Context) Args() Args + Args returns the command line arguments associated with the context. + +func (cCtx *Context) Bool(name string) bool + Bool looks up the value of a local BoolFlag, returns false if not found + +func (cCtx *Context) Duration(name string) time.Duration + Duration looks up the value of a local DurationFlag, returns 0 if not found + +func (cCtx *Context) FlagNames() []string + FlagNames returns a slice of flag names used by the this context and all of + its parent contexts. + +func (cCtx *Context) Float64(name string) float64 + Float64 looks up the value of a local Float64Flag, returns 0 if not found + +func (cCtx *Context) Float64Slice(name string) []float64 + Float64Slice looks up the value of a local Float64SliceFlag, returns nil if + not found + +func (cCtx *Context) Generic(name string) interface{} + Generic looks up the value of a local GenericFlag, returns nil if not found + +func (cCtx *Context) Int(name string) int + Int looks up the value of a local IntFlag, returns 0 if not found + +func (cCtx *Context) Int64(name string) int64 + Int64 looks up the value of a local Int64Flag, returns 0 if not found + +func (cCtx *Context) Int64Slice(name string) []int64 + Int64Slice looks up the value of a local Int64SliceFlag, returns nil if not + found + +func (cCtx *Context) IntSlice(name string) []int + IntSlice looks up the value of a local IntSliceFlag, returns nil if not + found + +func (cCtx *Context) IsSet(name string) bool + IsSet determines if the flag was actually set + +func (cCtx *Context) Lineage() []*Context + Lineage returns *this* context and all of its ancestor contexts in order + from child to parent + +func (cCtx *Context) LocalFlagNames() []string + LocalFlagNames returns a slice of flag names used in this context. + +func (cCtx *Context) NArg() int + NArg returns the number of the command line arguments. + +func (cCtx *Context) NumFlags() int + NumFlags returns the number of flags set + +func (cCtx *Context) Path(name string) string + Path looks up the value of a local PathFlag, returns "" if not found + +func (cCtx *Context) Set(name, value string) error + Set sets a context flag to a value. + +func (cCtx *Context) String(name string) string + String looks up the value of a local StringFlag, returns "" if not found + +func (cCtx *Context) StringSlice(name string) []string + StringSlice looks up the value of a local StringSliceFlag, returns nil if + not found + +func (cCtx *Context) Timestamp(name string) *time.Time + Timestamp gets the timestamp from a flag name + +func (cCtx *Context) Uint(name string) uint + Uint looks up the value of a local UintFlag, returns 0 if not found + +func (cCtx *Context) Uint64(name string) uint64 + Uint64 looks up the value of a local Uint64Flag, returns 0 if not found + +func (cCtx *Context) Value(name string) interface{} + Value returns the value of the flag corresponding to `name` + +type DocGenerationFlag interface { + Flag + + // TakesValue returns true if the flag takes a value, otherwise false + TakesValue() bool + + // GetUsage returns the usage string for the flag + GetUsage() string + + // GetValue returns the flags value as string representation and an empty + // string if the flag takes no value at all. + GetValue() string + + // GetDefaultText returns the default text for this flag + GetDefaultText() string + + // GetEnvVars returns the env vars for this flag + GetEnvVars() []string +} + DocGenerationFlag is an interface that allows documentation generation for + the flag + +type DurationFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value time.Duration + Destination *time.Duration + + Aliases []string + EnvVars []string +} + DurationFlag is a flag with type time.Duration + +func (f *DurationFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *DurationFlag) Get(ctx *Context) time.Duration + Get returns the flag’s value in the given Context. + +func (f *DurationFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *DurationFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *DurationFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *DurationFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *DurationFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *DurationFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *DurationFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *DurationFlag) Names() []string + Names returns the names of the flag + +func (f *DurationFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *DurationFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type ErrorFormatter interface { + Format(s fmt.State, verb rune) +} + ErrorFormatter is the interface that will suitably format the error output + +type ExitCoder interface { + error + ExitCode() int +} + ExitCoder is the interface checked by `App` and `Command` for a custom exit + code + +func Exit(message interface{}, exitCode int) ExitCoder + Exit wraps a message and exit code into an error, which by default is + handled with a call to os.Exit during default error handling. + + This is the simplest way to trigger a non-zero exit code for an App without + having to call os.Exit manually. During testing, this behavior can be + avoided by overiding the ExitErrHandler function on an App or the + package-global OsExiter function. + +func NewExitError(message interface{}, exitCode int) ExitCoder + NewExitError calls Exit to create a new ExitCoder. + + Deprecated: This function is a duplicate of Exit and will eventually be + removed. + +type ExitErrHandlerFunc func(cCtx *Context, err error) + ExitErrHandlerFunc is executed if provided in order to handle exitError + values returned by Actions and Before/After functions. + +type Flag interface { + fmt.Stringer + // Apply Flag settings to the given flag set + Apply(*flag.FlagSet) error + Names() []string + IsSet() bool +} + Flag is a common interface related to parsing flags in cli. For more + advanced flag parsing techniques, it is recommended that this interface be + implemented. + +var BashCompletionFlag Flag = &BoolFlag{ + Name: "generate-bash-completion", + Hidden: true, +} + BashCompletionFlag enables bash-completion for all commands and subcommands + +var HelpFlag Flag = &BoolFlag{ + Name: "help", + Aliases: []string{"h"}, + Usage: "show help", +} + HelpFlag prints the help for all commands and subcommands. Set to nil to + disable the flag. The subcommand will still be added unless HideHelp or + HideHelpCommand is set to true. + +var VersionFlag Flag = &BoolFlag{ + Name: "version", + Aliases: []string{"v"}, + Usage: "print the version", +} + VersionFlag prints the version for the application + +type FlagEnvHintFunc func(envVars []string, str string) string + FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help + with the environment variable details. + +var FlagEnvHinter FlagEnvHintFunc = withEnvHint + FlagEnvHinter annotates flag help message with the environment variable + details. This is used by the default FlagStringer. + +type FlagFileHintFunc func(filePath, str string) string + FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help + with the file path details. + +var FlagFileHinter FlagFileHintFunc = withFileHint + FlagFileHinter annotates flag help message with the environment variable + details. This is used by the default FlagStringer. + +type FlagNamePrefixFunc func(fullName []string, placeholder string) string + FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix + text for a flag's full name. + +var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames + FlagNamePrefixer converts a full flag name and its placeholder into the help + message flag prefix. This is used by the default FlagStringer. + +type FlagStringFunc func(Flag) string + FlagStringFunc is used by the help generation to display a flag, which is + expected to be a single line. + +var FlagStringer FlagStringFunc = stringifyFlag + FlagStringer converts a flag definition to a string. This is used by help to + display a flag. + +type FlagsByName []Flag + FlagsByName is a slice of Flag. + +func (f FlagsByName) Len() int + +func (f FlagsByName) Less(i, j int) bool + +func (f FlagsByName) Swap(i, j int) + +type Float64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value float64 + Destination *float64 + + Aliases []string + EnvVars []string +} + Float64Flag is a flag with type float64 + +func (f *Float64Flag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Float64Flag) Get(ctx *Context) float64 + Get returns the flag’s value in the given Context. + +func (f *Float64Flag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Float64Flag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Float64Flag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Float64Flag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Float64Flag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Float64Flag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Float64Flag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Float64Flag) Names() []string + Names returns the names of the flag + +func (f *Float64Flag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Float64Flag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Float64Slice struct { + // Has unexported fields. +} + Float64Slice wraps []float64 to satisfy flag.Value + +func NewFloat64Slice(defaults ...float64) *Float64Slice + NewFloat64Slice makes a *Float64Slice with default values + +func (f *Float64Slice) Get() interface{} + Get returns the slice of float64s set by this flag + +func (f *Float64Slice) Serialize() string + Serialize allows Float64Slice to fulfill Serializer + +func (f *Float64Slice) Set(value string) error + Set parses the value into a float64 and appends it to the list of values + +func (f *Float64Slice) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Float64Slice) Value() []float64 + Value returns the slice of float64s set by this flag + +type Float64SliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Float64Slice + Destination *Float64Slice + + Aliases []string + EnvVars []string +} + Float64SliceFlag is a flag with type Float64Slice + +func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Float64SliceFlag) Get(ctx *Context) []float64 + Get returns the flag’s value in the given Context. + +func (f *Float64SliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Float64SliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Float64SliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Float64SliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Float64SliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Float64SliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Float64SliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Float64SliceFlag) Names() []string + Names returns the names of the flag + +func (f *Float64SliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Float64SliceFlag) TakesValue() bool + TakesValue returns true if the flag takes a value, otherwise false + +type Generic interface { + Set(value string) error + String() string +} + Generic is a generic parseable type identified by a specific flag + +type GenericFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value Generic + Destination *Generic + + Aliases []string + EnvVars []string + + TakesFile bool +} + GenericFlag is a flag with type Generic + +func (f GenericFlag) Apply(set *flag.FlagSet) error + 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) Get(ctx *Context) interface{} + Get returns the flag’s value in the given Context. + +func (f *GenericFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *GenericFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *GenericFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *GenericFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *GenericFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *GenericFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *GenericFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *GenericFlag) Names() []string + Names returns the names of the flag + +func (f *GenericFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *GenericFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Int64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value int64 + Destination *int64 + + Aliases []string + EnvVars []string +} + Int64Flag is a flag with type int64 + +func (f *Int64Flag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Int64Flag) Get(ctx *Context) int64 + Get returns the flag’s value in the given Context. + +func (f *Int64Flag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Int64Flag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Int64Flag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Int64Flag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Int64Flag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Int64Flag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Int64Flag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Int64Flag) Names() []string + Names returns the names of the flag + +func (f *Int64Flag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Int64Flag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Int64Slice struct { + // Has unexported fields. +} + Int64Slice wraps []int64 to satisfy flag.Value + +func NewInt64Slice(defaults ...int64) *Int64Slice + NewInt64Slice makes an *Int64Slice with default values + +func (i *Int64Slice) Get() interface{} + Get returns the slice of ints set by this flag + +func (i *Int64Slice) Serialize() string + Serialize allows Int64Slice to fulfill Serializer + +func (i *Int64Slice) Set(value string) error + Set parses the value into an integer and appends it to the list of values + +func (i *Int64Slice) String() string + String returns a readable representation of this value (for usage defaults) + +func (i *Int64Slice) Value() []int64 + Value returns the slice of ints set by this flag + +type Int64SliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Int64Slice + Destination *Int64Slice + + Aliases []string + EnvVars []string +} + Int64SliceFlag is a flag with type Int64Slice + +func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Int64SliceFlag) Get(ctx *Context) []int64 + Get returns the flag’s value in the given Context. + +func (f *Int64SliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Int64SliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f Int64SliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Int64SliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Int64SliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Int64SliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Int64SliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Int64SliceFlag) Names() []string + Names returns the names of the flag + +func (f *Int64SliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Int64SliceFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type IntFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value int + Destination *int + + Aliases []string + EnvVars []string +} + IntFlag is a flag with type int + +func (f *IntFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *IntFlag) Get(ctx *Context) int + Get returns the flag’s value in the given Context. + +func (f *IntFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *IntFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *IntFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *IntFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *IntFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *IntFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *IntFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *IntFlag) Names() []string + Names returns the names of the flag + +func (f *IntFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *IntFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type IntSlice struct { + // Has unexported fields. +} + IntSlice wraps []int to satisfy flag.Value + +func NewIntSlice(defaults ...int) *IntSlice + NewIntSlice makes an *IntSlice with default values + +func (i *IntSlice) Get() interface{} + Get returns the slice of ints set by this flag + +func (i *IntSlice) Serialize() string + Serialize allows IntSlice to fulfill Serializer + +func (i *IntSlice) Set(value string) error + Set parses the value into an integer and appends it to the list of values + +func (i *IntSlice) SetInt(value int) + TODO: Consistently have specific Set function for Int64 and Float64 ? SetInt + directly adds an integer to the list of values + +func (i *IntSlice) String() string + String returns a readable representation of this value (for usage defaults) + +func (i *IntSlice) Value() []int + Value returns the slice of ints set by this flag + +type IntSliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *IntSlice + Destination *IntSlice + + Aliases []string + EnvVars []string +} + IntSliceFlag is a flag with type IntSlice + +func (f *IntSliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *IntSliceFlag) Get(ctx *Context) []int + Get returns the flag’s value in the given Context. + +func (f *IntSliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *IntSliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f IntSliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *IntSliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *IntSliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *IntSliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *IntSliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *IntSliceFlag) Names() []string + Names returns the names of the flag + +func (f *IntSliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *IntSliceFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type MultiError interface { + error + Errors() []error +} + MultiError is an error that wraps multiple errors. + +type OnUsageErrorFunc func(cCtx *Context, err error, isSubcommand bool) error + OnUsageErrorFunc is executed if a usage error occurs. This is useful for + displaying customized usage error messages. This function is able to replace + the original error messages. If this function is not set, the "Incorrect + usage" is displayed and the execution is interrupted. + +type Path = string + +type PathFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value Path + Destination *Path + + Aliases []string + EnvVars []string + + TakesFile bool +} + PathFlag is a flag with type Path + +func (f *PathFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *PathFlag) Get(ctx *Context) string + Get returns the flag’s value in the given Context. + +func (f *PathFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *PathFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *PathFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *PathFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *PathFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *PathFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *PathFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *PathFlag) Names() []string + Names returns the names of the flag + +func (f *PathFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *PathFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type RequiredFlag interface { + Flag + + IsRequired() bool +} + RequiredFlag is an interface that allows us to mark flags as required it + allows flags required flags to be backwards compatible with the Flag + interface + +type Serializer interface { + Serialize() string +} + Serializer is used to circumvent the limitations of flag.FlagSet.Set + +type StringFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value string + Destination *string + + Aliases []string + EnvVars []string + + TakesFile bool +} + StringFlag is a flag with type string + +func (f *StringFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *StringFlag) Get(ctx *Context) string + Get returns the flag’s value in the given Context. + +func (f *StringFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *StringFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *StringFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *StringFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *StringFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *StringFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *StringFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *StringFlag) Names() []string + Names returns the names of the flag + +func (f *StringFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *StringFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type StringSlice struct { + // Has unexported fields. +} + StringSlice wraps a []string to satisfy flag.Value + +func NewStringSlice(defaults ...string) *StringSlice + NewStringSlice creates a *StringSlice with default values + +func (s *StringSlice) Get() interface{} + Get returns the slice of strings set by this flag + +func (s *StringSlice) Serialize() string + Serialize allows StringSlice to fulfill Serializer + +func (s *StringSlice) Set(value string) error + Set appends the string value to the list of values + +func (s *StringSlice) String() string + String returns a readable representation of this value (for usage defaults) + +func (s *StringSlice) Value() []string + Value returns the slice of strings set by this flag + +type StringSliceFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *StringSlice + Destination *StringSlice + + Aliases []string + EnvVars []string + + TakesFile bool +} + StringSliceFlag is a flag with type StringSlice + +func (f *StringSliceFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *StringSliceFlag) Get(ctx *Context) []string + Get returns the flag’s value in the given Context. + +func (f *StringSliceFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *StringSliceFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *StringSliceFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *StringSliceFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *StringSliceFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *StringSliceFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *StringSliceFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *StringSliceFlag) Names() []string + Names returns the names of the flag + +func (f *StringSliceFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *StringSliceFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Timestamp struct { + // Has unexported fields. +} + Timestamp wrap to satisfy golang's flag interface. + +func NewTimestamp(timestamp time.Time) *Timestamp + Timestamp constructor + +func (t *Timestamp) Get() interface{} + Get returns the flag structure + +func (t *Timestamp) Set(value string) error + Parses the string value to timestamp + +func (t *Timestamp) SetLayout(layout string) + Set the timestamp string layout for future parsing + +func (t *Timestamp) SetTimestamp(value time.Time) + Set the timestamp value directly + +func (t *Timestamp) String() string + String returns a readable representation of this value (for usage defaults) + +func (t *Timestamp) Value() *time.Time + Value returns the timestamp value stored in the flag + +type TimestampFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value *Timestamp + Destination *Timestamp + + Aliases []string + EnvVars []string + + Layout string +} + TimestampFlag is a flag with type Timestamp + +func (f *TimestampFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *TimestampFlag) Get(ctx *Context) *time.Time + Get returns the flag’s value in the given Context. + +func (f *TimestampFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *TimestampFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *TimestampFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *TimestampFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *TimestampFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *TimestampFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *TimestampFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *TimestampFlag) Names() []string + Names returns the names of the flag + +func (f *TimestampFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *TimestampFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type Uint64Flag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value uint64 + Destination *uint64 + + Aliases []string + EnvVars []string +} + Uint64Flag is a flag with type uint64 + +func (f *Uint64Flag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *Uint64Flag) Get(ctx *Context) uint64 + Get returns the flag’s value in the given Context. + +func (f *Uint64Flag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *Uint64Flag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *Uint64Flag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *Uint64Flag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *Uint64Flag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *Uint64Flag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *Uint64Flag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *Uint64Flag) Names() []string + Names returns the names of the flag + +func (f *Uint64Flag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *Uint64Flag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type UintFlag struct { + Name string + + DefaultText string + FilePath string + Usage string + + Required bool + Hidden bool + HasBeenSet bool + + Value uint + Destination *uint + + Aliases []string + EnvVars []string +} + UintFlag is a flag with type uint + +func (f *UintFlag) Apply(set *flag.FlagSet) error + Apply populates the flag given the flag set and environment + +func (f *UintFlag) Get(ctx *Context) uint + Get returns the flag’s value in the given Context. + +func (f *UintFlag) GetDefaultText() string + GetDefaultText returns the default text for this flag + +func (f *UintFlag) GetEnvVars() []string + GetEnvVars returns the env vars for this flag + +func (f *UintFlag) GetUsage() string + GetUsage returns the usage string for the flag + +func (f *UintFlag) GetValue() string + GetValue returns the flags value as string representation and an empty + string if the flag takes no value at all. + +func (f *UintFlag) IsRequired() bool + IsRequired returns whether or not the flag is required + +func (f *UintFlag) IsSet() bool + IsSet returns whether or not the flag has been set through env or file + +func (f *UintFlag) IsVisible() bool + IsVisible returns true if the flag is not hidden, otherwise false + +func (f *UintFlag) Names() []string + Names returns the names of the flag + +func (f *UintFlag) String() string + String returns a readable representation of this value (for usage defaults) + +func (f *UintFlag) TakesValue() bool + TakesValue returns true of the flag takes a value, otherwise false + +type VisibleFlag interface { + Flag + + // IsVisible returns true if the flag is not hidden, otherwise false + IsVisible() bool +} + VisibleFlag is an interface that allows to check if a flag is visible + +package altsrc // import "github.com/urfave/cli/v2/altsrc" + + +FUNCTIONS + +func ApplyInputSourceValues(cCtx *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error + ApplyInputSourceValues iterates over all provided flags and executes + ApplyInputSourceValue on flags implementing the FlagInputSourceExtension + interface to initialize these flags to an alternate input source. + +func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc + InitInputSource is used to to setup an InputSourceContext on a cli.Command + Before method. It will create a new input source based on the func provided. + If there is no error it will then apply the new input source to any flags + that are supported by the input source + +func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(cCtx *cli.Context) (InputSourceContext, error)) cli.BeforeFunc + InitInputSourceWithContext is used to to setup an InputSourceContext on a + cli.Command Before method. It will create a new input source based on the + func provided with potentially using existing cli.Context values to + initialize itself. If there is no error it will then apply the new input + source to any flags that are supported by the input source + +func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) + NewJSONSourceFromFlagFunc returns a func that takes a cli.Context and + returns an InputSourceContext suitable for retrieving config variables from + a file containing JSON data with the file name defined by the given flag. + +func NewTomlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) + NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a + provided flag name and source context. + +func NewYamlSourceFromFlagFunc(flagFileName string) func(cCtx *cli.Context) (InputSourceContext, error) + NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a + provided flag name and source context. + + +TYPES + +type BoolFlag struct { + *cli.BoolFlag + // Has unexported fields. +} + BoolFlag is the flag type that wraps cli.BoolFlag to allow for other values + to be specified + +func NewBoolFlag(fl *cli.BoolFlag) *BoolFlag + NewBoolFlag creates a new BoolFlag + +func (f *BoolFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + BoolFlag.Apply + +func (f *BoolFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Bool value to the flagSet if required + +type DurationFlag struct { + *cli.DurationFlag + // Has unexported fields. +} + DurationFlag is the flag type that wraps cli.DurationFlag to allow for other + values to be specified + +func NewDurationFlag(fl *cli.DurationFlag) *DurationFlag + NewDurationFlag creates a new DurationFlag + +func (f *DurationFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + DurationFlag.Apply + +func (f *DurationFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Duration value to the flagSet if required + +type FlagInputSourceExtension interface { + cli.Flag + ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error +} + FlagInputSourceExtension is an extension interface of cli.Flag that allows a + value to be set on the existing parsed flags. + +type Float64Flag struct { + *cli.Float64Flag + // Has unexported fields. +} + Float64Flag is the flag type that wraps cli.Float64Flag to allow for other + values to be specified + +func NewFloat64Flag(fl *cli.Float64Flag) *Float64Flag + NewFloat64Flag creates a new Float64Flag + +func (f *Float64Flag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Float64Flag.Apply + +func (f *Float64Flag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Float64 value to the flagSet if required + +type Float64SliceFlag struct { + *cli.Float64SliceFlag + // Has unexported fields. +} + Float64SliceFlag is the flag type that wraps cli.Float64SliceFlag to allow + for other values to be specified + +func NewFloat64SliceFlag(fl *cli.Float64SliceFlag) *Float64SliceFlag + NewFloat64SliceFlag creates a new Float64SliceFlag + +func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Float64SliceFlag.Apply + +type GenericFlag struct { + *cli.GenericFlag + // Has unexported fields. +} + GenericFlag is the flag type that wraps cli.GenericFlag to allow for other + values to be specified + +func NewGenericFlag(fl *cli.GenericFlag) *GenericFlag + NewGenericFlag creates a new GenericFlag + +func (f *GenericFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + GenericFlag.Apply + +func (f *GenericFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a generic value to the flagSet if required + +type InputSourceContext interface { + Source() string + + Int(name string) (int, error) + Duration(name string) (time.Duration, error) + Float64(name string) (float64, error) + String(name string) (string, error) + StringSlice(name string) ([]string, error) + IntSlice(name string) ([]int, error) + Generic(name string) (cli.Generic, error) + Bool(name string) (bool, error) + + // Has unexported methods. +} + InputSourceContext is an interface used to allow other input sources to be + implemented as needed. + + Source returns an identifier for the input source. In case of file source it + should return path to the file. + +func NewJSONSource(data []byte) (InputSourceContext, error) + NewJSONSource returns an InputSourceContext suitable for retrieving config + variables from raw JSON data. + +func NewJSONSourceFromFile(f string) (InputSourceContext, error) + NewJSONSourceFromFile returns an InputSourceContext suitable for retrieving + config variables from a file (or url) containing JSON data. + +func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) + NewJSONSourceFromReader returns an InputSourceContext suitable for + retrieving config variables from an io.Reader that returns JSON data. + +func NewTomlSourceFromFile(file string) (InputSourceContext, error) + NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. + +func NewYamlSourceFromFile(file string) (InputSourceContext, error) + NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. + +type Int64Flag struct { + *cli.Int64Flag + // Has unexported fields. +} + Int64Flag is the flag type that wraps cli.Int64Flag to allow for other + values to be specified + +func NewInt64Flag(fl *cli.Int64Flag) *Int64Flag + NewInt64Flag creates a new Int64Flag + +func (f *Int64Flag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Int64Flag.Apply + +type Int64SliceFlag struct { + *cli.Int64SliceFlag + // Has unexported fields. +} + Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow for + other values to be specified + +func NewInt64SliceFlag(fl *cli.Int64SliceFlag) *Int64SliceFlag + NewInt64SliceFlag creates a new Int64SliceFlag + +func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Int64SliceFlag.Apply + +type IntFlag struct { + *cli.IntFlag + // Has unexported fields. +} + IntFlag is the flag type that wraps cli.IntFlag to allow for other values to + be specified + +func NewIntFlag(fl *cli.IntFlag) *IntFlag + NewIntFlag creates a new IntFlag + +func (f *IntFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + IntFlag.Apply + +func (f *IntFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a int value to the flagSet if required + +type IntSliceFlag struct { + *cli.IntSliceFlag + // Has unexported fields. +} + IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow for other + values to be specified + +func NewIntSliceFlag(fl *cli.IntSliceFlag) *IntSliceFlag + NewIntSliceFlag creates a new IntSliceFlag + +func (f *IntSliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + IntSliceFlag.Apply + +func (f *IntSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a IntSlice value if required + +type MapInputSource struct { + // Has unexported fields. +} + MapInputSource implements InputSourceContext to return data from the map + that is loaded. + +func NewMapInputSource(file string, valueMap map[interface{}]interface{}) *MapInputSource + NewMapInputSource creates a new MapInputSource for implementing custom input + sources. + +func (fsm *MapInputSource) Bool(name string) (bool, error) + Bool returns an bool from the map otherwise returns false + +func (fsm *MapInputSource) Duration(name string) (time.Duration, error) + Duration returns a duration from the map if it exists otherwise returns 0 + +func (fsm *MapInputSource) Float64(name string) (float64, error) + Float64 returns an float64 from the map if it exists otherwise returns 0 + +func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) + Generic returns an cli.Generic from the map if it exists otherwise returns + nil + +func (fsm *MapInputSource) Int(name string) (int, error) + Int returns an int from the map if it exists otherwise returns 0 + +func (fsm *MapInputSource) IntSlice(name string) ([]int, error) + IntSlice returns an []int from the map if it exists otherwise returns nil + +func (fsm *MapInputSource) Source() string + Source returns the path of the source file + +func (fsm *MapInputSource) String(name string) (string, error) + String returns a string from the map if it exists otherwise returns an empty + string + +func (fsm *MapInputSource) StringSlice(name string) ([]string, error) + StringSlice returns an []string from the map if it exists otherwise returns + nil + +type PathFlag struct { + *cli.PathFlag + // Has unexported fields. +} + PathFlag is the flag type that wraps cli.PathFlag to allow for other values + to be specified + +func NewPathFlag(fl *cli.PathFlag) *PathFlag + NewPathFlag creates a new PathFlag + +func (f *PathFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + PathFlag.Apply + +func (f *PathFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a Path value to the flagSet if required + +type StringFlag struct { + *cli.StringFlag + // Has unexported fields. +} + StringFlag is the flag type that wraps cli.StringFlag to allow for other + values to be specified + +func NewStringFlag(fl *cli.StringFlag) *StringFlag + NewStringFlag creates a new StringFlag + +func (f *StringFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + StringFlag.Apply + +func (f *StringFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a String value to the flagSet if required + +type StringSliceFlag struct { + *cli.StringSliceFlag + // Has unexported fields. +} + StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow for + other values to be specified + +func NewStringSliceFlag(fl *cli.StringSliceFlag) *StringSliceFlag + NewStringSliceFlag creates a new StringSliceFlag + +func (f *StringSliceFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + StringSliceFlag.Apply + +func (f *StringSliceFlag) ApplyInputSourceValue(cCtx *cli.Context, isc InputSourceContext) error + ApplyInputSourceValue applies a StringSlice value to the flagSet if required + +type Uint64Flag struct { + *cli.Uint64Flag + // Has unexported fields. +} + Uint64Flag is the flag type that wraps cli.Uint64Flag to allow for other + values to be specified + +func NewUint64Flag(fl *cli.Uint64Flag) *Uint64Flag + NewUint64Flag creates a new Uint64Flag + +func (f *Uint64Flag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + Uint64Flag.Apply + +type UintFlag struct { + *cli.UintFlag + // Has unexported fields. +} + UintFlag is the flag type that wraps cli.UintFlag to allow for other values + to be specified + +func NewUintFlag(fl *cli.UintFlag) *UintFlag + NewUintFlag creates a new UintFlag + +func (f *UintFlag) Apply(set *flag.FlagSet) error + Apply saves the flagSet for later usage calls, then calls the wrapped + UintFlag.Apply + From 2ac3904d122959569d8fb8b8a2136853237c7169 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Wed, 4 May 2022 23:11:05 -0400 Subject: [PATCH 44/45] Include `*` in documented flag value type when applicable --- internal/genflags/generated.gotmpl | 2 +- zz_generated.flags.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/genflags/generated.gotmpl b/internal/genflags/generated.gotmpl index 681a101..5d6f0dd 100644 --- a/internal/genflags/generated.gotmpl +++ b/internal/genflags/generated.gotmpl @@ -3,7 +3,7 @@ package {{.PackageName}} {{range .SortedFlagTypes}} -// {{.TypeName}} is a flag with type {{.GoType}} +// {{.TypeName}} is a flag with type {{if .ValuePointer}}*{{end}}{{.GoType}} type {{.TypeName}} struct { Name string diff --git a/zz_generated.flags.go b/zz_generated.flags.go index eb980bc..703e66a 100644 --- a/zz_generated.flags.go +++ b/zz_generated.flags.go @@ -4,7 +4,7 @@ package cli import "time" -// Float64SliceFlag is a flag with type Float64Slice +// Float64SliceFlag is a flag with type *Float64Slice type Float64SliceFlag struct { Name string @@ -69,7 +69,7 @@ func (f *GenericFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } -// Int64SliceFlag is a flag with type Int64Slice +// Int64SliceFlag is a flag with type *Int64Slice type Int64SliceFlag struct { Name string @@ -98,7 +98,7 @@ func (f *Int64SliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } -// IntSliceFlag is a flag with type IntSlice +// IntSliceFlag is a flag with type *IntSlice type IntSliceFlag struct { Name string @@ -163,7 +163,7 @@ func (f *PathFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } -// StringSliceFlag is a flag with type StringSlice +// StringSliceFlag is a flag with type *StringSlice type StringSliceFlag struct { Name string @@ -194,7 +194,7 @@ func (f *StringSliceFlag) Names() []string { return FlagNames(f.Name, f.Aliases) } -// TimestampFlag is a flag with type Timestamp +// TimestampFlag is a flag with type *Timestamp type TimestampFlag struct { Name string From f1834d176f97c38419b7b249bca96ec5c12e5aaf Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Wed, 4 May 2022 23:12:27 -0400 Subject: [PATCH 45/45] Update docs with corrected flag value --- godoc-current.txt | 10 +++++----- testdata/godoc-v2.x.txt | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/godoc-current.txt b/godoc-current.txt index 1a9ab23..1bddc54 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -934,7 +934,7 @@ type Float64SliceFlag struct { Aliases []string EnvVars []string } - Float64SliceFlag is a flag with type Float64Slice + Float64SliceFlag is a flag with type *Float64Slice func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1134,7 +1134,7 @@ type Int64SliceFlag struct { Aliases []string EnvVars []string } - Int64SliceFlag is a flag with type Int64Slice + Int64SliceFlag is a flag with type *Int64Slice func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1273,7 +1273,7 @@ type IntSliceFlag struct { Aliases []string EnvVars []string } - IntSliceFlag is a flag with type IntSlice + IntSliceFlag is a flag with type *IntSlice func (f *IntSliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1498,7 +1498,7 @@ type StringSliceFlag struct { TakesFile bool } - StringSliceFlag is a flag with type StringSlice + StringSliceFlag is a flag with type *StringSlice func (f *StringSliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1582,7 +1582,7 @@ type TimestampFlag struct { Layout string } - TimestampFlag is a flag with type Timestamp + TimestampFlag is a flag with type *Timestamp func (f *TimestampFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 1a9ab23..1bddc54 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -934,7 +934,7 @@ type Float64SliceFlag struct { Aliases []string EnvVars []string } - Float64SliceFlag is a flag with type Float64Slice + Float64SliceFlag is a flag with type *Float64Slice func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1134,7 +1134,7 @@ type Int64SliceFlag struct { Aliases []string EnvVars []string } - Int64SliceFlag is a flag with type Int64Slice + Int64SliceFlag is a flag with type *Int64Slice func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1273,7 +1273,7 @@ type IntSliceFlag struct { Aliases []string EnvVars []string } - IntSliceFlag is a flag with type IntSlice + IntSliceFlag is a flag with type *IntSlice func (f *IntSliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1498,7 +1498,7 @@ type StringSliceFlag struct { TakesFile bool } - StringSliceFlag is a flag with type StringSlice + StringSliceFlag is a flag with type *StringSlice func (f *StringSliceFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment @@ -1582,7 +1582,7 @@ type TimestampFlag struct { Layout string } - TimestampFlag is a flag with type Timestamp + TimestampFlag is a flag with type *Timestamp func (f *TimestampFlag) Apply(set *flag.FlagSet) error Apply populates the flag given the flag set and environment