diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index c60ed4c2fa..84f9cae01d 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 0000000000..dd6893a473 --- /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 62a5bc22d2..2a11c5ad49 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 41671a9703..88c6b95534 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,18 +1,102 @@ ## Contributing -Use @urfave/cli to ping the maintainers. - -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. - -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. - -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! +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. + +> As a general guiding principle, the current maintainers may be notified via the +> @urfave/cli GitHub team. + +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. + +### 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 0000000000..d85fa30bd1 --- /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 d2a33f01f9..0c61d002a0 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 018f9afccd..f984acfc2f 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 9c6dde7b9e..a6677ad288 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 62fdb13a48..62a1973007 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 8be586c40e..0f09c8cfe3 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 ae594d4d84..71e7173166 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 63ec9a48df..b68c3e86e3 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 929e2d8d94..3f71134bcc 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 08f03ddfbe..14d8c41c6a 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 e5b9e21e5e..4ed7f2c59e 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 a7a213e877..095a59648a 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 0365762d64..4831c17959 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 82aa1a492c..9e69d0008b 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 7bda8ee221..32899f5321 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 0fd0777d73..625f11c15d 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 c8314ac00f..58969a5d13 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 7a4b88bbf0..3455d264f4 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 bde502bd2e..2c20c28ff3 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,13 @@ -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= +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= diff --git a/internal/build/build.go b/internal/build/build.go index dec0dfb64f..0e9391b4fb 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)) - if err != nil { - return err - } + filename := pkg + ".coverprofile" - b, err := ioutil.ReadAll(file) + lineBytes, err := os.ReadFile(filename) if err != nil { return err } - out.Write(b) - err = file.Close() - if err != nil { - return err - } + lines := strings.Split(string(lineBytes), "\n") - err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg)) - if err != nil { + fmt.Fprintf(out, strings.Join(lines[1:], "\n")) + + 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 0000000000..cad25088f6 --- /dev/null +++ b/internal/genflags/cmd/genflags/main.go @@ -0,0 +1,163 @@ +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{ + Name: "genflags", + Usage: "Generate flag types for urfave/cli", + 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 0000000000..5d6f0dde1a --- /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 {{if .ValuePointer}}*{{end}}{{.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 0000000000..44e9ad4428 --- /dev/null +++ b/internal/genflags/generated_test.gotmpl @@ -0,0 +1,27 @@ +// WARNING: this file is generated. DO NOT EDIT + +package {{.TestPackageName}} + +{{range .SortedFlagTypes}} +{{if .GenerateFlagInterface}} +func Test{{.TypeName}}_SatisfiesFlagInterface(t *testing.T) { + var f {{$.UrfaveCLITestNamespace}}Flag = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + + _ = f.IsSet() + _ = f.Names() +} +{{end}} + +{{if .GenerateFmtStringerInterface}} +func Test{{.TypeName}}_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &{{$.UrfaveCLITestNamespace}}{{.TypeName}}{} + + _ = f.String() +} +{{end}} +{{end}} + +// vim{{/* 👻 */}}:ro +{{/* +vim:filetype=gotexttmpl +*/}} diff --git a/internal/genflags/package.go b/internal/genflags/package.go new file mode 100644 index 0000000000..4e5de41a77 --- /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 0000000000..3920540778 --- /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 0000000000..d9f18db03f --- /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/internal/genflags/spec_test.go b/internal/genflags/spec_test.go new file mode 100644 index 0000000000..25a9c8b36f --- /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.go b/zz_generated.flags.go new file mode 100644 index 0000000000..703e66a406 --- /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 0000000000..b8363d4a35 --- /dev/null +++ b/zz_generated.flags_test.go @@ -0,0 +1,183 @@ +// 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 f cli.Flag = &cli.Float64SliceFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestGenericFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.GenericFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestGenericFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.GenericFlag{} + + _ = f.String() +} + +func TestInt64SliceFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.Int64SliceFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestIntSliceFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.IntSliceFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestPathFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.PathFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestPathFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.PathFlag{} + + _ = f.String() +} + +func TestStringSliceFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.StringSliceFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestTimestampFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.TimestampFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestTimestampFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.TimestampFlag{} + + _ = f.String() +} + +func TestBoolFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.BoolFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestBoolFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.BoolFlag{} + + _ = f.String() +} + +func TestFloat64Flag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.Float64Flag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestFloat64Flag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.Float64Flag{} + + _ = f.String() +} + +func TestIntFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.IntFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestIntFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.IntFlag{} + + _ = f.String() +} + +func TestInt64Flag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.Int64Flag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestInt64Flag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.Int64Flag{} + + _ = f.String() +} + +func TestStringFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.StringFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestStringFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.StringFlag{} + + _ = f.String() +} + +func TestDurationFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.DurationFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestDurationFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.DurationFlag{} + + _ = f.String() +} + +func TestUintFlag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.UintFlag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestUintFlag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.UintFlag{} + + _ = f.String() +} + +func TestUint64Flag_SatisfiesFlagInterface(t *testing.T) { + var f cli.Flag = &cli.Uint64Flag{} + + _ = f.IsSet() + _ = f.Names() +} + +func TestUint64Flag_SatisfiesFmtStringerInterface(t *testing.T) { + var f fmt.Stringer = &cli.Uint64Flag{} + + _ = f.String() +} + +// vim:ro