From 4962423cbac126c7b298a25c42ef6a7ee6fa792a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Franc=CC=A7a=20dos=20Reis?= Date: Sat, 11 Jun 2016 15:22:07 -0700 Subject: [PATCH 01/29] Adding Int64Flag type and related tests --- flag.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ flag_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/flag.go b/flag.go index b087e25..a67f69c 100644 --- a/flag.go +++ b/flag.go @@ -420,6 +420,51 @@ func (f IntFlag) GetName() string { return f.Name } +// Int64Flag is a flag that takes a 64-bit integer +// Errors if the value provided cannot be parsed +type Int64Flag struct { + Name string + Value int64 + Usage string + EnvVar string + Destination *int64 + Hidden bool +} + +// String returns the usage +func (f Int64Flag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f Int64Flag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + if err == nil { + f.Value = envValInt + break + } + } + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Int64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Int64(name, f.Value, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f Int64Flag) GetName() string { + return f.Name +} + // DurationFlag is a flag that takes a duration specified in Go's duration // format: https://golang.org/pkg/time/#ParseDuration type DurationFlag struct { diff --git a/flag_test.go b/flag_test.go index e0df23b..32d57fb 100644 --- a/flag_test.go +++ b/flag_test.go @@ -162,6 +162,42 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { } } +var int64FlagTests = []struct { + name string + expected string +}{ + {"hats", "--hats value\t(default: 8589934592)"}, + {"H", "-H value\t(default: 8589934592)"}, +} + +func TestInt64FlagHelpOutput(t *testing.T) { + for _, test := range int64FlagTests { + flag := Int64Flag{Name: test.name, Value: 8589934592} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAR", "2") + for _, test := range int64FlagTests { + flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} + output := flag.String() + + expectedSuffix := " [$APP_BAR]" + if runtime.GOOS == "windows" { + expectedSuffix = " [%APP_BAR%]" + } + if !strings.HasSuffix(output, expectedSuffix) { + t.Errorf("%s does not end with"+expectedSuffix, output) + } + } +} + var durationFlagTests = []struct { name string expected string From 80d3d863d9dade84a6ade0729923e87310d84253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Franc=CC=A7a=20dos=20Reis?= Date: Sat, 11 Jun 2016 15:41:24 -0700 Subject: [PATCH 02/29] Adding Int64 and GlobalInt64 in context, plus related tests --- context.go | 26 ++++++++++++++++++++++++++ context_test.go | 19 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/context.go b/context.go index c342463..112dac3 100644 --- a/context.go +++ b/context.go @@ -31,6 +31,11 @@ func (c *Context) Int(name string) int { return lookupInt(name, c.flagSet) } +// Int64 looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Int64(name string) int64 { + return lookupInt64(name, c.flagSet) +} + // Duration looks up the value of a local time.Duration flag, returns 0 if no // time.Duration flag exists func (c *Context) Duration(name string) time.Duration { @@ -84,6 +89,14 @@ func (c *Context) GlobalInt(name string) int { return 0 } +// GlobalInt64 looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalInt64(name string) int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64(name, fs) + } + return 0 +} + // GlobalFloat64 looks up the value of a global float64 flag, returns float64(0) // if no float64 flag exists func (c *Context) GlobalFloat64(name string) float64 { @@ -316,6 +329,19 @@ func lookupInt(name string, set *flag.FlagSet) int { return 0 } +func lookupInt64(name string, set *flag.FlagSet) int64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseInt(f.Value.String(), 10, 64) + if err != nil { + return 0 + } + return val + } + + return 0 +} + func lookupDuration(name string, set *flag.FlagSet) time.Duration { f := set.Lookup(name) if f != nil { diff --git a/context_test.go b/context_test.go index 28d4884..7625c5b 100644 --- a/context_test.go +++ b/context_test.go @@ -9,17 +9,21 @@ import ( func TestNewContext(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") + set.Int("myflagInt64", 12, "doc") set.Float64("myflag64", float64(17), "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Int("myflag", 42, "doc") + globalSet.Int("myflagInt64", 42, "doc") globalSet.Float64("myflag64", float64(47), "doc") globalCtx := NewContext(nil, globalSet, nil) command := Command{Name: "mycommand"} c := NewContext(nil, set, globalCtx) c.Command = command expect(t, c.Int("myflag"), 12) + expect(t, c.Int64("myflagInt64"), int64(12)) expect(t, c.Float64("myflag64"), float64(17)) expect(t, c.GlobalInt("myflag"), 42) + expect(t, c.GlobalInt64("myflagInt64"), int64(42)) expect(t, c.GlobalFloat64("myflag64"), float64(47)) expect(t, c.Command.Name, "mycommand") } @@ -31,6 +35,13 @@ func TestContext_Int(t *testing.T) { expect(t, c.Int("myflag"), 12) } +func TestContext_Int64(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int64("myflagInt64", 12, "doc") + c := NewContext(nil, set, nil) + expect(t, c.Int64("myflagInt64"), int64(12)) +} + func TestContext_GlobalInt(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") @@ -39,6 +50,14 @@ func TestContext_GlobalInt(t *testing.T) { expect(t, c.GlobalInt("nope"), 0) } +func TestContext_GlobalInt64(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int64("myflagInt64", 12, "doc") + c := NewContext(nil, set, nil) + expect(t, c.GlobalInt64("myflagInt64"), int64(12)) + expect(t, c.GlobalInt64("nope"), int64(0)) +} + func TestContext_Float64(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Float64("myflag", float64(17), "doc") From 5c7cca7f1682ac72d6a2481e004f6bfa636ad260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Franc=CC=A7a=20dos=20Reis?= Date: Sat, 11 Jun 2016 21:54:33 -0700 Subject: [PATCH 03/29] Adding Int64Slice related stuff in flag.go and context.go, and related tests --- context.go | 25 +++++++++++++ flag.go | 85 +++++++++++++++++++++++++++++++++++++++++++ flag_test.go | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) diff --git a/context.go b/context.go index 112dac3..d6d4a19 100644 --- a/context.go +++ b/context.go @@ -75,6 +75,12 @@ func (c *Context) IntSlice(name string) []int { return lookupIntSlice(name, c.flagSet) } +// Int64Slice looks up the value of a local int slice flag, returns nil if no int +// slice flag exists +func (c *Context) Int64Slice(name string) []int64 { + return lookupInt64Slice(name, c.flagSet) +} + // Generic looks up the value of a local generic flag, returns nil if no generic // flag exists func (c *Context) Generic(name string) interface{} { @@ -160,6 +166,15 @@ func (c *Context) GlobalIntSlice(name string) []int { return nil } +// GlobalInt64Slice looks up the value of a global int slice flag, returns nil if +// no int slice flag exists +func (c *Context) GlobalInt64Slice(name string) []int64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt64Slice(name, fs) + } + return nil +} + // GlobalGeneric looks up the value of a global generic flag, returns nil if no // generic flag exists func (c *Context) GlobalGeneric(name string) interface{} { @@ -396,6 +411,16 @@ func lookupIntSlice(name string, set *flag.FlagSet) []int { return nil } +func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*Int64Slice)).Value() + + } + + return nil +} + func lookupGeneric(name string, set *flag.FlagSet) interface{} { f := set.Lookup(name) if f != nil { diff --git a/flag.go b/flag.go index a67f69c..abeff90 100644 --- a/flag.go +++ b/flag.go @@ -245,6 +245,77 @@ func (f IntSliceFlag) GetName() string { return f.Name } +// Int64Slice is an opaque type for []int to satisfy flag.Value +type Int64Slice []int64 + +// Set parses the value into an integer and appends it to the list of values +func (f *Int64Slice) Set(value string) error { + tmp, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + *f = append(*f, tmp) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *Int64Slice) String() string { + return fmt.Sprintf("%d", *f) +} + +// Value returns the slice of ints set by this flag +func (f *Int64Slice) Value() []int64 { + return *f +} + +// Int64SliceFlag is an int flag that can be specified multiple times on the +// command-line +type Int64SliceFlag struct { + Name string + Value *Int64Slice + Usage string + EnvVar string + Hidden bool +} + +// String returns the usage +func (f Int64SliceFlag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f Int64SliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + newVal := &Int64Slice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + err := newVal.Set(s) + if err != nil { + fmt.Fprintf(ErrWriter, err.Error()) + } + } + f.Value = newVal + break + } + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &Int64Slice{} + } + set.Var(f.Value, name, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f Int64SliceFlag) GetName() string { + return f.Name +} + // BoolFlag is a switch that defaults to false type BoolFlag struct { Name string @@ -638,6 +709,9 @@ func stringifyFlag(f Flag) string { case IntSliceFlag: return withEnvHint(fv.FieldByName("EnvVar").String(), stringifyIntSliceFlag(f.(IntSliceFlag))) + case Int64SliceFlag: + return withEnvHint(fv.FieldByName("EnvVar").String(), + stringifyInt64SliceFlag(f.(Int64SliceFlag))) case StringSliceFlag: return withEnvHint(fv.FieldByName("EnvVar").String(), stringifyStringSliceFlag(f.(StringSliceFlag))) @@ -683,6 +757,17 @@ func stringifyIntSliceFlag(f IntSliceFlag) string { return stringifySliceFlag(f.Usage, f.Name, defaultVals) } +func stringifyInt64SliceFlag(f Int64SliceFlag) string { + defaultVals := []string{} + if f.Value != nil && len(f.Value.Value()) > 0 { + for _, i := range f.Value.Value() { + defaultVals = append(defaultVals, fmt.Sprintf("%d", i)) + } + } + + return stringifySliceFlag(f.Usage, f.Name, defaultVals) +} + func stringifyStringSliceFlag(f StringSliceFlag) string { defaultVals := []string{} if f.Value != nil && len(f.Value.Value()) > 0 { diff --git a/flag_test.go b/flag_test.go index 32d57fb..296d5ac 100644 --- a/flag_test.go +++ b/flag_test.go @@ -277,6 +277,49 @@ func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { } } +var int64SliceFlagTests = []struct { + name string + value *Int64Slice + expected string +}{ + {"heads", &Int64Slice{}, "--heads value\t"}, + {"H", &Int64Slice{}, "-H value\t"}, + {"H, heads", func() *Int64Slice { + i := &Int64Slice{} + i.Set("2") + i.Set("17179869184") + return i + }(), "-H value, --heads value\t(default: 2, 17179869184)"}, +} + +func TestInt64SliceFlagHelpOutput(t *testing.T) { + for _, test := range int64SliceFlagTests { + flag := Int64SliceFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_SMURF", "42,17179869184") + for _, test := range int64SliceFlagTests { + flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} + output := flag.String() + + expectedSuffix := " [$APP_SMURF]" + if runtime.GOOS == "windows" { + expectedSuffix = " [%APP_SMURF%]" + } + if !strings.HasSuffix(output, expectedSuffix) { + t.Errorf("%q does not end with"+expectedSuffix, output) + } + } +} + var float64FlagTests = []struct { name string expected string @@ -615,6 +658,63 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { }).Run([]string{"run"}) } +func TestParseMultiInt64Slice(t *testing.T) { + (&App{ + Flags: []Flag{ + Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}}, + }, + Action: func(ctx *Context) error { + if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.Int64Slice("s"), []int64{10, 17179869184}) { + t.Errorf("short name not set") + } + return nil + }, + }).Run([]string{"run", "-s", "10", "-s", "17179869184"}) +} + +func TestParseMultiInt64SliceFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,17179869184") + + (&App{ + Flags: []Flag{ + Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"}, + }, + Action: func(ctx *Context) error { + if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { + t.Errorf("short name not set from env") + } + return nil + }, + }).Run([]string{"run"}) +} + +func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,17179869184") + + (&App{ + Flags: []Flag{ + Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + }, + Action: func(ctx *Context) error { + if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { + t.Errorf("short name not set from env") + } + return nil + }, + }).Run([]string{"run"}) +} + func TestParseMultiFloat64(t *testing.T) { a := App{ Flags: []Flag{ From 537f5beb66a21f2ec875cae4be798249f8fd6983 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Thu, 16 Jun 2016 10:14:28 -0400 Subject: [PATCH 04/29] Tweaks to Int64Flag PR --- CHANGELOG.md | 1 + context_test.go | 2 +- flag.go | 7 ++----- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6da886..205d8db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - `./runtests` test runner with coverage tracking by default - testing on OS X - testing on Windows +- `Int64Flag` type and supporting code ### Changed - Use spaces for alignment in help/usage output instead of tabs, making the diff --git a/context_test.go b/context_test.go index 7625c5b..ae37637 100644 --- a/context_test.go +++ b/context_test.go @@ -13,7 +13,7 @@ func TestNewContext(t *testing.T) { set.Float64("myflag64", float64(17), "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Int("myflag", 42, "doc") - globalSet.Int("myflagInt64", 42, "doc") + globalSet.Int64("myflagInt64", int64(42), "doc") globalSet.Float64("myflag64", float64(47), "doc") globalCtx := NewContext(nil, globalSet, nil) command := Command{Name: "mycommand"} diff --git a/flag.go b/flag.go index abeff90..6af15ed 100644 --- a/flag.go +++ b/flag.go @@ -189,7 +189,7 @@ func (f *IntSlice) Set(value string) error { // String returns a readable representation of this value (for usage defaults) func (f *IntSlice) String() string { - return fmt.Sprintf("%d", *f) + return fmt.Sprintf("%#v", *f) } // Value returns the slice of ints set by this flag @@ -260,7 +260,7 @@ func (f *Int64Slice) Set(value string) error { // String returns a readable representation of this value (for usage defaults) func (f *Int64Slice) String() string { - return fmt.Sprintf("%d", *f) + return fmt.Sprintf("%#v", *f) } // Value returns the slice of ints set by this flag @@ -447,7 +447,6 @@ func (f StringFlag) GetName() string { } // IntFlag is a flag that takes an integer -// Errors if the value provided cannot be parsed type IntFlag struct { Name string Value int @@ -492,7 +491,6 @@ func (f IntFlag) GetName() string { } // Int64Flag is a flag that takes a 64-bit integer -// Errors if the value provided cannot be parsed type Int64Flag struct { Name string Value int64 @@ -582,7 +580,6 @@ func (f DurationFlag) GetName() string { } // Float64Flag is a flag that takes an float value -// Errors if the value provided cannot be parsed type Float64Flag struct { Name string Value float64 From a0e694ed72569a637acbd432fbee7fc74cfe506c Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Thu, 16 Jun 2016 11:13:32 -0400 Subject: [PATCH 05/29] Add UintFlag, Uint64Flag types and supporting code --- CHANGELOG.md | 2 +- README.md | 31 +++++++++++++++-- context.go | 58 ++++++++++++++++++++++++++++++-- context_test.go | 24 +++++++++++++- flag.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ flag_test.go | 72 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 268 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 205d8db..d975be7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - `./runtests` test runner with coverage tracking by default - testing on OS X - testing on Windows -- `Int64Flag` type and supporting code +- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code ### Changed - Use spaces for alignment in help/usage output instead of tabs, making the diff --git a/README.md b/README.md index 9bbfe83..d073997 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS - --version Shows version information + --version Shows version information ``` ### Arguments @@ -984,7 +984,7 @@ func main() { demonstration purposes. Use of one's imagination is encouraged. ``` go package main @@ -1036,6 +1036,19 @@ func (w *hexWriter) Write(p []byte) (int, error) { return len(p), nil } +type genericType struct{ + s string +} + +func (g *genericType) Set(value string) error { + g.s = value + return nil +} + +func (g *genericType) String() string { + return g.s +} + func main() { app := cli.NewApp() app.Name = "kənˈtrīv" @@ -1105,7 +1118,17 @@ func main() { app.Flags = []cli.Flag{ cli.BoolFlag{Name: "fancy"}, cli.BoolTFlag{Name: "fancier"}, + cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3}, + cli.Float64Flag{Name: "howmuch"}, + cli.GenericFlag{Name: "wat", Value: &genericType{}}, + cli.Int64Flag{Name: "longdistance"}, + cli.Int64SliceFlag{Name: "intervals"}, + cli.IntFlag{Name: "distance"}, + cli.IntSliceFlag{Name: "times"}, cli.StringFlag{Name: "dance-move, d"}, + cli.StringSliceFlag{Name: "names, N"}, + cli.UintFlag{Name: "age"}, + cli.Uint64Flag{Name: "bigage"}, } app.EnableBashCompletion = true app.HideHelp = false @@ -1182,10 +1205,14 @@ func main() { fmt.Printf("%#v\n", nc.Duration("howlong")) fmt.Printf("%#v\n", nc.Float64("hay")) fmt.Printf("%#v\n", nc.Generic("bloop")) + fmt.Printf("%#v\n", nc.Int64("bonk")) + fmt.Printf("%#v\n", nc.Int64Slice("burnks")) fmt.Printf("%#v\n", nc.Int("bips")) fmt.Printf("%#v\n", nc.IntSlice("blups")) fmt.Printf("%#v\n", nc.String("snurt")) fmt.Printf("%#v\n", nc.StringSlice("snurkles")) + fmt.Printf("%#v\n", nc.Uint("flub")) + fmt.Printf("%#v\n", nc.Uint64("florb")) fmt.Printf("%#v\n", nc.GlobalBool("global-nope")) fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp")) fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong")) diff --git a/context.go b/context.go index d6d4a19..879bae5 100644 --- a/context.go +++ b/context.go @@ -36,6 +36,16 @@ func (c *Context) Int64(name string) int64 { return lookupInt64(name, c.flagSet) } +// Uint looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Uint(name string) uint { + return lookupUint(name, c.flagSet) +} + +// Uint64 looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Uint64(name string) uint64 { + return lookupUint64(name, c.flagSet) +} + // Duration looks up the value of a local time.Duration flag, returns 0 if no // time.Duration flag exists func (c *Context) Duration(name string) time.Duration { @@ -103,6 +113,22 @@ func (c *Context) GlobalInt64(name string) int64 { return 0 } +// GlobalUint looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalUint(name string) uint { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint(name, fs) + } + return 0 +} + +// GlobalUint64 looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalUint64(name string) uint64 { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupUint64(name, fs) + } + return 0 +} + // GlobalFloat64 looks up the value of a global float64 flag, returns float64(0) // if no float64 flag exists func (c *Context) GlobalFloat64(name string) float64 { @@ -334,7 +360,20 @@ func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { func lookupInt(name string, set *flag.FlagSet) int { f := set.Lookup(name) if f != nil { - val, err := strconv.Atoi(f.Value.String()) + val, err := strconv.ParseInt(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return int(val) + } + + return 0 +} + +func lookupInt64(name string, set *flag.FlagSet) int64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseInt(f.Value.String(), 0, 64) if err != nil { return 0 } @@ -344,10 +383,23 @@ func lookupInt(name string, set *flag.FlagSet) int { return 0 } -func lookupInt64(name string, set *flag.FlagSet) int64 { +func lookupUint(name string, set *flag.FlagSet) uint { f := set.Lookup(name) if f != nil { - val, err := strconv.ParseInt(f.Value.String(), 10, 64) + val, err := strconv.ParseUint(f.Value.String(), 0, 64) + if err != nil { + return 0 + } + return uint(val) + } + + return 0 +} + +func lookupUint64(name string, set *flag.FlagSet) uint64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } diff --git a/context_test.go b/context_test.go index ae37637..5c68fdd 100644 --- a/context_test.go +++ b/context_test.go @@ -9,11 +9,15 @@ import ( func TestNewContext(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") - set.Int("myflagInt64", 12, "doc") + set.Int64("myflagInt64", int64(12), "doc") + set.Uint("myflagUint", uint(93), "doc") + set.Uint64("myflagUint64", uint64(93), "doc") set.Float64("myflag64", float64(17), "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Int("myflag", 42, "doc") globalSet.Int64("myflagInt64", int64(42), "doc") + globalSet.Uint("myflagUint", uint(33), "doc") + globalSet.Uint64("myflagUint64", uint64(33), "doc") globalSet.Float64("myflag64", float64(47), "doc") globalCtx := NewContext(nil, globalSet, nil) command := Command{Name: "mycommand"} @@ -21,9 +25,13 @@ func TestNewContext(t *testing.T) { c.Command = command expect(t, c.Int("myflag"), 12) expect(t, c.Int64("myflagInt64"), int64(12)) + expect(t, c.Uint("myflagUint"), uint(93)) + expect(t, c.Uint64("myflagUint64"), uint64(93)) expect(t, c.Float64("myflag64"), float64(17)) expect(t, c.GlobalInt("myflag"), 42) expect(t, c.GlobalInt64("myflagInt64"), int64(42)) + expect(t, c.GlobalUint("myflagUint"), uint(33)) + expect(t, c.GlobalUint64("myflagUint64"), uint64(33)) expect(t, c.GlobalFloat64("myflag64"), float64(47)) expect(t, c.Command.Name, "mycommand") } @@ -42,6 +50,20 @@ func TestContext_Int64(t *testing.T) { expect(t, c.Int64("myflagInt64"), int64(12)) } +func TestContext_Uint(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Uint("myflagUint", uint(13), "doc") + c := NewContext(nil, set, nil) + expect(t, c.Uint("myflagUint"), uint(13)) +} + +func TestContext_Uint64(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Uint64("myflagUint64", uint64(9), "doc") + c := NewContext(nil, set, nil) + expect(t, c.Uint64("myflagUint64"), uint64(9)) +} + func TestContext_GlobalInt(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") diff --git a/flag.go b/flag.go index 6af15ed..f8a28d1 100644 --- a/flag.go +++ b/flag.go @@ -534,6 +534,94 @@ func (f Int64Flag) GetName() string { return f.Name } +// UintFlag is a flag that takes an unsigned integer +type UintFlag struct { + Name string + Value uint + Usage string + EnvVar string + Destination *uint + Hidden bool +} + +// String returns the usage +func (f UintFlag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f UintFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + if err == nil { + f.Value = uint(envValInt) + break + } + } + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.UintVar(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint(name, f.Value, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f UintFlag) GetName() string { + return f.Name +} + +// Uint64Flag is a flag that takes an unsigned 64-bit integer +type Uint64Flag struct { + Name string + Value uint64 + Usage string + EnvVar string + Destination *uint64 + Hidden bool +} + +// String returns the usage +func (f Uint64Flag) String() string { + return FlagStringer(f) +} + +// Apply populates the flag given the flag set and environment +func (f Uint64Flag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseUint(envVal, 0, 64) + if err == nil { + f.Value = uint64(envValInt) + break + } + } + } + } + + eachName(f.Name, func(name string) { + if f.Destination != nil { + set.Uint64Var(f.Destination, name, f.Value, f.Usage) + return + } + set.Uint64(name, f.Value, f.Usage) + }) +} + +// GetName returns the name of the flag. +func (f Uint64Flag) GetName() string { + return f.Name +} + // DurationFlag is a flag that takes a duration specified in Go's duration // format: https://golang.org/pkg/time/#ParseDuration type DurationFlag struct { diff --git a/flag_test.go b/flag_test.go index 296d5ac..a7afcc4 100644 --- a/flag_test.go +++ b/flag_test.go @@ -198,6 +198,78 @@ func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { } } +var uintFlagTests = []struct { + name string + expected string +}{ + {"nerfs", "--nerfs value\t(default: 41)"}, + {"N", "-N value\t(default: 41)"}, +} + +func TestUintFlagHelpOutput(t *testing.T) { + for _, test := range uintFlagTests { + flag := UintFlag{Name: test.name, Value: 41} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAR", "2") + for _, test := range uintFlagTests { + flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} + output := flag.String() + + expectedSuffix := " [$APP_BAR]" + if runtime.GOOS == "windows" { + expectedSuffix = " [%APP_BAR%]" + } + if !strings.HasSuffix(output, expectedSuffix) { + t.Errorf("%s does not end with"+expectedSuffix, output) + } + } +} + +var uint64FlagTests = []struct { + name string + expected string +}{ + {"gerfs", "--gerfs value\t(default: 8589934582)"}, + {"G", "-G value\t(default: 8589934582)"}, +} + +func TestUint64FlagHelpOutput(t *testing.T) { + for _, test := range uint64FlagTests { + flag := Uint64Flag{Name: test.name, Value: 8589934582} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAR", "2") + for _, test := range uint64FlagTests { + flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} + output := flag.String() + + expectedSuffix := " [$APP_BAR]" + if runtime.GOOS == "windows" { + expectedSuffix = " [%APP_BAR%]" + } + if !strings.HasSuffix(output, expectedSuffix) { + t.Errorf("%s does not end with"+expectedSuffix, output) + } + } +} + var durationFlagTests = []struct { name string expected string From ee69d3c011b74a397d473ba29fbb076be6f8df4c Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Thu, 16 Jun 2016 11:47:19 -0400 Subject: [PATCH 06/29] Generate TOC in README and assert it is up to date --- .gitignore | 1 + .travis.yml | 8 ++++++++ README.md | 37 ++++++++++++++++++++++++++++++++++--- runtests | 8 +++++++- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 7823778..faf70c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.coverprofile +node_modules/ diff --git a/.travis.yml b/.travis.yml index 657e96a..273d017 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,10 @@ language: go sudo: false +cache: + directories: + - node_modules + go: - 1.2.2 - 1.3.3 @@ -25,8 +29,12 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... +- if [ ! -f node_modules/.bin/markdown-toc ] ; then + npm install markdown-toc ; + fi script: - ./runtests vet - ./runtests test - ./runtests gfmxr +- ./runtests toc diff --git a/README.md b/README.md index 9bbfe83..005dba0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +cli +=== + [![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) @@ -6,9 +9,6 @@ [![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) / [![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc) - -# cli - **Notice:** This is the library formerly known as `github.com/codegangsta/cli` -- Github will automatically redirect requests to this repository, but we recommend updating your references for clarity. @@ -17,6 +17,37 @@ cli is a simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. + + +- [Overview](#overview) +- [Installation](#installation) + * [Supported platforms](#supported-platforms) + * [Using the `v2` branch](#using-the-v2-branch) + * [Pinning to the `v1` branch](#pinning-to-the-v1-branch) +- [Getting Started](#getting-started) +- [Examples](#examples) + * [Arguments](#arguments) + * [Flags](#flags) + + [Placeholder Values](#placeholder-values) + + [Alternate Names](#alternate-names) + + [Values from the Environment](#values-from-the-environment) + + [Values from alternate input sources (YAML and others)](#values-from-alternate-input-sources-yaml-and-others) + * [Subcommands](#subcommands) + * [Subcommands categories](#subcommands-categories) + * [Exit code](#exit-code) + * [Bash Completion](#bash-completion) + + [Enabling](#enabling) + + [Distribution](#distribution) + + [Customization](#customization) + * [Generated Help Text](#generated-help-text) + + [Customization](#customization-1) + * [Version Flag](#version-flag) + + [Customization](#customization-2) + + [Full API Example](#full-api-example) +- [Contribution Guidelines](#contribution-guidelines) + + + ## Overview Command line apps are usually so tiny that there is absolutely no reason why diff --git a/runtests b/runtests index 72c1f0d..0a7b483 100755 --- a/runtests +++ b/runtests @@ -18,7 +18,8 @@ def main(sysargs=sys.argv[:]): targets = { 'vet': _vet, 'test': _test, - 'gfmxr': _gfmxr + 'gfmxr': _gfmxr, + 'toc': _toc, } parser = argparse.ArgumentParser() @@ -62,6 +63,11 @@ def _vet(): _run('go vet ./...'.split()) +def _toc(): + _run(['node_modules/.bin/markdown-toc', '-i', 'README.md']) + _run(['git', 'diff', '--quiet']) + + def _run(command): print('runtests: {}'.format(' '.join(command)), file=sys.stderr) check_call(command) From 0c9b549918e1c3be9c927a4ceec0d0cc3f1b71ba Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 09:54:54 -0400 Subject: [PATCH 07/29] Ensure remaining v1 additions are v2 compatible --- .travis.yml | 3 + flag.go | 160 ++++++++++++++++++++++++++++++--------------------- flag_test.go | 45 +++++++++------ 3 files changed, 126 insertions(+), 82 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3225cc1..84c3828 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,9 @@ before_script: - if [ ! -f node_modules/.bin/markdown-toc ] ; then npm install markdown-toc ; fi +- mkdir -p ~/gopath/src/gopkg.in/urfave +- rm -rf ~/gopath/src/gopkg.in/urfave/cli.v2 +- ln -sv ~/gopath/src/github.com/urfave/cli ~/gopath/src/gopkg.in/urfave/cli.v2 script: - flake8 runtests cli-v1-to-v2 diff --git a/flag.go b/flag.go index 2e6caf4..3ff53ce 100644 --- a/flag.go +++ b/flag.go @@ -219,6 +219,11 @@ func NewIntSlice(defaults ...int) *IntSlice { return &IntSlice{slice: append([]int{}, defaults...)} } +// NewInt64Slice makes an *Int64Slice with default values +func NewInt64Slice(defaults ...int64) *Int64Slice { + return &Int64Slice{slice: append([]int64{}, defaults...)} +} + // SetInt directly adds an integer to the list of values func (i *IntSlice) SetInt(value int) { if !i.hasBeenSet { @@ -243,12 +248,12 @@ func (i *IntSlice) Set(value string) error { return nil } - tmp, err := strconv.Atoi(value) + tmp, err := strconv.ParseInt(value, 0, 64) if err != nil { return err } - i.slice = append(i.slice, tmp) + i.slice = append(i.slice, int(tmp)) return nil } @@ -318,50 +323,72 @@ func (f *IntSliceFlag) Names() []string { } // Int64Slice is an opaque type for []int to satisfy flag.Value -type Int64Slice []int64 +type Int64Slice struct { + slice []int64 + hasBeenSet bool +} // Set parses the value into an integer and appends it to the list of values func (f *Int64Slice) Set(value string) error { - tmp, err := strconv.ParseInt(value, 10, 64) + if !f.hasBeenSet { + f.slice = []int64{} + f.hasBeenSet = true + } + + if strings.HasPrefix(value, slPfx) { + // Deserializing assumes overwrite + _ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice) + f.hasBeenSet = true + return nil + } + + tmp, err := strconv.ParseInt(value, 0, 64) if err != nil { return err } - *f = append(*f, tmp) + + f.slice = append(f.slice, tmp) return nil } // String returns a readable representation of this value (for usage defaults) func (f *Int64Slice) String() string { - return fmt.Sprintf("%#v", *f) + return fmt.Sprintf("%#v", f.slice) +} + +// Serialized allows Int64Slice to fulfill Serializeder +func (f *Int64Slice) Serialized() string { + jsonBytes, _ := json.Marshal(f.slice) + return fmt.Sprintf("%s%s", slPfx, string(jsonBytes)) } // Value returns the slice of ints set by this flag func (f *Int64Slice) Value() []int64 { - return *f + return f.slice } // Int64SliceFlag is an int flag that can be specified multiple times on the // command-line type Int64SliceFlag struct { - Name string - Value *Int64Slice - Usage string - EnvVar string - Hidden bool + Name string + Aliases []string + Value *Int64Slice + Usage string + EnvVars []string + Hidden bool } // String returns the usage -func (f Int64SliceFlag) String() string { +func (f *Int64SliceFlag) String() string { return FlagStringer(f) } // Apply populates the flag given the flag set and environment -func (f Int64SliceFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) +func (f *Int64SliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { if envVal := os.Getenv(envVar); envVal != "" { - newVal := &Int64Slice{} + newVal := NewInt64Slice() for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) err := newVal.Set(s) @@ -375,17 +402,18 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { - if f.Value == nil { - f.Value = &Int64Slice{} - } + if f.Value == nil { + f.Value = NewInt64Slice() + } + + for _, name := range f.Names() { set.Var(f.Value, name, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f Int64SliceFlag) GetName() string { - return f.Name +// Names returns the names of the flag. +func (f *Int64SliceFlag) Names() []string { + return flagNames(f) } // BoolFlag is a switch that defaults to false @@ -520,23 +548,23 @@ func (f *IntFlag) Names() []string { // Int64Flag is a flag that takes a 64-bit integer type Int64Flag struct { Name string + Aliases []string Value int64 Usage string - EnvVar string + EnvVars []string Destination *int64 Hidden bool } // String returns the usage -func (f Int64Flag) String() string { +func (f *Int64Flag) String() string { return FlagStringer(f) } // Apply populates the flag given the flag set and environment -func (f Int64Flag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) +func (f *Int64Flag) Apply(set *flag.FlagSet) { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { if envVal := os.Getenv(envVar); envVal != "" { envValInt, err := strconv.ParseInt(envVal, 0, 64) if err == nil { @@ -547,40 +575,40 @@ func (f Int64Flag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.Int64Var(f.Destination, name, f.Value, f.Usage) return } set.Int64(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f Int64Flag) GetName() string { - return f.Name +// Names returns the names of the flag. +func (f *Int64Flag) Names() []string { + return flagNames(f) } // UintFlag is a flag that takes an unsigned integer type UintFlag struct { Name string + Aliases []string Value uint Usage string - EnvVar string + EnvVars []string Destination *uint Hidden bool } // String returns the usage -func (f UintFlag) String() string { +func (f *UintFlag) String() string { return FlagStringer(f) } // Apply populates the flag given the flag set and environment -func (f UintFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) +func (f *UintFlag) Apply(set *flag.FlagSet) { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { if envVal := os.Getenv(envVar); envVal != "" { envValInt, err := strconv.ParseUint(envVal, 0, 64) if err == nil { @@ -591,40 +619,40 @@ func (f UintFlag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.UintVar(f.Destination, name, f.Value, f.Usage) return } set.Uint(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f UintFlag) GetName() string { - return f.Name +// Names returns the names of the flag. +func (f *UintFlag) Names() []string { + return flagNames(f) } // Uint64Flag is a flag that takes an unsigned 64-bit integer type Uint64Flag struct { Name string + Aliases []string Value uint64 Usage string - EnvVar string + EnvVars []string Destination *uint64 Hidden bool } // String returns the usage -func (f Uint64Flag) String() string { +func (f *Uint64Flag) String() string { return FlagStringer(f) } // Apply populates the flag given the flag set and environment -func (f Uint64Flag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - for _, envVar := range strings.Split(f.EnvVar, ",") { - envVar = strings.TrimSpace(envVar) +func (f *Uint64Flag) Apply(set *flag.FlagSet) { + if f.EnvVars != nil { + for _, envVar := range f.EnvVars { if envVal := os.Getenv(envVar); envVal != "" { envValInt, err := strconv.ParseUint(envVal, 0, 64) if err == nil { @@ -635,18 +663,18 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) { } } - eachName(f.Name, func(name string) { + for _, name := range f.Names() { if f.Destination != nil { set.Uint64Var(f.Destination, name, f.Value, f.Usage) return } set.Uint64(name, f.Value, f.Usage) - }) + } } -// GetName returns the name of the flag. -func (f Uint64Flag) GetName() string { - return f.Name +// Names returns the names of the flag. +func (f *Uint64Flag) Names() []string { + return flagNames(f) } // DurationFlag is a flag that takes a duration specified in Go's duration @@ -860,13 +888,13 @@ func stringifyFlag(f Flag) string { switch f.(type) { case *IntSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), + return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyIntSliceFlag(f.(*IntSliceFlag))) case *Int64SliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), + return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyInt64SliceFlag(f.(*Int64SliceFlag))) case *StringSliceFlag: - return withEnvHint(fv.FieldByName("EnvVar").String(), + return withEnvHint(flagStringSliceField(f, "EnvVars"), stringifyStringSliceFlag(f.(*StringSliceFlag))) } @@ -907,10 +935,10 @@ func stringifyIntSliceFlag(f *IntSliceFlag) string { } } - return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) + return stringifySliceFlag(f.Usage, f.Names(), defaultVals) } -func stringifyInt64SliceFlag(f Int64SliceFlag) string { +func stringifyInt64SliceFlag(f *Int64SliceFlag) string { defaultVals := []string{} if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { @@ -918,7 +946,7 @@ func stringifyInt64SliceFlag(f Int64SliceFlag) string { } } - return stringifySliceFlag(f.Usage, f.Name, defaultVals) + return stringifySliceFlag(f.Usage, f.Names(), defaultVals) } func stringifyStringSliceFlag(f *StringSliceFlag) string { @@ -931,7 +959,7 @@ func stringifyStringSliceFlag(f *StringSliceFlag) string { } } - return stringifySliceFlag(f.Usage, append([]string{f.Name}, f.Aliases...), defaultVals) + return stringifySliceFlag(f.Usage, f.Names(), defaultVals) } func stringifySliceFlag(usage string, names, defaultVals []string) string { diff --git a/flag_test.go b/flag_test.go index c82f4d3..1b69f51 100644 --- a/flag_test.go +++ b/flag_test.go @@ -215,7 +215,7 @@ func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2") for _, test := range int64FlagTests { - flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} + flag := IntFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := flag.String() expectedSuffix := " [$APP_BAR]" @@ -251,7 +251,7 @@ func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2") for _, test := range uintFlagTests { - flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} + flag := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := flag.String() expectedSuffix := " [$APP_BAR]" @@ -287,7 +287,7 @@ func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_BAR", "2") for _, test := range uint64FlagTests { - flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} + flag := UintFlag{Name: test.name, EnvVars: []string{"APP_BAR"}} output := flag.String() expectedSuffix := " [$APP_BAR]" @@ -397,22 +397,19 @@ func TestIntSliceFlagApply_SetsAllNames(t *testing.T) { var int64SliceFlagTests = []struct { name string + aliases []string value *Int64Slice expected string }{ - {"heads", &Int64Slice{}, "--heads value\t"}, - {"H", &Int64Slice{}, "-H value\t"}, - {"H, heads", func() *Int64Slice { - i := &Int64Slice{} - i.Set("2") - i.Set("17179869184") - return i - }(), "-H value, --heads value\t(default: 2, 17179869184)"}, + {"heads", nil, NewInt64Slice(), "--heads value\t"}, + {"H", nil, NewInt64Slice(), "-H value\t"}, + {"heads", []string{"H"}, NewInt64Slice(int64(2), int64(17179869184)), + "--heads value, -H value\t(default: 2, 17179869184)"}, } func TestInt64SliceFlagHelpOutput(t *testing.T) { for _, test := range int64SliceFlagTests { - flag := Int64SliceFlag{Name: test.name, Value: test.value} + flag := Int64SliceFlag{Name: test.name, Aliases: test.aliases, Value: test.value} output := flag.String() if output != test.expected { @@ -425,7 +422,7 @@ func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() os.Setenv("APP_SMURF", "42,17179869184") for _, test := range int64SliceFlagTests { - flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} + flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVars: []string{"APP_SMURF"}} output := flag.String() expectedSuffix := " [$APP_SMURF]" @@ -929,7 +926,7 @@ func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { func TestParseMultiInt64Slice(t *testing.T) { (&App{ Flags: []Flag{ - Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}}, + &Int64SliceFlag{Name: "serve", Aliases: []string{"s"}, Value: NewInt64Slice()}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) { @@ -949,7 +946,7 @@ func TestParseMultiInt64SliceFromEnv(t *testing.T) { (&App{ Flags: []Flag{ - Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"}, + &Int64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { @@ -969,7 +966,7 @@ func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) { (&App{ Flags: []Flag{ - Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + &Int64SliceFlag{Name: "intervals", Aliases: []string{"i"}, Value: NewInt64Slice(), EnvVars: []string{"COMPAT_INTERVALS", "APP_INTERVALS"}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { @@ -1339,3 +1336,19 @@ func TestIntSlice_Serialized_Set(t *testing.T) { t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1) } } + +func TestInt64Slice_Serialized_Set(t *testing.T) { + sl0 := NewInt64Slice(int64(1), int64(2)) + ser0 := sl0.Serialized() + + if len(ser0) < len(slPfx) { + t.Fatalf("serialized shorter than expected: %q", ser0) + } + + sl1 := NewInt64Slice(int64(3), int64(4)) + sl1.Set(ser0) + + if sl0.String() != sl1.String() { + t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1) + } +} From 9e21e0e342fc012f6e980d2d847ae979ab821a36 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 10:00:12 -0400 Subject: [PATCH 08/29] Fix up the full API example in the README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 15097d4..4e80447 100644 --- a/README.md +++ b/README.md @@ -1047,7 +1047,7 @@ func init() { cli.HelpFlag = &cli.BoolFlag{Name: "halp"} cli.BashCompletionFlag = &cli.BoolFlag{Name: "compgen", Hidden: true} - cli.VersionFlag = &cli.BoolFlag{Name: "print-version, V"} + cli.VersionFlag = &cli.BoolFlag{Name: "print-version", Aliases: []string{"V"}} cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { fmt.Fprintf(w, "best of luck to you\n") @@ -1114,7 +1114,7 @@ func main() { Description: "no really, there is a lot of dooing to be done", ArgsUsage: "[arrgh]", Flags: []cli.Flag{ - &cli.BoolFlag{Name: "forever, forevvarr"}, + &cli.BoolFlag{Name: "forever", Aliases: []string{"forevvarr"}}, }, Subcommands: []*cli.Command{ &cli.Command{ @@ -1157,15 +1157,15 @@ func main() { app.Flags = []cli.Flag{ &cli.BoolFlag{Name: "fancy"}, &cli.BoolFlag{Value: true, Name: "fancier"}, - &cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3}, + &cli.DurationFlag{Name: "howlong", Aliases: []string{"H"}, Value: time.Second * 3}, &cli.Float64Flag{Name: "howmuch"}, &cli.GenericFlag{Name: "wat", Value: &genericType{}}, &cli.Int64Flag{Name: "longdistance"}, &cli.Int64SliceFlag{Name: "intervals"}, &cli.IntFlag{Name: "distance"}, &cli.IntSliceFlag{Name: "times"}, - &cli.StringFlag{Name: "dance-move, d"}, - &cli.StringSliceFlag{Name: "names, N"}, + &cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}}, + &cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}}, &cli.UintFlag{Name: "age"}, &cli.Uint64Flag{Name: "bigage"}, } From dc55e12942ecba68ede4f07b5f3f2995acd395ed Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 10:01:34 -0400 Subject: [PATCH 09/29] Pull the full API example up a level --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e80447..7268140 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ applications in an expressive way. + [Customization](#customization-1) * [Version Flag](#version-flag) + [Customization](#customization-2) - + [Full API Example](#full-api-example) + * [Full API Example](#full-api-example) - [Contribution Guidelines](#contribution-guidelines) @@ -1017,7 +1017,7 @@ func main() { } ``` -#### Full API Example +### Full API Example **NOTE**: This is a contrived (functioning) example meant strictly for API demonstration purposes. Use of one's imagination is encouraged. From 6e6a80c4d3176bc9f2eb46d83ce89aaa04b0c375 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 10:19:23 -0400 Subject: [PATCH 10/29] Try symlinking into gopkg.in/urfave/cli.v2 better --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 84c3828..5584de2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ matrix: env: pip_install="sudo pip install" - go: 1.1.2 install: go get -v . - before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION + before_script: echo skipping gfmxr on ${TRAVIS_GO_VERSION} script: - ./runtests vet - ./runtests test @@ -36,9 +36,9 @@ before_script: - if [ ! -f node_modules/.bin/markdown-toc ] ; then npm install markdown-toc ; fi -- mkdir -p ~/gopath/src/gopkg.in/urfave -- rm -rf ~/gopath/src/gopkg.in/urfave/cli.v2 -- ln -sv ~/gopath/src/github.com/urfave/cli ~/gopath/src/gopkg.in/urfave/cli.v2 +- mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave +- rm -rf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 +- ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 script: - flake8 runtests cli-v1-to-v2 From 3357fcd631e85e8018a1d3397d563711a661405a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 10:31:05 -0400 Subject: [PATCH 11/29] Try linking to gopkg.in/urfave/cli.v2 on Appveyor, too --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index ae80b2f..3ec36e6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,6 +18,8 @@ install: - go env - go get github.com/urfave/gfmxr/... - go get -v -t ./... +- mkdir c:\gopath\src\gopkg.in\urfave +- mklink c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 build_script: - python runtests vet @@ -25,3 +27,4 @@ build_script: - python runtests gfmxr - python cli-v1-to-v2 --selftest - python runtests migrations +- python runtests toc From cafccda7b9dd061b2371a13d35b89f01ea458a42 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 10:42:44 -0400 Subject: [PATCH 12/29] Install gfmxr after symlinking into gopkg.in/urfave/cli.v2 --- .travis.yml | 4 ++-- appveyor.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5584de2..c879139 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,14 +31,14 @@ matrix: - ./runtests test before_script: -- go get github.com/urfave/gfmxr/... - $pip_install flake8 - if [ ! -f node_modules/.bin/markdown-toc ] ; then npm install markdown-toc ; fi - mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave -- rm -rf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 +- rm -rvf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 - ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 +- go get github.com/urfave/gfmxr/... script: - flake8 runtests cli-v1-to-v2 diff --git a/appveyor.yml b/appveyor.yml index 3ec36e6..720f4d3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,10 +16,10 @@ install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env -- go get github.com/urfave/gfmxr/... -- go get -v -t ./... - mkdir c:\gopath\src\gopkg.in\urfave - mklink c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 +- go get github.com/urfave/gfmxr/... +- go get -v -t ./... build_script: - python runtests vet From 5b14460f4e52085dec55b38cf9356d8405da651f Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 10:48:56 -0400 Subject: [PATCH 13/29] Use xcopy instead of mklink on Appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 720f4d3..8f9010d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ install: - go version - go env - mkdir c:\gopath\src\gopkg.in\urfave -- mklink c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 +- xcopy /E c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... From c07b18df3a43e4fedbe0e746293868933f71720c Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 11:13:55 -0400 Subject: [PATCH 14/29] Try out robocopy maybe? --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 8f9010d..a3a5df3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ install: - go version - go env - mkdir c:\gopath\src\gopkg.in\urfave -- xcopy /E c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 +- robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 /E - go get github.com/urfave/gfmxr/... - go get -v -t ./... From 54df4b6c6dc52f4c25edf88403484e38a7532962 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 11:20:49 -0400 Subject: [PATCH 15/29] Try a more fancy robocopy command --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index a3a5df3..13147a5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,8 @@ install: - go version - go env - mkdir c:\gopath\src\gopkg.in\urfave -- robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 /E +- robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 + /DCOPY:DA /MIR /FFT /Z /XA:SH /R:0 /TEE /XJD - go get github.com/urfave/gfmxr/... - go get -v -t ./... From 98218064c0bd1e8f7576461dcf4fe43eaf2fec2a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 11:31:18 -0400 Subject: [PATCH 16/29] Straighten out robocopy exit code --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 13147a5..b2aa9e3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,8 +17,8 @@ install: - go version - go env - mkdir c:\gopath\src\gopkg.in\urfave -- robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 - /DCOPY:DA /MIR /FFT /Z /XA:SH /R:0 /TEE /XJD +- (robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 + /DCOPY:DA /MIR /FFT /Z /XA:SH /R:0 /TEE /XJD) ^& IF %ERRORLEVEL% LEQ 4 exit 0 - go get github.com/urfave/gfmxr/... - go get -v -t ./... From 7cb40bd5762c3042e92a6f0ea42516ba4ee48d81 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 17 Jun 2016 11:36:23 -0400 Subject: [PATCH 17/29] Fighting more with cmd --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b2aa9e3..f51a9e2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,8 +17,8 @@ install: - go version - go env - mkdir c:\gopath\src\gopkg.in\urfave -- (robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 - /DCOPY:DA /MIR /FFT /Z /XA:SH /R:0 /TEE /XJD) ^& IF %ERRORLEVEL% LEQ 4 exit 0 +- robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 + /DCOPY:DA /MIR /FFT /Z /XA:SH /R:0 /TEE /XJD ^& IF %ERRORLEVEL% LEQ 4 exit 0 - go get github.com/urfave/gfmxr/... - go get -v -t ./... From a8027b8491ff7c5bb684b70a1e1041348ccc1447 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 15:20:35 -0400 Subject: [PATCH 18/29] Use git to make a copy into urfave/cli.v2 --- .travis.yml | 4 +--- appveyor.yml | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index c879139..46bd7ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,9 +35,7 @@ before_script: - if [ ! -f node_modules/.bin/markdown-toc ] ; then npm install markdown-toc ; fi -- mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave -- rm -rvf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 -- ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 +- git clone . ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 - go get github.com/urfave/gfmxr/... script: diff --git a/appveyor.yml b/appveyor.yml index f51a9e2..ae91cd4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,9 +16,7 @@ install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env -- mkdir c:\gopath\src\gopkg.in\urfave -- robocopy c:\gopath\src\github.com\urfave\cli c:\gopath\src\gopkg.in\urfave\cli.v2 - /DCOPY:DA /MIR /FFT /Z /XA:SH /R:0 /TEE /XJD ^& IF %ERRORLEVEL% LEQ 4 exit 0 +- git clone . c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... From 11311185a0592038140a3ef50b439a59d4ade145 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 15:47:23 -0400 Subject: [PATCH 19/29] Switch back to symlinking on Linux and Mac and fix existence of markdown-toc on Windows --- .travis.yml | 4 +++- appveyor.yml | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 46bd7ef..c879139 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,9 @@ before_script: - if [ ! -f node_modules/.bin/markdown-toc ] ; then npm install markdown-toc ; fi -- git clone . ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 +- mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave +- rm -rvf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 +- ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2 - go get github.com/urfave/gfmxr/... script: diff --git a/appveyor.yml b/appveyor.yml index ae91cd4..a56f1db 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,6 +4,9 @@ os: Windows Server 2012 R2 clone_folder: c:\gopath\src\github.com\urfave\cli +cache: +- node_modules + environment: GOPATH: C:\gopath GOVERSION: 1.6 @@ -19,6 +22,9 @@ install: - git clone . c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... +- ps: Install-Product node 6 +- ps: $MarkdownTocExists = Test-Path node_modules\.bin\markdown-toc +- ps: if ($MarkdownTocExists -eq $False) { npm install markdown-toc } build_script: - python runtests vet From 29dc3f0c7c82125bfaa53140b4a067eef2a1d9b8 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 15:59:00 -0400 Subject: [PATCH 20/29] More tweaks to npm usage on Windows --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a56f1db..d278a06 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,9 +22,9 @@ install: - git clone . c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... -- ps: Install-Product node 6 -- ps: $MarkdownTocExists = Test-Path node_modules\.bin\markdown-toc -- ps: if ($MarkdownTocExists -eq $False) { npm install markdown-toc } +- ps: if (Test-Path node_modules\.bin\markdown-toc -eq $False) { + npm install markdown-toc + } build_script: - python runtests vet From c24cac3f932215d99ec5e20ad6c98b7b2a2cdf81 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 15:59:54 -0400 Subject: [PATCH 21/29] Try to YAML betterer --- appveyor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d278a06..59cfa9b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,9 +22,7 @@ install: - git clone . c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... -- ps: if (Test-Path node_modules\.bin\markdown-toc -eq $False) { - npm install markdown-toc - } +- ps: if (Test-Path node_modules\.bin\markdown-toc -eq $False) { npm install markdown-toc } build_script: - python runtests vet From 8eaf6be8fae1df4825d3de1d8edc15e137cdd50a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:04:39 -0400 Subject: [PATCH 22/29] Trying to powershell better --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 59cfa9b..610f5c0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,7 +22,8 @@ install: - git clone . c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... -- ps: if (Test-Path node_modules\.bin\markdown-toc -eq $False) { npm install markdown-toc } +- ps: $MarkdownTocExists = Test-Path node_modules\.bin\markdown-toc +- ps: if ($MarkdownTocExists -eq $False) { npm install markdown-toc } build_script: - python runtests vet From dec06d28379085c1b74a5017489f6d5693bc72f9 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:20:29 -0400 Subject: [PATCH 23/29] Dynamically find path of markdown-toc executable --- runtests | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtests b/runtests index 85656c7..40c7809 100755 --- a/runtests +++ b/runtests @@ -94,7 +94,8 @@ def _migrations(): @_target def _toc(): - _run(['node_modules/.bin/markdown-toc', '-i', 'README.md']) + npm_bindir = check_output(['npm', 'bin']).strip() + _run(['node', os.path.join(npm_bindir, 'markdown-toc'), '-i', 'README.md']) _run(['git', 'diff', '--quiet']) From 0ef116788d3e28646e642b2b7af4079ab6e70e0a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:24:46 -0400 Subject: [PATCH 24/29] Run npm install in cmd subshell on Windows --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 610f5c0..004949d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,7 +23,7 @@ install: - go get github.com/urfave/gfmxr/... - go get -v -t ./... - ps: $MarkdownTocExists = Test-Path node_modules\.bin\markdown-toc -- ps: if ($MarkdownTocExists -eq $False) { npm install markdown-toc } +- ps: if ($MarkdownTocExists -eq $False) { cmd /c npm install markdown-toc } build_script: - python runtests vet From 21219e2b65ee1bbb65a46f1e8edb326cd30054b6 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:28:50 -0400 Subject: [PATCH 25/29] Flush stderr after every print --- runtests | 1 + 1 file changed, 1 insertion(+) diff --git a/runtests b/runtests index 40c7809..e762074 100755 --- a/runtests +++ b/runtests @@ -101,6 +101,7 @@ def _toc(): def _run(command): print('runtests: {}'.format(' '.join(command)), file=sys.stderr) + sys.stderr.flush() check_call(command) From 2569db1dc9ba68eed7606a32cd55a27b89ef230a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:29:33 -0400 Subject: [PATCH 26/29] Where is npm bindir? --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 004949d..2a5c07d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,6 +22,7 @@ install: - git clone . c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... +- npm bin - ps: $MarkdownTocExists = Test-Path node_modules\.bin\markdown-toc - ps: if ($MarkdownTocExists -eq $False) { cmd /c npm install markdown-toc } From d2596f4c86fa4517d2826fa61996d01f6228e922 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:31:05 -0400 Subject: [PATCH 27/29] Disable gfmxr debugging on Windows --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2a5c07d..353a28e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,7 +13,6 @@ environment: PYTHON: C:\Python27-x64 PYTHON_VERSION: 2.7.x PYTHON_ARCH: 64 - GFMXR_DEBUG: 1 install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% From 8cfdccbf0fbec9c3badb8745f2e0cf17d27e5ec5 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:37:11 -0400 Subject: [PATCH 28/29] More tweaks to how `./runtests toc` runs on Windows --- appveyor.yml | 4 +--- runtests | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 353a28e..90de4d5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,9 +21,7 @@ install: - git clone . c:\gopath\src\gopkg.in\urfave\cli.v2 - go get github.com/urfave/gfmxr/... - go get -v -t ./... -- npm bin -- ps: $MarkdownTocExists = Test-Path node_modules\.bin\markdown-toc -- ps: if ($MarkdownTocExists -eq $False) { cmd /c npm install markdown-toc } +- if not exist node_modules\.bin\markdown-toc npm install markdown-toc build_script: - python runtests vet diff --git a/runtests b/runtests index e762074..d531588 100755 --- a/runtests +++ b/runtests @@ -94,8 +94,10 @@ def _migrations(): @_target def _toc(): - npm_bindir = check_output(['npm', 'bin']).strip() - _run(['node', os.path.join(npm_bindir, 'markdown-toc'), '-i', 'README.md']) + _run([ + 'node', os.path.join('node_modules', '.bin', 'markdown-toc'), + '-i', 'README.md' + ]) _run(['git', 'diff', '--quiet']) From 77722a46e115c4f00a78a1105cde74aa7f0d3b13 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sat, 18 Jun 2016 16:46:41 -0400 Subject: [PATCH 29/29] Explicitly run markdown-toc with bash on Windows --- runtests | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/runtests b/runtests index d531588..5b903e6 100755 --- a/runtests +++ b/runtests @@ -4,6 +4,7 @@ from __future__ import print_function import argparse import glob import os +import platform import shutil import sys import tempfile @@ -11,6 +12,7 @@ import tempfile from subprocess import check_call, check_output +_WINDOWS = platform.system().lower() == 'windows' _PACKAGE_NAME = os.environ.get( 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' ) @@ -94,8 +96,9 @@ def _migrations(): @_target def _toc(): - _run([ - 'node', os.path.join('node_modules', '.bin', 'markdown-toc'), + exe = ['bash'] if _WINDOWS else [] + _run(exe + [ + os.path.join('node_modules', '.bin', 'markdown-toc'), '-i', 'README.md' ]) _run(['git', 'diff', '--quiet'])