Bo-Yi Wu
7 years ago
23 changed files with 5789 additions and 0 deletions
@ -0,0 +1,47 @@ |
|||||
|
workspace: |
||||
|
base: /go/src |
||||
|
path: github.com/appleboy/drone-packer |
||||
|
|
||||
|
clone: |
||||
|
git: |
||||
|
image: plugins/git |
||||
|
depth: 50 |
||||
|
tags: true |
||||
|
|
||||
|
pipeline: |
||||
|
lint: |
||||
|
image: golang:1.10 |
||||
|
pull: true |
||||
|
commands: |
||||
|
- make vet |
||||
|
- make lint |
||||
|
- make test-vendor |
||||
|
- make misspell-check |
||||
|
|
||||
|
build_linux_amd64: |
||||
|
image: golang:1.10 |
||||
|
pull: true |
||||
|
group: build |
||||
|
commands: |
||||
|
- make build_linux_amd64 |
||||
|
|
||||
|
build_linux_i386: |
||||
|
image: golang:1.10 |
||||
|
pull: true |
||||
|
group: build |
||||
|
commands: |
||||
|
- make build_linux_i386 |
||||
|
|
||||
|
build_linux_arm64: |
||||
|
image: golang:1.10 |
||||
|
pull: true |
||||
|
group: build |
||||
|
commands: |
||||
|
- make build_linux_arm64 |
||||
|
|
||||
|
build_linux_arm: |
||||
|
image: golang:1.10 |
||||
|
pull: true |
||||
|
group: build |
||||
|
commands: |
||||
|
- make build_linux_arm |
@ -0,0 +1,435 @@ |
|||||
|
# Change Log |
||||
|
|
||||
|
**ATTN**: This project uses [semantic versioning](http://semver.org/). |
||||
|
|
||||
|
## [Unreleased] |
||||
|
|
||||
|
## 1.20.0 - 2017-08-10 |
||||
|
|
||||
|
### Fixed |
||||
|
|
||||
|
* `HandleExitCoder` is now correctly iterates over all errors in |
||||
|
a `MultiError`. The exit code is the exit code of the last error or `1` if |
||||
|
there are no `ExitCoder`s in the `MultiError`. |
||||
|
* Fixed YAML file loading on Windows (previously would fail validate the file path) |
||||
|
* Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly |
||||
|
propogated |
||||
|
* `ErrWriter` is now passed downwards through command structure to avoid the |
||||
|
need to redefine it |
||||
|
* Pass `Command` context into `OnUsageError` rather than parent context so that |
||||
|
all fields are avaiable |
||||
|
* Errors occuring in `Before` funcs are no longer double printed |
||||
|
* Use `UsageText` in the help templates for commands and subcommands if |
||||
|
defined; otherwise build the usage as before (was previously ignoring this |
||||
|
field) |
||||
|
* `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if |
||||
|
a program calls `Set` or `GlobalSet` directly after flag parsing (would |
||||
|
previously only return `true` if the flag was set during parsing) |
||||
|
|
||||
|
### Changed |
||||
|
|
||||
|
* No longer exit the program on command/subcommand error if the error raised is |
||||
|
not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was |
||||
|
determined to be a regression in functionality. See [the |
||||
|
PR](https://github.com/urfave/cli/pull/595) for discussion. |
||||
|
|
||||
|
### Added |
||||
|
|
||||
|
* `CommandsByName` type was added to make it easy to sort `Command`s by name, |
||||
|
alphabetically |
||||
|
* `altsrc` now handles loading of string and int arrays from TOML |
||||
|
* Support for definition of custom help templates for `App` via |
||||
|
`CustomAppHelpTemplate` |
||||
|
* Support for arbitrary key/value fields on `App` to be used with |
||||
|
`CustomAppHelpTemplate` via `ExtraInfo` |
||||
|
* `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be |
||||
|
`cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag` |
||||
|
interface to be used. |
||||
|
|
||||
|
|
||||
|
## [1.19.1] - 2016-11-21 |
||||
|
|
||||
|
### Fixed |
||||
|
|
||||
|
- Fixes regression introduced in 1.19.0 where using an `ActionFunc` as |
||||
|
the `Action` for a command would cause it to error rather than calling the |
||||
|
function. Should not have a affected declarative cases using `func(c |
||||
|
*cli.Context) err)`. |
||||
|
- Shell completion now handles the case where the user specifies |
||||
|
`--generate-bash-completion` immediately after a flag that takes an argument. |
||||
|
Previously it call the application with `--generate-bash-completion` as the |
||||
|
flag value. |
||||
|
|
||||
|
## [1.19.0] - 2016-11-19 |
||||
|
### Added |
||||
|
- `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`) |
||||
|
- A `Description` field was added to `App` for a more detailed description of |
||||
|
the application (similar to the existing `Description` field on `Command`) |
||||
|
- Flag type code generation via `go generate` |
||||
|
- Write to stderr and exit 1 if action returns non-nil error |
||||
|
- Added support for TOML to the `altsrc` loader |
||||
|
- `SkipArgReorder` was added to allow users to skip the argument reordering. |
||||
|
This is useful if you want to consider all "flags" after an argument as |
||||
|
arguments rather than flags (the default behavior of the stdlib `flag` |
||||
|
library). This is backported functionality from the [removal of the flag |
||||
|
reordering](https://github.com/urfave/cli/pull/398) in the unreleased version |
||||
|
2 |
||||
|
- For formatted errors (those implementing `ErrorFormatter`), the errors will |
||||
|
be formatted during output. Compatible with `pkg/errors`. |
||||
|
|
||||
|
### Changed |
||||
|
- Raise minimum tested/supported Go version to 1.2+ |
||||
|
|
||||
|
### Fixed |
||||
|
- Consider empty environment variables as set (previously environment variables |
||||
|
with the equivalent of `""` would be skipped rather than their value used). |
||||
|
- Return an error if the value in a given environment variable cannot be parsed |
||||
|
as the flag type. Previously these errors were silently swallowed. |
||||
|
- Print full error when an invalid flag is specified (which includes the invalid flag) |
||||
|
- `App.Writer` defaults to `stdout` when `nil` |
||||
|
- If no action is specified on a command or app, the help is now printed instead of `panic`ing |
||||
|
- `App.Metadata` is initialized automatically now (previously was `nil` unless initialized) |
||||
|
- Correctly show help message if `-h` is provided to a subcommand |
||||
|
- `context.(Global)IsSet` now respects environment variables. Previously it |
||||
|
would return `false` if a flag was specified in the environment rather than |
||||
|
as an argument |
||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user |
||||
|
- `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This |
||||
|
fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well |
||||
|
as `altsrc` where Go would complain that the types didn't match |
||||
|
|
||||
|
## [1.18.1] - 2016-08-28 |
||||
|
### Fixed |
||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported) |
||||
|
|
||||
|
## [1.18.0] - 2016-06-27 |
||||
|
### Added |
||||
|
- `./runtests` test runner with coverage tracking by default |
||||
|
- testing on OS X |
||||
|
- testing on Windows |
||||
|
- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code |
||||
|
|
||||
|
### Changed |
||||
|
- Use spaces for alignment in help/usage output instead of tabs, making the |
||||
|
output alignment consistent regardless of tab width |
||||
|
|
||||
|
### Fixed |
||||
|
- Printing of command aliases in help text |
||||
|
- Printing of visible flags for both struct and struct pointer flags |
||||
|
- Display the `help` subcommand when using `CommandCategories` |
||||
|
- No longer swallows `panic`s that occur within the `Action`s themselves when |
||||
|
detecting the signature of the `Action` field |
||||
|
|
||||
|
## [1.17.1] - 2016-08-28 |
||||
|
### Fixed |
||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user |
||||
|
|
||||
|
## [1.17.0] - 2016-05-09 |
||||
|
### Added |
||||
|
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` |
||||
|
- `context.GlobalBoolT` was added as an analogue to `context.GlobalBool` |
||||
|
- Support for hiding commands by setting `Hidden: true` -- this will hide the |
||||
|
commands in help output |
||||
|
|
||||
|
### Changed |
||||
|
- `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer |
||||
|
quoted in help text output. |
||||
|
- All flag types now include `(default: {value})` strings following usage when a |
||||
|
default value can be (reasonably) detected. |
||||
|
- `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent |
||||
|
with non-slice flag types |
||||
|
- Apps now exit with a code of 3 if an unknown subcommand is specified |
||||
|
(previously they printed "No help topic for...", but still exited 0. This |
||||
|
makes it easier to script around apps built using `cli` since they can trust |
||||
|
that a 0 exit code indicated a successful execution. |
||||
|
- cleanups based on [Go Report Card |
||||
|
feedback](https://goreportcard.com/report/github.com/urfave/cli) |
||||
|
|
||||
|
## [1.16.1] - 2016-08-28 |
||||
|
### Fixed |
||||
|
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user |
||||
|
|
||||
|
## [1.16.0] - 2016-05-02 |
||||
|
### Added |
||||
|
- `Hidden` field on all flag struct types to omit from generated help text |
||||
|
|
||||
|
### Changed |
||||
|
- `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from |
||||
|
generated help text via the `Hidden` field |
||||
|
|
||||
|
### Fixed |
||||
|
- handling of error values in `HandleAction` and `HandleExitCoder` |
||||
|
|
||||
|
## [1.15.0] - 2016-04-30 |
||||
|
### Added |
||||
|
- This file! |
||||
|
- Support for placeholders in flag usage strings |
||||
|
- `App.Metadata` map for arbitrary data/state management |
||||
|
- `Set` and `GlobalSet` methods on `*cli.Context` for altering values after |
||||
|
parsing. |
||||
|
- Support for nested lookup of dot-delimited keys in structures loaded from |
||||
|
YAML. |
||||
|
|
||||
|
### Changed |
||||
|
- The `App.Action` and `Command.Action` now prefer a return signature of |
||||
|
`func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil |
||||
|
`error` is returned, there may be two outcomes: |
||||
|
- If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called |
||||
|
automatically |
||||
|
- Else the error is bubbled up and returned from `App.Run` |
||||
|
- Specifying an `Action` with the legacy return signature of |
||||
|
`func(*cli.Context)` will produce a deprecation message to stderr |
||||
|
- Specifying an `Action` that is not a `func` type will produce a non-zero exit |
||||
|
from `App.Run` |
||||
|
- Specifying an `Action` func that has an invalid (input) signature will |
||||
|
produce a non-zero exit from `App.Run` |
||||
|
|
||||
|
### Deprecated |
||||
|
- <a name="deprecated-cli-app-runandexitonerror"></a> |
||||
|
`cli.App.RunAndExitOnError`, which should now be done by returning an error |
||||
|
that fulfills `cli.ExitCoder` to `cli.App.Run`. |
||||
|
- <a name="deprecated-cli-app-action-signature"></a> the legacy signature for |
||||
|
`cli.App.Action` of `func(*cli.Context)`, which should now have a return |
||||
|
signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. |
||||
|
|
||||
|
### Fixed |
||||
|
- Added missing `*cli.Context.GlobalFloat64` method |
||||
|
|
||||
|
## [1.14.0] - 2016-04-03 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Codebeat badge |
||||
|
- Support for categorization via `CategorizedHelp` and `Categories` on app. |
||||
|
|
||||
|
### Changed |
||||
|
- Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`. |
||||
|
|
||||
|
### Fixed |
||||
|
- Ensure version is not shown in help text when `HideVersion` set. |
||||
|
|
||||
|
## [1.13.0] - 2016-03-06 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- YAML file input support. |
||||
|
- `NArg` method on context. |
||||
|
|
||||
|
## [1.12.0] - 2016-02-17 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Custom usage error handling. |
||||
|
- Custom text support in `USAGE` section of help output. |
||||
|
- Improved help messages for empty strings. |
||||
|
- AppVeyor CI configuration. |
||||
|
|
||||
|
### Changed |
||||
|
- Removed `panic` from default help printer func. |
||||
|
- De-duping and optimizations. |
||||
|
|
||||
|
### Fixed |
||||
|
- Correctly handle `Before`/`After` at command level when no subcommands. |
||||
|
- Case of literal `-` argument causing flag reordering. |
||||
|
- Environment variable hints on Windows. |
||||
|
- Docs updates. |
||||
|
|
||||
|
## [1.11.1] - 2015-12-21 (backfilled 2016-04-25) |
||||
|
### Changed |
||||
|
- Use `path.Base` in `Name` and `HelpName` |
||||
|
- Export `GetName` on flag types. |
||||
|
|
||||
|
### Fixed |
||||
|
- Flag parsing when skipping is enabled. |
||||
|
- Test output cleanup. |
||||
|
- Move completion check to account for empty input case. |
||||
|
|
||||
|
## [1.11.0] - 2015-11-15 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Destination scan support for flags. |
||||
|
- Testing against `tip` in Travis CI config. |
||||
|
|
||||
|
### Changed |
||||
|
- Go version in Travis CI config. |
||||
|
|
||||
|
### Fixed |
||||
|
- Removed redundant tests. |
||||
|
- Use correct example naming in tests. |
||||
|
|
||||
|
## [1.10.2] - 2015-10-29 (backfilled 2016-04-25) |
||||
|
### Fixed |
||||
|
- Remove unused var in bash completion. |
||||
|
|
||||
|
## [1.10.1] - 2015-10-21 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Coverage and reference logos in README. |
||||
|
|
||||
|
### Fixed |
||||
|
- Use specified values in help and version parsing. |
||||
|
- Only display app version and help message once. |
||||
|
|
||||
|
## [1.10.0] - 2015-10-06 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- More tests for existing functionality. |
||||
|
- `ArgsUsage` at app and command level for help text flexibility. |
||||
|
|
||||
|
### Fixed |
||||
|
- Honor `HideHelp` and `HideVersion` in `App.Run`. |
||||
|
- Remove juvenile word from README. |
||||
|
|
||||
|
## [1.9.0] - 2015-09-08 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- `FullName` on command with accompanying help output update. |
||||
|
- Set default `$PROG` in bash completion. |
||||
|
|
||||
|
### Changed |
||||
|
- Docs formatting. |
||||
|
|
||||
|
### Fixed |
||||
|
- Removed self-referential imports in tests. |
||||
|
|
||||
|
## [1.8.0] - 2015-06-30 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Support for `Copyright` at app level. |
||||
|
- `Parent` func at context level to walk up context lineage. |
||||
|
|
||||
|
### Fixed |
||||
|
- Global flag processing at top level. |
||||
|
|
||||
|
## [1.7.1] - 2015-06-11 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Aggregate errors from `Before`/`After` funcs. |
||||
|
- Doc comments on flag structs. |
||||
|
- Include non-global flags when checking version and help. |
||||
|
- Travis CI config updates. |
||||
|
|
||||
|
### Fixed |
||||
|
- Ensure slice type flags have non-nil values. |
||||
|
- Collect global flags from the full command hierarchy. |
||||
|
- Docs prose. |
||||
|
|
||||
|
## [1.7.0] - 2015-05-03 (backfilled 2016-04-25) |
||||
|
### Changed |
||||
|
- `HelpPrinter` signature includes output writer. |
||||
|
|
||||
|
### Fixed |
||||
|
- Specify go 1.1+ in docs. |
||||
|
- Set `Writer` when running command as app. |
||||
|
|
||||
|
## [1.6.0] - 2015-03-23 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Multiple author support. |
||||
|
- `NumFlags` at context level. |
||||
|
- `Aliases` at command level. |
||||
|
|
||||
|
### Deprecated |
||||
|
- `ShortName` at command level. |
||||
|
|
||||
|
### Fixed |
||||
|
- Subcommand help output. |
||||
|
- Backward compatible support for deprecated `Author` and `Email` fields. |
||||
|
- Docs regarding `Names`/`Aliases`. |
||||
|
|
||||
|
## [1.5.0] - 2015-02-20 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- `After` hook func support at app and command level. |
||||
|
|
||||
|
### Fixed |
||||
|
- Use parsed context when running command as subcommand. |
||||
|
- Docs prose. |
||||
|
|
||||
|
## [1.4.1] - 2015-01-09 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Support for hiding `-h / --help` flags, but not `help` subcommand. |
||||
|
- Stop flag parsing after `--`. |
||||
|
|
||||
|
### Fixed |
||||
|
- Help text for generic flags to specify single value. |
||||
|
- Use double quotes in output for defaults. |
||||
|
- Use `ParseInt` instead of `ParseUint` for int environment var values. |
||||
|
- Use `0` as base when parsing int environment var values. |
||||
|
|
||||
|
## [1.4.0] - 2014-12-12 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Support for environment variable lookup "cascade". |
||||
|
- Support for `Stdout` on app for output redirection. |
||||
|
|
||||
|
### Fixed |
||||
|
- Print command help instead of app help in `ShowCommandHelp`. |
||||
|
|
||||
|
## [1.3.1] - 2014-11-13 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- Docs and example code updates. |
||||
|
|
||||
|
### Changed |
||||
|
- Default `-v / --version` flag made optional. |
||||
|
|
||||
|
## [1.3.0] - 2014-08-10 (backfilled 2016-04-25) |
||||
|
### Added |
||||
|
- `FlagNames` at context level. |
||||
|
- Exposed `VersionPrinter` var for more control over version output. |
||||
|
- Zsh completion hook. |
||||
|
- `AUTHOR` section in default app help template. |
||||
|
- Contribution guidelines. |
||||
|
- `DurationFlag` type. |
||||
|
|
||||
|
## [1.2.0] - 2014-08-02 |
||||
|
### Added |
||||
|
- Support for environment variable defaults on flags plus tests. |
||||
|
|
||||
|
## [1.1.0] - 2014-07-15 |
||||
|
### Added |
||||
|
- Bash completion. |
||||
|
- Optional hiding of built-in help command. |
||||
|
- Optional skipping of flag parsing at command level. |
||||
|
- `Author`, `Email`, and `Compiled` metadata on app. |
||||
|
- `Before` hook func support at app and command level. |
||||
|
- `CommandNotFound` func support at app level. |
||||
|
- Command reference available on context. |
||||
|
- `GenericFlag` type. |
||||
|
- `Float64Flag` type. |
||||
|
- `BoolTFlag` type. |
||||
|
- `IsSet` flag helper on context. |
||||
|
- More flag lookup funcs at context level. |
||||
|
- More tests & docs. |
||||
|
|
||||
|
### Changed |
||||
|
- Help template updates to account for presence/absence of flags. |
||||
|
- Separated subcommand help template. |
||||
|
- Exposed `HelpPrinter` var for more control over help output. |
||||
|
|
||||
|
## [1.0.0] - 2013-11-01 |
||||
|
### Added |
||||
|
- `help` flag in default app flag set and each command flag set. |
||||
|
- Custom handling of argument parsing errors. |
||||
|
- Command lookup by name at app level. |
||||
|
- `StringSliceFlag` type and supporting `StringSlice` type. |
||||
|
- `IntSliceFlag` type and supporting `IntSlice` type. |
||||
|
- Slice type flag lookups by name at context level. |
||||
|
- Export of app and command help functions. |
||||
|
- More tests & docs. |
||||
|
|
||||
|
## 0.1.0 - 2013-07-22 |
||||
|
### Added |
||||
|
- Initial implementation. |
||||
|
|
||||
|
[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD |
||||
|
[1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 |
||||
|
[1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 |
||||
|
[1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 |
||||
|
[1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0 |
||||
|
[1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0 |
||||
|
[1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0 |
||||
|
[1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0 |
||||
|
[1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1 |
||||
|
[1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0 |
||||
|
[1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2 |
||||
|
[1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1 |
||||
|
[1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0 |
||||
|
[1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0 |
||||
|
[1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0 |
||||
|
[1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1 |
||||
|
[1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0 |
||||
|
[1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0 |
||||
|
[1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0 |
||||
|
[1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1 |
||||
|
[1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0 |
||||
|
[1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1 |
||||
|
[1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0 |
||||
|
[1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0 |
||||
|
[1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0 |
||||
|
[1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0 |
@ -0,0 +1,74 @@ |
|||||
|
# Contributor Covenant Code of Conduct |
||||
|
|
||||
|
## Our Pledge |
||||
|
|
||||
|
In the interest of fostering an open and welcoming environment, we as |
||||
|
contributors and maintainers pledge to making participation in our project and |
||||
|
our community a harassment-free experience for everyone, regardless of age, body |
||||
|
size, disability, ethnicity, gender identity and expression, level of experience, |
||||
|
education, socio-economic status, nationality, personal appearance, race, |
||||
|
religion, or sexual identity and orientation. |
||||
|
|
||||
|
## Our Standards |
||||
|
|
||||
|
Examples of behavior that contributes to creating a positive environment |
||||
|
include: |
||||
|
|
||||
|
* Using welcoming and inclusive language |
||||
|
* Being respectful of differing viewpoints and experiences |
||||
|
* Gracefully accepting constructive criticism |
||||
|
* Focusing on what is best for the community |
||||
|
* Showing empathy towards other community members |
||||
|
|
||||
|
Examples of unacceptable behavior by participants include: |
||||
|
|
||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or |
||||
|
advances |
||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks |
||||
|
* Public or private harassment |
||||
|
* Publishing others' private information, such as a physical or electronic |
||||
|
address, without explicit permission |
||||
|
* Other conduct which could reasonably be considered inappropriate in a |
||||
|
professional setting |
||||
|
|
||||
|
## Our Responsibilities |
||||
|
|
||||
|
Project maintainers are responsible for clarifying the standards of acceptable |
||||
|
behavior and are expected to take appropriate and fair corrective action in |
||||
|
response to any instances of unacceptable behavior. |
||||
|
|
||||
|
Project maintainers have the right and responsibility to remove, edit, or |
||||
|
reject comments, commits, code, wiki edits, issues, and other contributions |
||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or |
||||
|
permanently any contributor for other behaviors that they deem inappropriate, |
||||
|
threatening, offensive, or harmful. |
||||
|
|
||||
|
## Scope |
||||
|
|
||||
|
This Code of Conduct applies both within project spaces and in public spaces |
||||
|
when an individual is representing the project or its community. Examples of |
||||
|
representing a project or community include using an official project e-mail |
||||
|
address, posting via an official social media account, or acting as an appointed |
||||
|
representative at an online or offline event. Representation of a project may be |
||||
|
further defined and clarified by project maintainers. |
||||
|
|
||||
|
## Enforcement |
||||
|
|
||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be |
||||
|
reported by contacting Dan Buch at dan@meatballhat.com. All complaints will be |
||||
|
reviewed and investigated and will result in a response that is deemed necessary |
||||
|
and appropriate to the circumstances. The project team is obligated to maintain |
||||
|
confidentiality with regard to the reporter of an incident. Further details of |
||||
|
specific enforcement policies may be posted separately. |
||||
|
|
||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good |
||||
|
faith may face temporary or permanent repercussions as determined by other |
||||
|
members of the project's leadership. |
||||
|
|
||||
|
## Attribution |
||||
|
|
||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, |
||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html |
||||
|
|
||||
|
[homepage]: https://www.contributor-covenant.org |
||||
|
|
@ -0,0 +1,19 @@ |
|||||
|
## Contributing |
||||
|
|
||||
|
**NOTE**: the primary maintainer(s) may be found in |
||||
|
[./MAINTAINERS.md](./MAINTAINERS.md). |
||||
|
|
||||
|
Feel free to put up a pull request to fix a bug or maybe add a feature. I will |
||||
|
give it a code review and make sure that it does not break backwards |
||||
|
compatibility. If I or any other 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 master 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! |
@ -0,0 +1,21 @@ |
|||||
|
MIT License |
||||
|
|
||||
|
Copyright (c) 2016 Jeremy Saenz & Contributors |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -0,0 +1 @@ |
|||||
|
- @meatballhat |
File diff suppressed because it is too large
@ -0,0 +1,508 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"io/ioutil" |
||||
|
"os" |
||||
|
"path/filepath" |
||||
|
"sort" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" |
||||
|
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) |
||||
|
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) |
||||
|
|
||||
|
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." |
||||
|
|
||||
|
errInvalidActionType = NewExitError("ERROR invalid Action type. "+ |
||||
|
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ |
||||
|
fmt.Sprintf("See %s", appActionDeprecationURL), 2) |
||||
|
) |
||||
|
|
||||
|
// App is the main structure of a cli application. It is recommended that
|
||||
|
// an app be created with the cli.NewApp() function
|
||||
|
type App struct { |
||||
|
// The name of the program. Defaults to path.Base(os.Args[0])
|
||||
|
Name string |
||||
|
// Full name of command for help, defaults to Name
|
||||
|
HelpName string |
||||
|
// Description of the program.
|
||||
|
Usage string |
||||
|
// Text to override the USAGE section of help
|
||||
|
UsageText string |
||||
|
// Description of the program argument format.
|
||||
|
ArgsUsage string |
||||
|
// Version of the program
|
||||
|
Version string |
||||
|
// Description of the program
|
||||
|
Description string |
||||
|
// List of commands to execute
|
||||
|
Commands []Command |
||||
|
// List of flags to parse
|
||||
|
Flags []Flag |
||||
|
// Boolean to enable bash completion commands
|
||||
|
EnableBashCompletion bool |
||||
|
// Boolean to hide built-in help command
|
||||
|
HideHelp bool |
||||
|
// Boolean to hide built-in version flag and the VERSION section of help
|
||||
|
HideVersion bool |
||||
|
// Populate on app startup, only gettable through method Categories()
|
||||
|
categories CommandCategories |
||||
|
// An action to execute when the bash-completion flag is set
|
||||
|
BashComplete BashCompleteFunc |
||||
|
// An action to execute before any subcommands are run, but after the context is ready
|
||||
|
// If a non-nil error is returned, no subcommands are run
|
||||
|
Before BeforeFunc |
||||
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||
|
// It is run even if Action() panics
|
||||
|
After AfterFunc |
||||
|
|
||||
|
// The action to execute when no subcommands are specified
|
||||
|
// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
|
||||
|
// *Note*: support for the deprecated `Action` signature will be removed in a future version
|
||||
|
Action interface{} |
||||
|
|
||||
|
// Execute this function if the proper command cannot be found
|
||||
|
CommandNotFound CommandNotFoundFunc |
||||
|
// Execute this function if an usage error occurs
|
||||
|
OnUsageError OnUsageErrorFunc |
||||
|
// Compilation date
|
||||
|
Compiled time.Time |
||||
|
// List of all authors who contributed
|
||||
|
Authors []Author |
||||
|
// Copyright of the binary if any
|
||||
|
Copyright string |
||||
|
// Name of Author (Note: Use App.Authors, this is deprecated)
|
||||
|
Author string |
||||
|
// Email of Author (Note: Use App.Authors, this is deprecated)
|
||||
|
Email string |
||||
|
// Writer writer to write output to
|
||||
|
Writer io.Writer |
||||
|
// ErrWriter writes error output
|
||||
|
ErrWriter io.Writer |
||||
|
// Execute this function to handle ExitErrors. If not provided, HandleExitCoder is provided to
|
||||
|
// function as a default, so this is optional.
|
||||
|
ExitErrHandler ExitErrHandlerFunc |
||||
|
// Other custom info
|
||||
|
Metadata map[string]interface{} |
||||
|
// Carries a function which returns app specific info.
|
||||
|
ExtraInfo func() map[string]string |
||||
|
// CustomAppHelpTemplate the text template for app help topic.
|
||||
|
// cli.go uses text/template to render templates. You can
|
||||
|
// render custom help text by setting this variable.
|
||||
|
CustomAppHelpTemplate string |
||||
|
|
||||
|
didSetup bool |
||||
|
} |
||||
|
|
||||
|
// Tries to find out when this binary was compiled.
|
||||
|
// Returns the current time if it fails to find it.
|
||||
|
func compileTime() time.Time { |
||||
|
info, err := os.Stat(os.Args[0]) |
||||
|
if err != nil { |
||||
|
return time.Now() |
||||
|
} |
||||
|
return info.ModTime() |
||||
|
} |
||||
|
|
||||
|
// NewApp creates a new cli Application with some reasonable defaults for Name,
|
||||
|
// Usage, Version and Action.
|
||||
|
func NewApp() *App { |
||||
|
return &App{ |
||||
|
Name: filepath.Base(os.Args[0]), |
||||
|
HelpName: filepath.Base(os.Args[0]), |
||||
|
Usage: "A new cli application", |
||||
|
UsageText: "", |
||||
|
Version: "0.0.0", |
||||
|
BashComplete: DefaultAppComplete, |
||||
|
Action: helpCommand.Action, |
||||
|
Compiled: compileTime(), |
||||
|
Writer: os.Stdout, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Setup runs initialization code to ensure all data structures are ready for
|
||||
|
// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
|
||||
|
// will return early if setup has already happened.
|
||||
|
func (a *App) Setup() { |
||||
|
if a.didSetup { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
a.didSetup = true |
||||
|
|
||||
|
if a.Author != "" || a.Email != "" { |
||||
|
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) |
||||
|
} |
||||
|
|
||||
|
newCmds := []Command{} |
||||
|
for _, c := range a.Commands { |
||||
|
if c.HelpName == "" { |
||||
|
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) |
||||
|
} |
||||
|
newCmds = append(newCmds, c) |
||||
|
} |
||||
|
a.Commands = newCmds |
||||
|
|
||||
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp { |
||||
|
a.Commands = append(a.Commands, helpCommand) |
||||
|
if (HelpFlag != BoolFlag{}) { |
||||
|
a.appendFlag(HelpFlag) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if !a.HideVersion { |
||||
|
a.appendFlag(VersionFlag) |
||||
|
} |
||||
|
|
||||
|
a.categories = CommandCategories{} |
||||
|
for _, command := range a.Commands { |
||||
|
a.categories = a.categories.AddCommand(command.Category, command) |
||||
|
} |
||||
|
sort.Sort(a.categories) |
||||
|
|
||||
|
if a.Metadata == nil { |
||||
|
a.Metadata = make(map[string]interface{}) |
||||
|
} |
||||
|
|
||||
|
if a.Writer == nil { |
||||
|
a.Writer = os.Stdout |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
||||
|
// to the proper flag/args combination
|
||||
|
func (a *App) Run(arguments []string) (err error) { |
||||
|
a.Setup() |
||||
|
|
||||
|
// handle the completion flag separately from the flagset since
|
||||
|
// completion could be attempted after a flag, but before its value was put
|
||||
|
// on the command line. this causes the flagset to interpret the completion
|
||||
|
// flag name as the value of the flag before it which is undesirable
|
||||
|
// note that we can only do this because the shell autocomplete function
|
||||
|
// always appends the completion flag at the end of the command
|
||||
|
shellComplete, arguments := checkShellCompleteFlag(a, arguments) |
||||
|
|
||||
|
// parse flags
|
||||
|
set, err := flagSet(a.Name, a.Flags) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
set.SetOutput(ioutil.Discard) |
||||
|
err = set.Parse(arguments[1:]) |
||||
|
nerr := normalizeFlags(a.Flags, set) |
||||
|
context := NewContext(a, set, nil) |
||||
|
if nerr != nil { |
||||
|
fmt.Fprintln(a.Writer, nerr) |
||||
|
ShowAppHelp(context) |
||||
|
return nerr |
||||
|
} |
||||
|
context.shellComplete = shellComplete |
||||
|
|
||||
|
if checkCompletions(context) { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
if err != nil { |
||||
|
if a.OnUsageError != nil { |
||||
|
err := a.OnUsageError(context, err, false) |
||||
|
a.handleExitCoder(context, err) |
||||
|
return err |
||||
|
} |
||||
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) |
||||
|
ShowAppHelp(context) |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
if !a.HideHelp && checkHelp(context) { |
||||
|
ShowAppHelp(context) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
if !a.HideVersion && checkVersion(context) { |
||||
|
ShowVersion(context) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
if a.After != nil { |
||||
|
defer func() { |
||||
|
if afterErr := a.After(context); afterErr != nil { |
||||
|
if err != nil { |
||||
|
err = NewMultiError(err, afterErr) |
||||
|
} else { |
||||
|
err = afterErr |
||||
|
} |
||||
|
} |
||||
|
}() |
||||
|
} |
||||
|
|
||||
|
if a.Before != nil { |
||||
|
beforeErr := a.Before(context) |
||||
|
if beforeErr != nil { |
||||
|
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr) |
||||
|
ShowAppHelp(context) |
||||
|
a.handleExitCoder(context, beforeErr) |
||||
|
err = beforeErr |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
args := context.Args() |
||||
|
if args.Present() { |
||||
|
name := args.First() |
||||
|
c := a.Command(name) |
||||
|
if c != nil { |
||||
|
return c.Run(context) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if a.Action == nil { |
||||
|
a.Action = helpCommand.Action |
||||
|
} |
||||
|
|
||||
|
// Run default Action
|
||||
|
err = HandleAction(a.Action, context) |
||||
|
|
||||
|
a.handleExitCoder(context, err) |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
|
||||
|
//
|
||||
|
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
|
||||
|
// to cli.App.Run. This will cause the application to exit with the given eror
|
||||
|
// code in the cli.ExitCoder
|
||||
|
func (a *App) RunAndExitOnError() { |
||||
|
if err := a.Run(os.Args); err != nil { |
||||
|
fmt.Fprintln(a.errWriter(), err) |
||||
|
OsExiter(1) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
|
||||
|
// generate command-specific flags
|
||||
|
func (a *App) RunAsSubcommand(ctx *Context) (err error) { |
||||
|
// append help to commands
|
||||
|
if len(a.Commands) > 0 { |
||||
|
if a.Command(helpCommand.Name) == nil && !a.HideHelp { |
||||
|
a.Commands = append(a.Commands, helpCommand) |
||||
|
if (HelpFlag != BoolFlag{}) { |
||||
|
a.appendFlag(HelpFlag) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
newCmds := []Command{} |
||||
|
for _, c := range a.Commands { |
||||
|
if c.HelpName == "" { |
||||
|
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) |
||||
|
} |
||||
|
newCmds = append(newCmds, c) |
||||
|
} |
||||
|
a.Commands = newCmds |
||||
|
|
||||
|
// parse flags
|
||||
|
set, err := flagSet(a.Name, a.Flags) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
set.SetOutput(ioutil.Discard) |
||||
|
err = set.Parse(ctx.Args().Tail()) |
||||
|
nerr := normalizeFlags(a.Flags, set) |
||||
|
context := NewContext(a, set, ctx) |
||||
|
|
||||
|
if nerr != nil { |
||||
|
fmt.Fprintln(a.Writer, nerr) |
||||
|
fmt.Fprintln(a.Writer) |
||||
|
if len(a.Commands) > 0 { |
||||
|
ShowSubcommandHelp(context) |
||||
|
} else { |
||||
|
ShowCommandHelp(ctx, context.Args().First()) |
||||
|
} |
||||
|
return nerr |
||||
|
} |
||||
|
|
||||
|
if checkCompletions(context) { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
if err != nil { |
||||
|
if a.OnUsageError != nil { |
||||
|
err = a.OnUsageError(context, err, true) |
||||
|
a.handleExitCoder(context, err) |
||||
|
return err |
||||
|
} |
||||
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) |
||||
|
ShowSubcommandHelp(context) |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
if len(a.Commands) > 0 { |
||||
|
if checkSubcommandHelp(context) { |
||||
|
return nil |
||||
|
} |
||||
|
} else { |
||||
|
if checkCommandHelp(ctx, context.Args().First()) { |
||||
|
return nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if a.After != nil { |
||||
|
defer func() { |
||||
|
afterErr := a.After(context) |
||||
|
if afterErr != nil { |
||||
|
a.handleExitCoder(context, err) |
||||
|
if err != nil { |
||||
|
err = NewMultiError(err, afterErr) |
||||
|
} else { |
||||
|
err = afterErr |
||||
|
} |
||||
|
} |
||||
|
}() |
||||
|
} |
||||
|
|
||||
|
if a.Before != nil { |
||||
|
beforeErr := a.Before(context) |
||||
|
if beforeErr != nil { |
||||
|
a.handleExitCoder(context, beforeErr) |
||||
|
err = beforeErr |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
args := context.Args() |
||||
|
if args.Present() { |
||||
|
name := args.First() |
||||
|
c := a.Command(name) |
||||
|
if c != nil { |
||||
|
return c.Run(context) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Run default Action
|
||||
|
err = HandleAction(a.Action, context) |
||||
|
|
||||
|
a.handleExitCoder(context, err) |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
// Command returns the named command on App. Returns nil if the command does not exist
|
||||
|
func (a *App) Command(name string) *Command { |
||||
|
for _, c := range a.Commands { |
||||
|
if c.HasName(name) { |
||||
|
return &c |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Categories returns a slice containing all the categories with the commands they contain
|
||||
|
func (a *App) Categories() CommandCategories { |
||||
|
return a.categories |
||||
|
} |
||||
|
|
||||
|
// VisibleCategories returns a slice of categories and commands that are
|
||||
|
// Hidden=false
|
||||
|
func (a *App) VisibleCategories() []*CommandCategory { |
||||
|
ret := []*CommandCategory{} |
||||
|
for _, category := range a.categories { |
||||
|
if visible := func() *CommandCategory { |
||||
|
for _, command := range category.Commands { |
||||
|
if !command.Hidden { |
||||
|
return category |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
}(); visible != nil { |
||||
|
ret = append(ret, visible) |
||||
|
} |
||||
|
} |
||||
|
return ret |
||||
|
} |
||||
|
|
||||
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
||||
|
func (a *App) VisibleCommands() []Command { |
||||
|
ret := []Command{} |
||||
|
for _, command := range a.Commands { |
||||
|
if !command.Hidden { |
||||
|
ret = append(ret, command) |
||||
|
} |
||||
|
} |
||||
|
return ret |
||||
|
} |
||||
|
|
||||
|
// VisibleFlags returns a slice of the Flags with Hidden=false
|
||||
|
func (a *App) VisibleFlags() []Flag { |
||||
|
return visibleFlags(a.Flags) |
||||
|
} |
||||
|
|
||||
|
func (a *App) hasFlag(flag Flag) bool { |
||||
|
for _, f := range a.Flags { |
||||
|
if flag == f { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func (a *App) errWriter() io.Writer { |
||||
|
// When the app ErrWriter is nil use the package level one.
|
||||
|
if a.ErrWriter == nil { |
||||
|
return ErrWriter |
||||
|
} |
||||
|
|
||||
|
return a.ErrWriter |
||||
|
} |
||||
|
|
||||
|
func (a *App) appendFlag(flag Flag) { |
||||
|
if !a.hasFlag(flag) { |
||||
|
a.Flags = append(a.Flags, flag) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (a *App) handleExitCoder(context *Context, err error) { |
||||
|
if a.ExitErrHandler != nil { |
||||
|
a.ExitErrHandler(context, err) |
||||
|
} else { |
||||
|
HandleExitCoder(err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Author represents someone who has contributed to a cli project.
|
||||
|
type Author struct { |
||||
|
Name string // The Authors name
|
||||
|
Email string // The Authors email
|
||||
|
} |
||||
|
|
||||
|
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
|
||||
|
func (a Author) String() string { |
||||
|
e := "" |
||||
|
if a.Email != "" { |
||||
|
e = " <" + a.Email + ">" |
||||
|
} |
||||
|
|
||||
|
return fmt.Sprintf("%v%v", a.Name, e) |
||||
|
} |
||||
|
|
||||
|
// HandleAction attempts to figure out which Action signature was used. If
|
||||
|
// it's an ActionFunc or a func with the legacy signature for Action, the func
|
||||
|
// is run!
|
||||
|
func HandleAction(action interface{}, context *Context) (err error) { |
||||
|
if a, ok := action.(ActionFunc); ok { |
||||
|
return a(context) |
||||
|
} else if a, ok := action.(func(*Context) error); ok { |
||||
|
return a(context) |
||||
|
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
|
||||
|
a(context) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
return errInvalidActionType |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
version: "{build}" |
||||
|
|
||||
|
os: Windows Server 2016 |
||||
|
|
||||
|
image: Visual Studio 2017 |
||||
|
|
||||
|
clone_folder: c:\gopath\src\github.com\urfave\cli |
||||
|
|
||||
|
environment: |
||||
|
GOPATH: C:\gopath |
||||
|
GOVERSION: 1.8.x |
||||
|
PYTHON: C:\Python36-x64 |
||||
|
PYTHON_VERSION: 3.6.x |
||||
|
PYTHON_ARCH: 64 |
||||
|
|
||||
|
install: |
||||
|
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH% |
||||
|
- go version |
||||
|
- go env |
||||
|
- go get github.com/urfave/gfmrun/... |
||||
|
- go get -v -t ./... |
||||
|
|
||||
|
build_script: |
||||
|
- python runtests vet |
||||
|
- python runtests test |
||||
|
- python runtests gfmrun |
@ -0,0 +1,44 @@ |
|||||
|
package cli |
||||
|
|
||||
|
// CommandCategories is a slice of *CommandCategory.
|
||||
|
type CommandCategories []*CommandCategory |
||||
|
|
||||
|
// CommandCategory is a category containing commands.
|
||||
|
type CommandCategory struct { |
||||
|
Name string |
||||
|
Commands Commands |
||||
|
} |
||||
|
|
||||
|
func (c CommandCategories) Less(i, j int) bool { |
||||
|
return lexicographicLess(c[i].Name, c[j].Name) |
||||
|
} |
||||
|
|
||||
|
func (c CommandCategories) Len() int { |
||||
|
return len(c) |
||||
|
} |
||||
|
|
||||
|
func (c CommandCategories) Swap(i, j int) { |
||||
|
c[i], c[j] = c[j], c[i] |
||||
|
} |
||||
|
|
||||
|
// AddCommand adds a command to a category.
|
||||
|
func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { |
||||
|
for _, commandCategory := range c { |
||||
|
if commandCategory.Name == category { |
||||
|
commandCategory.Commands = append(commandCategory.Commands, command) |
||||
|
return c |
||||
|
} |
||||
|
} |
||||
|
return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) |
||||
|
} |
||||
|
|
||||
|
// VisibleCommands returns a slice of the Commands with Hidden=false
|
||||
|
func (c *CommandCategory) VisibleCommands() []Command { |
||||
|
ret := []Command{} |
||||
|
for _, command := range c.Commands { |
||||
|
if !command.Hidden { |
||||
|
ret = append(ret, command) |
||||
|
} |
||||
|
} |
||||
|
return ret |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
// Package cli provides a minimal framework for creating and organizing command line
|
||||
|
// Go applications. cli is designed to be easy to understand and write, the most simple
|
||||
|
// cli application can be written as follows:
|
||||
|
// func main() {
|
||||
|
// cli.NewApp().Run(os.Args)
|
||||
|
// }
|
||||
|
//
|
||||
|
// Of course this application does not do much, so let's make this an actual application:
|
||||
|
// func main() {
|
||||
|
// app := cli.NewApp()
|
||||
|
// app.Name = "greet"
|
||||
|
// app.Usage = "say a greeting"
|
||||
|
// app.Action = func(c *cli.Context) error {
|
||||
|
// println("Greetings")
|
||||
|
// return nil
|
||||
|
// }
|
||||
|
//
|
||||
|
// app.Run(os.Args)
|
||||
|
// }
|
||||
|
package cli |
||||
|
|
||||
|
//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
|
@ -0,0 +1,336 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import ( |
||||
|
"flag" |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
"sort" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// Command is a subcommand for a cli.App.
|
||||
|
type Command struct { |
||||
|
// The name of the command
|
||||
|
Name string |
||||
|
// short name of the command. Typically one character (deprecated, use `Aliases`)
|
||||
|
ShortName string |
||||
|
// A list of aliases for the command
|
||||
|
Aliases []string |
||||
|
// A short description of the usage of this command
|
||||
|
Usage string |
||||
|
// Custom text to show on USAGE section of help
|
||||
|
UsageText string |
||||
|
// A longer explanation of how the command works
|
||||
|
Description string |
||||
|
// A short description of the arguments of this command
|
||||
|
ArgsUsage string |
||||
|
// The category the command is part of
|
||||
|
Category string |
||||
|
// The function to call when checking for bash command completions
|
||||
|
BashComplete BashCompleteFunc |
||||
|
// An action to execute before any sub-subcommands are run, but after the context is ready
|
||||
|
// If a non-nil error is returned, no sub-subcommands are run
|
||||
|
Before BeforeFunc |
||||
|
// An action to execute after any subcommands are run, but after the subcommand has finished
|
||||
|
// It is run even if Action() panics
|
||||
|
After AfterFunc |
||||
|
// The function to call when this command is invoked
|
||||
|
Action interface{} |
||||
|
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
|
||||
|
// of deprecation period has passed, maybe?
|
||||
|
|
||||
|
// Execute this function if a usage error occurs.
|
||||
|
OnUsageError OnUsageErrorFunc |
||||
|
// List of child commands
|
||||
|
Subcommands Commands |
||||
|
// List of flags to parse
|
||||
|
Flags []Flag |
||||
|
// Treat all flags as normal arguments if true
|
||||
|
SkipFlagParsing bool |
||||
|
// Skip argument reordering which attempts to move flags before arguments,
|
||||
|
// but only works if all flags appear after all arguments. This behavior was
|
||||
|
// removed n version 2 since it only works under specific conditions so we
|
||||
|
// backport here by exposing it as an option for compatibility.
|
||||
|
SkipArgReorder bool |
||||
|
// Boolean to hide built-in help command
|
||||
|
HideHelp bool |
||||
|
// Boolean to hide this command from help or completion
|
||||
|
Hidden bool |
||||
|
// Boolean to enable short-option handling so user can combine several
|
||||
|
// single-character bool arguements into one
|
||||
|
// i.e. foobar -o -v -> foobar -ov
|
||||
|
UseShortOptionHandling bool |
||||
|
|
||||
|
// Full name of command for help, defaults to full command name, including parent commands.
|
||||
|
HelpName string |
||||
|
commandNamePath []string |
||||
|
|
||||
|
// CustomHelpTemplate the text template for the command help topic.
|
||||
|
// cli.go uses text/template to render templates. You can
|
||||
|
// render custom help text by setting this variable.
|
||||
|
CustomHelpTemplate string |
||||
|
} |
||||
|
|
||||
|
type CommandsByName []Command |
||||
|
|
||||
|
func (c CommandsByName) Len() int { |
||||
|
return len(c) |
||||
|
} |
||||
|
|
||||
|
func (c CommandsByName) Less(i, j int) bool { |
||||
|
return lexicographicLess(c[i].Name, c[j].Name) |
||||
|
} |
||||
|
|
||||
|
func (c CommandsByName) Swap(i, j int) { |
||||
|
c[i], c[j] = c[j], c[i] |
||||
|
} |
||||
|
|
||||
|
// FullName returns the full name of the command.
|
||||
|
// For subcommands this ensures that parent commands are part of the command path
|
||||
|
func (c Command) FullName() string { |
||||
|
if c.commandNamePath == nil { |
||||
|
return c.Name |
||||
|
} |
||||
|
return strings.Join(c.commandNamePath, " ") |
||||
|
} |
||||
|
|
||||
|
// Commands is a slice of Command
|
||||
|
type Commands []Command |
||||
|
|
||||
|
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
||||
|
func (c Command) Run(ctx *Context) (err error) { |
||||
|
if len(c.Subcommands) > 0 { |
||||
|
return c.startApp(ctx) |
||||
|
} |
||||
|
|
||||
|
if !c.HideHelp && (HelpFlag != BoolFlag{}) { |
||||
|
// append help to flags
|
||||
|
c.Flags = append( |
||||
|
c.Flags, |
||||
|
HelpFlag, |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
set, err := c.parseFlags(ctx.Args().Tail()) |
||||
|
|
||||
|
context := NewContext(ctx.App, set, ctx) |
||||
|
context.Command = c |
||||
|
if checkCommandCompletions(context, c.Name) { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
if err != nil { |
||||
|
if c.OnUsageError != nil { |
||||
|
err := c.OnUsageError(context, err, false) |
||||
|
context.App.handleExitCoder(context, err) |
||||
|
return err |
||||
|
} |
||||
|
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) |
||||
|
fmt.Fprintln(context.App.Writer) |
||||
|
ShowCommandHelp(context, c.Name) |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
if checkCommandHelp(context, c.Name) { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
if c.After != nil { |
||||
|
defer func() { |
||||
|
afterErr := c.After(context) |
||||
|
if afterErr != nil { |
||||
|
context.App.handleExitCoder(context, err) |
||||
|
if err != nil { |
||||
|
err = NewMultiError(err, afterErr) |
||||
|
} else { |
||||
|
err = afterErr |
||||
|
} |
||||
|
} |
||||
|
}() |
||||
|
} |
||||
|
|
||||
|
if c.Before != nil { |
||||
|
err = c.Before(context) |
||||
|
if err != nil { |
||||
|
ShowCommandHelp(context, c.Name) |
||||
|
context.App.handleExitCoder(context, err) |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if c.Action == nil { |
||||
|
c.Action = helpSubcommand.Action |
||||
|
} |
||||
|
|
||||
|
err = HandleAction(c.Action, context) |
||||
|
|
||||
|
if err != nil { |
||||
|
context.App.handleExitCoder(context, err) |
||||
|
} |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { |
||||
|
set, err := flagSet(c.Name, c.Flags) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
set.SetOutput(ioutil.Discard) |
||||
|
|
||||
|
if c.SkipFlagParsing { |
||||
|
return set, set.Parse(append([]string{"--"}, args...)) |
||||
|
} |
||||
|
|
||||
|
if c.UseShortOptionHandling { |
||||
|
args = translateShortOptions(args) |
||||
|
} |
||||
|
|
||||
|
if !c.SkipArgReorder { |
||||
|
args = reorderArgs(args) |
||||
|
} |
||||
|
|
||||
|
err = set.Parse(args) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = normalizeFlags(c.Flags, set) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return set, nil |
||||
|
} |
||||
|
|
||||
|
// reorderArgs moves all flags before arguments as this is what flag expects
|
||||
|
func reorderArgs(args []string) []string { |
||||
|
var nonflags, flags []string |
||||
|
|
||||
|
readFlagValue := false |
||||
|
for i, arg := range args { |
||||
|
if arg == "--" { |
||||
|
nonflags = append(nonflags, args[i:]...) |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") { |
||||
|
readFlagValue = false |
||||
|
flags = append(flags, arg) |
||||
|
continue |
||||
|
} |
||||
|
readFlagValue = false |
||||
|
|
||||
|
if arg != "-" && strings.HasPrefix(arg, "-") { |
||||
|
flags = append(flags, arg) |
||||
|
|
||||
|
readFlagValue = !strings.Contains(arg, "=") |
||||
|
} else { |
||||
|
nonflags = append(nonflags, arg) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return append(flags, nonflags...) |
||||
|
} |
||||
|
|
||||
|
func translateShortOptions(flagArgs Args) []string { |
||||
|
// separate combined flags
|
||||
|
var flagArgsSeparated []string |
||||
|
for _, flagArg := range flagArgs { |
||||
|
if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 { |
||||
|
for _, flagChar := range flagArg[1:] { |
||||
|
flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar)) |
||||
|
} |
||||
|
} else { |
||||
|
flagArgsSeparated = append(flagArgsSeparated, flagArg) |
||||
|
} |
||||
|
} |
||||
|
return flagArgsSeparated |
||||
|
} |
||||
|
|
||||
|
// Names returns the names including short names and aliases.
|
||||
|
func (c Command) Names() []string { |
||||
|
names := []string{c.Name} |
||||
|
|
||||
|
if c.ShortName != "" { |
||||
|
names = append(names, c.ShortName) |
||||
|
} |
||||
|
|
||||
|
return append(names, c.Aliases...) |
||||
|
} |
||||
|
|
||||
|
// HasName returns true if Command.Name or Command.ShortName matches given name
|
||||
|
func (c Command) HasName(name string) bool { |
||||
|
for _, n := range c.Names() { |
||||
|
if n == name { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func (c Command) startApp(ctx *Context) error { |
||||
|
app := NewApp() |
||||
|
app.Metadata = ctx.App.Metadata |
||||
|
// set the name and usage
|
||||
|
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) |
||||
|
if c.HelpName == "" { |
||||
|
app.HelpName = c.HelpName |
||||
|
} else { |
||||
|
app.HelpName = app.Name |
||||
|
} |
||||
|
|
||||
|
app.Usage = c.Usage |
||||
|
app.Description = c.Description |
||||
|
app.ArgsUsage = c.ArgsUsage |
||||
|
|
||||
|
// set CommandNotFound
|
||||
|
app.CommandNotFound = ctx.App.CommandNotFound |
||||
|
app.CustomAppHelpTemplate = c.CustomHelpTemplate |
||||
|
|
||||
|
// set the flags and commands
|
||||
|
app.Commands = c.Subcommands |
||||
|
app.Flags = c.Flags |
||||
|
app.HideHelp = c.HideHelp |
||||
|
|
||||
|
app.Version = ctx.App.Version |
||||
|
app.HideVersion = ctx.App.HideVersion |
||||
|
app.Compiled = ctx.App.Compiled |
||||
|
app.Author = ctx.App.Author |
||||
|
app.Email = ctx.App.Email |
||||
|
app.Writer = ctx.App.Writer |
||||
|
app.ErrWriter = ctx.App.ErrWriter |
||||
|
|
||||
|
app.categories = CommandCategories{} |
||||
|
for _, command := range c.Subcommands { |
||||
|
app.categories = app.categories.AddCommand(command.Category, command) |
||||
|
} |
||||
|
|
||||
|
sort.Sort(app.categories) |
||||
|
|
||||
|
// bash completion
|
||||
|
app.EnableBashCompletion = ctx.App.EnableBashCompletion |
||||
|
if c.BashComplete != nil { |
||||
|
app.BashComplete = c.BashComplete |
||||
|
} |
||||
|
|
||||
|
// set the actions
|
||||
|
app.Before = c.Before |
||||
|
app.After = c.After |
||||
|
if c.Action != nil { |
||||
|
app.Action = c.Action |
||||
|
} else { |
||||
|
app.Action = helpSubcommand.Action |
||||
|
} |
||||
|
app.OnUsageError = c.OnUsageError |
||||
|
|
||||
|
for index, cc := range app.Commands { |
||||
|
app.Commands[index].commandNamePath = []string{c.Name, cc.Name} |
||||
|
} |
||||
|
|
||||
|
return app.RunAsSubcommand(ctx) |
||||
|
} |
||||
|
|
||||
|
// VisibleFlags returns a slice of the Flags with Hidden=false
|
||||
|
func (c Command) VisibleFlags() []Flag { |
||||
|
return visibleFlags(c.Flags) |
||||
|
} |
@ -0,0 +1,287 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"flag" |
||||
|
"os" |
||||
|
"reflect" |
||||
|
"strings" |
||||
|
"syscall" |
||||
|
) |
||||
|
|
||||
|
// Context is a type that is passed through to
|
||||
|
// each Handler action in a cli application. Context
|
||||
|
// can be used to retrieve context-specific Args and
|
||||
|
// parsed command-line options.
|
||||
|
type Context struct { |
||||
|
App *App |
||||
|
Command Command |
||||
|
shellComplete bool |
||||
|
flagSet *flag.FlagSet |
||||
|
setFlags map[string]bool |
||||
|
parentContext *Context |
||||
|
} |
||||
|
|
||||
|
// NewContext creates a new context. For use in when invoking an App or Command action.
|
||||
|
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { |
||||
|
c := &Context{App: app, flagSet: set, parentContext: parentCtx} |
||||
|
|
||||
|
if parentCtx != nil { |
||||
|
c.shellComplete = parentCtx.shellComplete |
||||
|
} |
||||
|
|
||||
|
return c |
||||
|
} |
||||
|
|
||||
|
// NumFlags returns the number of flags set
|
||||
|
func (c *Context) NumFlags() int { |
||||
|
return c.flagSet.NFlag() |
||||
|
} |
||||
|
|
||||
|
// Set sets a context flag to a value.
|
||||
|
func (c *Context) Set(name, value string) error { |
||||
|
c.setFlags = nil |
||||
|
return c.flagSet.Set(name, value) |
||||
|
} |
||||
|
|
||||
|
// GlobalSet sets a context flag to a value on the global flagset
|
||||
|
func (c *Context) GlobalSet(name, value string) error { |
||||
|
globalContext(c).setFlags = nil |
||||
|
return globalContext(c).flagSet.Set(name, value) |
||||
|
} |
||||
|
|
||||
|
// IsSet determines if the flag was actually set
|
||||
|
func (c *Context) IsSet(name string) bool { |
||||
|
if c.setFlags == nil { |
||||
|
c.setFlags = make(map[string]bool) |
||||
|
|
||||
|
c.flagSet.Visit(func(f *flag.Flag) { |
||||
|
c.setFlags[f.Name] = true |
||||
|
}) |
||||
|
|
||||
|
c.flagSet.VisitAll(func(f *flag.Flag) { |
||||
|
if _, ok := c.setFlags[f.Name]; ok { |
||||
|
return |
||||
|
} |
||||
|
c.setFlags[f.Name] = false |
||||
|
}) |
||||
|
|
||||
|
// XXX hack to support IsSet for flags with EnvVar
|
||||
|
//
|
||||
|
// There isn't an easy way to do this with the current implementation since
|
||||
|
// whether a flag was set via an environment variable is very difficult to
|
||||
|
// determine here. Instead, we intend to introduce a backwards incompatible
|
||||
|
// change in version 2 to add `IsSet` to the Flag interface to push the
|
||||
|
// responsibility closer to where the information required to determine
|
||||
|
// whether a flag is set by non-standard means such as environment
|
||||
|
// variables is available.
|
||||
|
//
|
||||
|
// See https://github.com/urfave/cli/issues/294 for additional discussion
|
||||
|
flags := c.Command.Flags |
||||
|
if c.Command.Name == "" { // cannot == Command{} since it contains slice types
|
||||
|
if c.App != nil { |
||||
|
flags = c.App.Flags |
||||
|
} |
||||
|
} |
||||
|
for _, f := range flags { |
||||
|
eachName(f.GetName(), func(name string) { |
||||
|
if isSet, ok := c.setFlags[name]; isSet || !ok { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
val := reflect.ValueOf(f) |
||||
|
if val.Kind() == reflect.Ptr { |
||||
|
val = val.Elem() |
||||
|
} |
||||
|
|
||||
|
filePathValue := val.FieldByName("FilePath") |
||||
|
if filePathValue.IsValid() { |
||||
|
eachName(filePathValue.String(), func(filePath string) { |
||||
|
if _, err := os.Stat(filePath); err == nil { |
||||
|
c.setFlags[name] = true |
||||
|
return |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
envVarValue := val.FieldByName("EnvVar") |
||||
|
if envVarValue.IsValid() { |
||||
|
eachName(envVarValue.String(), func(envVar string) { |
||||
|
envVar = strings.TrimSpace(envVar) |
||||
|
if _, ok := syscall.Getenv(envVar); ok { |
||||
|
c.setFlags[name] = true |
||||
|
return |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return c.setFlags[name] |
||||
|
} |
||||
|
|
||||
|
// GlobalIsSet determines if the global flag was actually set
|
||||
|
func (c *Context) GlobalIsSet(name string) bool { |
||||
|
ctx := c |
||||
|
if ctx.parentContext != nil { |
||||
|
ctx = ctx.parentContext |
||||
|
} |
||||
|
|
||||
|
for ; ctx != nil; ctx = ctx.parentContext { |
||||
|
if ctx.IsSet(name) { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// FlagNames returns a slice of flag names used in this context.
|
||||
|
func (c *Context) FlagNames() (names []string) { |
||||
|
for _, flag := range c.Command.Flags { |
||||
|
name := strings.Split(flag.GetName(), ",")[0] |
||||
|
if name == "help" { |
||||
|
continue |
||||
|
} |
||||
|
names = append(names, name) |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// GlobalFlagNames returns a slice of global flag names used by the app.
|
||||
|
func (c *Context) GlobalFlagNames() (names []string) { |
||||
|
for _, flag := range c.App.Flags { |
||||
|
name := strings.Split(flag.GetName(), ",")[0] |
||||
|
if name == "help" || name == "version" { |
||||
|
continue |
||||
|
} |
||||
|
names = append(names, name) |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Parent returns the parent context, if any
|
||||
|
func (c *Context) Parent() *Context { |
||||
|
return c.parentContext |
||||
|
} |
||||
|
|
||||
|
// value returns the value of the flag coressponding to `name`
|
||||
|
func (c *Context) value(name string) interface{} { |
||||
|
return c.flagSet.Lookup(name).Value.(flag.Getter).Get() |
||||
|
} |
||||
|
|
||||
|
// Args contains apps console arguments
|
||||
|
type Args []string |
||||
|
|
||||
|
// Args returns the command line arguments associated with the context.
|
||||
|
func (c *Context) Args() Args { |
||||
|
args := Args(c.flagSet.Args()) |
||||
|
return args |
||||
|
} |
||||
|
|
||||
|
// NArg returns the number of the command line arguments.
|
||||
|
func (c *Context) NArg() int { |
||||
|
return len(c.Args()) |
||||
|
} |
||||
|
|
||||
|
// Get returns the nth argument, or else a blank string
|
||||
|
func (a Args) Get(n int) string { |
||||
|
if len(a) > n { |
||||
|
return a[n] |
||||
|
} |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
// First returns the first argument, or else a blank string
|
||||
|
func (a Args) First() string { |
||||
|
return a.Get(0) |
||||
|
} |
||||
|
|
||||
|
// Tail returns the rest of the arguments (not the first one)
|
||||
|
// or else an empty string slice
|
||||
|
func (a Args) Tail() []string { |
||||
|
if len(a) >= 2 { |
||||
|
return []string(a)[1:] |
||||
|
} |
||||
|
return []string{} |
||||
|
} |
||||
|
|
||||
|
// Present checks if there are any arguments present
|
||||
|
func (a Args) Present() bool { |
||||
|
return len(a) != 0 |
||||
|
} |
||||
|
|
||||
|
// Swap swaps arguments at the given indexes
|
||||
|
func (a Args) Swap(from, to int) error { |
||||
|
if from >= len(a) || to >= len(a) { |
||||
|
return errors.New("index out of range") |
||||
|
} |
||||
|
a[from], a[to] = a[to], a[from] |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func globalContext(ctx *Context) *Context { |
||||
|
if ctx == nil { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
for { |
||||
|
if ctx.parentContext == nil { |
||||
|
return ctx |
||||
|
} |
||||
|
ctx = ctx.parentContext |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { |
||||
|
if ctx.parentContext != nil { |
||||
|
ctx = ctx.parentContext |
||||
|
} |
||||
|
for ; ctx != nil; ctx = ctx.parentContext { |
||||
|
if f := ctx.flagSet.Lookup(name); f != nil { |
||||
|
return ctx.flagSet |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { |
||||
|
switch ff.Value.(type) { |
||||
|
case *StringSlice: |
||||
|
default: |
||||
|
set.Set(name, ff.Value.String()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func normalizeFlags(flags []Flag, set *flag.FlagSet) error { |
||||
|
visited := make(map[string]bool) |
||||
|
set.Visit(func(f *flag.Flag) { |
||||
|
visited[f.Name] = true |
||||
|
}) |
||||
|
for _, f := range flags { |
||||
|
parts := strings.Split(f.GetName(), ",") |
||||
|
if len(parts) == 1 { |
||||
|
continue |
||||
|
} |
||||
|
var ff *flag.Flag |
||||
|
for _, name := range parts { |
||||
|
name = strings.Trim(name, " ") |
||||
|
if visited[name] { |
||||
|
if ff != nil { |
||||
|
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) |
||||
|
} |
||||
|
ff = set.Lookup(name) |
||||
|
} |
||||
|
} |
||||
|
if ff == nil { |
||||
|
continue |
||||
|
} |
||||
|
for _, name := range parts { |
||||
|
name = strings.Trim(name, " ") |
||||
|
if !visited[name] { |
||||
|
copyFlag(name, ff, set) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
} |
@ -0,0 +1,115 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"os" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
|
||||
|
var OsExiter = os.Exit |
||||
|
|
||||
|
// ErrWriter is used to write errors to the user. This can be anything
|
||||
|
// implementing the io.Writer interface and defaults to os.Stderr.
|
||||
|
var ErrWriter io.Writer = os.Stderr |
||||
|
|
||||
|
// MultiError is an error that wraps multiple errors.
|
||||
|
type MultiError struct { |
||||
|
Errors []error |
||||
|
} |
||||
|
|
||||
|
// NewMultiError creates a new MultiError. Pass in one or more errors.
|
||||
|
func NewMultiError(err ...error) MultiError { |
||||
|
return MultiError{Errors: err} |
||||
|
} |
||||
|
|
||||
|
// Error implements the error interface.
|
||||
|
func (m MultiError) Error() string { |
||||
|
errs := make([]string, len(m.Errors)) |
||||
|
for i, err := range m.Errors { |
||||
|
errs[i] = err.Error() |
||||
|
} |
||||
|
|
||||
|
return strings.Join(errs, "\n") |
||||
|
} |
||||
|
|
||||
|
type ErrorFormatter interface { |
||||
|
Format(s fmt.State, verb rune) |
||||
|
} |
||||
|
|
||||
|
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
||||
|
// code
|
||||
|
type ExitCoder interface { |
||||
|
error |
||||
|
ExitCode() int |
||||
|
} |
||||
|
|
||||
|
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
||||
|
type ExitError struct { |
||||
|
exitCode int |
||||
|
message interface{} |
||||
|
} |
||||
|
|
||||
|
// NewExitError makes a new *ExitError
|
||||
|
func NewExitError(message interface{}, exitCode int) *ExitError { |
||||
|
return &ExitError{ |
||||
|
exitCode: exitCode, |
||||
|
message: message, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Error returns the string message, fulfilling the interface required by
|
||||
|
// `error`
|
||||
|
func (ee *ExitError) Error() string { |
||||
|
return fmt.Sprintf("%v", ee.message) |
||||
|
} |
||||
|
|
||||
|
// ExitCode returns the exit code, fulfilling the interface required by
|
||||
|
// `ExitCoder`
|
||||
|
func (ee *ExitError) ExitCode() int { |
||||
|
return ee.exitCode |
||||
|
} |
||||
|
|
||||
|
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
||||
|
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
||||
|
// given exit code. If the given error is a MultiError, then this func is
|
||||
|
// called on all members of the Errors slice and calls OsExiter with the last exit code.
|
||||
|
func HandleExitCoder(err error) { |
||||
|
if err == nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if exitErr, ok := err.(ExitCoder); ok { |
||||
|
if err.Error() != "" { |
||||
|
if _, ok := exitErr.(ErrorFormatter); ok { |
||||
|
fmt.Fprintf(ErrWriter, "%+v\n", err) |
||||
|
} else { |
||||
|
fmt.Fprintln(ErrWriter, err) |
||||
|
} |
||||
|
} |
||||
|
OsExiter(exitErr.ExitCode()) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if multiErr, ok := err.(MultiError); ok { |
||||
|
code := handleMultiError(multiErr) |
||||
|
OsExiter(code) |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func handleMultiError(multiErr MultiError) int { |
||||
|
code := 1 |
||||
|
for _, merr := range multiErr.Errors { |
||||
|
if multiErr2, ok := merr.(MultiError); ok { |
||||
|
code = handleMultiError(multiErr2) |
||||
|
} else { |
||||
|
fmt.Fprintln(ErrWriter, merr) |
||||
|
if exitErr, ok := merr.(ExitCoder); ok { |
||||
|
code = exitErr.ExitCode() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return code |
||||
|
} |
@ -0,0 +1,93 @@ |
|||||
|
[ |
||||
|
{ |
||||
|
"name": "Bool", |
||||
|
"type": "bool", |
||||
|
"value": false, |
||||
|
"context_default": "false", |
||||
|
"parser": "strconv.ParseBool(f.Value.String())" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "BoolT", |
||||
|
"type": "bool", |
||||
|
"value": false, |
||||
|
"doctail": " that is true by default", |
||||
|
"context_default": "false", |
||||
|
"parser": "strconv.ParseBool(f.Value.String())" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Duration", |
||||
|
"type": "time.Duration", |
||||
|
"doctail": " (see https://golang.org/pkg/time/#ParseDuration)", |
||||
|
"context_default": "0", |
||||
|
"parser": "time.ParseDuration(f.Value.String())" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Float64", |
||||
|
"type": "float64", |
||||
|
"context_default": "0", |
||||
|
"parser": "strconv.ParseFloat(f.Value.String(), 64)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Generic", |
||||
|
"type": "Generic", |
||||
|
"dest": false, |
||||
|
"context_default": "nil", |
||||
|
"context_type": "interface{}" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Int64", |
||||
|
"type": "int64", |
||||
|
"context_default": "0", |
||||
|
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Int", |
||||
|
"type": "int", |
||||
|
"context_default": "0", |
||||
|
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)", |
||||
|
"parser_cast": "int(parsed)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "IntSlice", |
||||
|
"type": "*IntSlice", |
||||
|
"dest": false, |
||||
|
"context_default": "nil", |
||||
|
"context_type": "[]int", |
||||
|
"parser": "(f.Value.(*IntSlice)).Value(), error(nil)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Int64Slice", |
||||
|
"type": "*Int64Slice", |
||||
|
"dest": false, |
||||
|
"context_default": "nil", |
||||
|
"context_type": "[]int64", |
||||
|
"parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "String", |
||||
|
"type": "string", |
||||
|
"context_default": "\"\"", |
||||
|
"parser": "f.Value.String(), error(nil)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "StringSlice", |
||||
|
"type": "*StringSlice", |
||||
|
"dest": false, |
||||
|
"context_default": "nil", |
||||
|
"context_type": "[]string", |
||||
|
"parser": "(f.Value.(*StringSlice)).Value(), error(nil)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Uint64", |
||||
|
"type": "uint64", |
||||
|
"context_default": "0", |
||||
|
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Uint", |
||||
|
"type": "uint", |
||||
|
"context_default": "0", |
||||
|
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)", |
||||
|
"parser_cast": "uint(parsed)" |
||||
|
} |
||||
|
] |
@ -0,0 +1,786 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import ( |
||||
|
"flag" |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
"reflect" |
||||
|
"runtime" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
"syscall" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
const defaultPlaceholder = "value" |
||||
|
|
||||
|
// BashCompletionFlag enables bash-completion for all commands and subcommands
|
||||
|
var BashCompletionFlag Flag = BoolFlag{ |
||||
|
Name: "generate-bash-completion", |
||||
|
Hidden: true, |
||||
|
} |
||||
|
|
||||
|
// VersionFlag prints the version for the application
|
||||
|
var VersionFlag Flag = BoolFlag{ |
||||
|
Name: "version, v", |
||||
|
Usage: "print the version", |
||||
|
} |
||||
|
|
||||
|
// HelpFlag prints the help for all commands and subcommands
|
||||
|
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
||||
|
// unless HideHelp is set to true)
|
||||
|
var HelpFlag Flag = BoolFlag{ |
||||
|
Name: "help, h", |
||||
|
Usage: "show help", |
||||
|
} |
||||
|
|
||||
|
// FlagStringer converts a flag definition to a string. This is used by help
|
||||
|
// to display a flag.
|
||||
|
var FlagStringer FlagStringFunc = stringifyFlag |
||||
|
|
||||
|
// FlagNamePrefixer converts a full flag name and its placeholder into the help
|
||||
|
// message flag prefix. This is used by the default FlagStringer.
|
||||
|
var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames |
||||
|
|
||||
|
// FlagEnvHinter annotates flag help message with the environment variable
|
||||
|
// details. This is used by the default FlagStringer.
|
||||
|
var FlagEnvHinter FlagEnvHintFunc = withEnvHint |
||||
|
|
||||
|
// FlagFileHinter annotates flag help message with the environment variable
|
||||
|
// details. This is used by the default FlagStringer.
|
||||
|
var FlagFileHinter FlagFileHintFunc = withFileHint |
||||
|
|
||||
|
// FlagsByName is a slice of Flag.
|
||||
|
type FlagsByName []Flag |
||||
|
|
||||
|
func (f FlagsByName) Len() int { |
||||
|
return len(f) |
||||
|
} |
||||
|
|
||||
|
func (f FlagsByName) Less(i, j int) bool { |
||||
|
return lexicographicLess(f[i].GetName(), f[j].GetName()) |
||||
|
} |
||||
|
|
||||
|
func (f FlagsByName) Swap(i, j int) { |
||||
|
f[i], f[j] = f[j], f[i] |
||||
|
} |
||||
|
|
||||
|
// Flag is a common interface related to parsing flags in cli.
|
||||
|
// For more advanced flag parsing techniques, it is recommended that
|
||||
|
// this interface be implemented.
|
||||
|
type Flag interface { |
||||
|
fmt.Stringer |
||||
|
// Apply Flag settings to the given flag set
|
||||
|
Apply(*flag.FlagSet) |
||||
|
GetName() string |
||||
|
} |
||||
|
|
||||
|
// errorableFlag is an interface that allows us to return errors during apply
|
||||
|
// it allows flags defined in this library to return errors in a fashion backwards compatible
|
||||
|
// TODO remove in v2 and modify the existing Flag interface to return errors
|
||||
|
type errorableFlag interface { |
||||
|
Flag |
||||
|
|
||||
|
ApplyWithError(*flag.FlagSet) error |
||||
|
} |
||||
|
|
||||
|
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { |
||||
|
set := flag.NewFlagSet(name, flag.ContinueOnError) |
||||
|
|
||||
|
for _, f := range flags { |
||||
|
//TODO remove in v2 when errorableFlag is removed
|
||||
|
if ef, ok := f.(errorableFlag); ok { |
||||
|
if err := ef.ApplyWithError(set); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
} else { |
||||
|
f.Apply(set) |
||||
|
} |
||||
|
} |
||||
|
return set, nil |
||||
|
} |
||||
|
|
||||
|
func eachName(longName string, fn func(string)) { |
||||
|
parts := strings.Split(longName, ",") |
||||
|
for _, name := range parts { |
||||
|
name = strings.Trim(name, " ") |
||||
|
fn(name) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Generic is a generic parseable type identified by a specific flag
|
||||
|
type Generic interface { |
||||
|
Set(value string) error |
||||
|
String() string |
||||
|
} |
||||
|
|
||||
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||
|
// provided by the user for parsing by the flag
|
||||
|
// Ignores parsing errors
|
||||
|
func (f GenericFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError takes the flagset and calls Set on the generic flag with the value
|
||||
|
// provided by the user for parsing by the flag
|
||||
|
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
val := f.Value |
||||
|
if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
if err := val.Set(fileEnvVal); err != nil { |
||||
|
return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
set.Var(f.Value, name, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
|
||||
|
type StringSlice []string |
||||
|
|
||||
|
// Set appends the string value to the list of values
|
||||
|
func (f *StringSlice) Set(value string) error { |
||||
|
*f = append(*f, value) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value (for usage defaults)
|
||||
|
func (f *StringSlice) String() string { |
||||
|
return fmt.Sprintf("%s", *f) |
||||
|
} |
||||
|
|
||||
|
// Value returns the slice of strings set by this flag
|
||||
|
func (f *StringSlice) Value() []string { |
||||
|
return *f |
||||
|
} |
||||
|
|
||||
|
// Get returns the slice of strings set by this flag
|
||||
|
func (f *StringSlice) Get() interface{} { |
||||
|
return *f |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f StringSliceFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
newVal := &StringSlice{} |
||||
|
for _, s := range strings.Split(envVal, ",") { |
||||
|
s = strings.TrimSpace(s) |
||||
|
if err := newVal.Set(s); err != nil { |
||||
|
return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
} |
||||
|
if f.Value == nil { |
||||
|
f.Value = newVal |
||||
|
} else { |
||||
|
*f.Value = *newVal |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Value == nil { |
||||
|
f.Value = &StringSlice{} |
||||
|
} |
||||
|
set.Var(f.Value, name, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||
|
type IntSlice []int |
||||
|
|
||||
|
// Set parses the value into an integer and appends it to the list of values
|
||||
|
func (f *IntSlice) Set(value string) error { |
||||
|
tmp, err := strconv.Atoi(value) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
*f = append(*f, tmp) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value (for usage defaults)
|
||||
|
func (f *IntSlice) String() string { |
||||
|
return fmt.Sprintf("%#v", *f) |
||||
|
} |
||||
|
|
||||
|
// Value returns the slice of ints set by this flag
|
||||
|
func (f *IntSlice) Value() []int { |
||||
|
return *f |
||||
|
} |
||||
|
|
||||
|
// Get returns the slice of ints set by this flag
|
||||
|
func (f *IntSlice) Get() interface{} { |
||||
|
return *f |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f IntSliceFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
newVal := &IntSlice{} |
||||
|
for _, s := range strings.Split(envVal, ",") { |
||||
|
s = strings.TrimSpace(s) |
||||
|
if err := newVal.Set(s); err != nil { |
||||
|
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
} |
||||
|
if f.Value == nil { |
||||
|
f.Value = newVal |
||||
|
} else { |
||||
|
*f.Value = *newVal |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Value == nil { |
||||
|
f.Value = &IntSlice{} |
||||
|
} |
||||
|
set.Var(f.Value, name, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
||||
|
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("%#v", *f) |
||||
|
} |
||||
|
|
||||
|
// Value returns the slice of ints set by this flag
|
||||
|
func (f *Int64Slice) Value() []int64 { |
||||
|
return *f |
||||
|
} |
||||
|
|
||||
|
// Get returns the slice of ints set by this flag
|
||||
|
func (f *Int64Slice) Get() interface{} { |
||||
|
return *f |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f Int64SliceFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
newVal := &Int64Slice{} |
||||
|
for _, s := range strings.Split(envVal, ",") { |
||||
|
s = strings.TrimSpace(s) |
||||
|
if err := newVal.Set(s); err != nil { |
||||
|
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
} |
||||
|
if f.Value == nil { |
||||
|
f.Value = newVal |
||||
|
} else { |
||||
|
*f.Value = *newVal |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Value == nil { |
||||
|
f.Value = &Int64Slice{} |
||||
|
} |
||||
|
set.Var(f.Value, name, f.Usage) |
||||
|
}) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f BoolFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
val := false |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
if envVal == "" { |
||||
|
val = false |
||||
|
} else { |
||||
|
envValBool, err := strconv.ParseBool(envVal) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
val = envValBool |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Destination != nil { |
||||
|
set.BoolVar(f.Destination, name, val, f.Usage) |
||||
|
return |
||||
|
} |
||||
|
set.Bool(name, val, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f BoolTFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
val := true |
||||
|
|
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
if envVal == "" { |
||||
|
val = false |
||||
|
} else { |
||||
|
envValBool, err := strconv.ParseBool(envVal) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
val = envValBool |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Destination != nil { |
||||
|
set.BoolVar(f.Destination, name, val, f.Usage) |
||||
|
return |
||||
|
} |
||||
|
set.Bool(name, val, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f StringFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
f.Value = envVal |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Destination != nil { |
||||
|
set.StringVar(f.Destination, name, f.Value, f.Usage) |
||||
|
return |
||||
|
} |
||||
|
set.String(name, f.Value, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f IntFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
envValInt, err := strconv.ParseInt(envVal, 0, 64) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
f.Value = int(envValInt) |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Destination != nil { |
||||
|
set.IntVar(f.Destination, name, f.Value, f.Usage) |
||||
|
return |
||||
|
} |
||||
|
set.Int(name, f.Value, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f Int64Flag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
envValInt, err := strconv.ParseInt(envVal, 0, 64) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
|
||||
|
f.Value = envValInt |
||||
|
} |
||||
|
|
||||
|
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) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f UintFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
envValInt, err := strconv.ParseUint(envVal, 0, 64) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
|
||||
|
f.Value = uint(envValInt) |
||||
|
} |
||||
|
|
||||
|
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) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f Uint64Flag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
envValInt, err := strconv.ParseUint(envVal, 0, 64) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
|
||||
|
f.Value = uint64(envValInt) |
||||
|
} |
||||
|
|
||||
|
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) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f DurationFlag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
envValDuration, err := time.ParseDuration(envVal) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
|
||||
|
f.Value = envValDuration |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Destination != nil { |
||||
|
set.DurationVar(f.Destination, name, f.Value, f.Usage) |
||||
|
return |
||||
|
} |
||||
|
set.Duration(name, f.Value, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Apply populates the flag given the flag set and environment
|
||||
|
// Ignores errors
|
||||
|
func (f Float64Flag) Apply(set *flag.FlagSet) { |
||||
|
f.ApplyWithError(set) |
||||
|
} |
||||
|
|
||||
|
// ApplyWithError populates the flag given the flag set and environment
|
||||
|
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { |
||||
|
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { |
||||
|
envValFloat, err := strconv.ParseFloat(envVal, 10) |
||||
|
if err != nil { |
||||
|
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) |
||||
|
} |
||||
|
|
||||
|
f.Value = float64(envValFloat) |
||||
|
} |
||||
|
|
||||
|
eachName(f.Name, func(name string) { |
||||
|
if f.Destination != nil { |
||||
|
set.Float64Var(f.Destination, name, f.Value, f.Usage) |
||||
|
return |
||||
|
} |
||||
|
set.Float64(name, f.Value, f.Usage) |
||||
|
}) |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func visibleFlags(fl []Flag) []Flag { |
||||
|
visible := []Flag{} |
||||
|
for _, flag := range fl { |
||||
|
field := flagValue(flag).FieldByName("Hidden") |
||||
|
if !field.IsValid() || !field.Bool() { |
||||
|
visible = append(visible, flag) |
||||
|
} |
||||
|
} |
||||
|
return visible |
||||
|
} |
||||
|
|
||||
|
func prefixFor(name string) (prefix string) { |
||||
|
if len(name) == 1 { |
||||
|
prefix = "-" |
||||
|
} else { |
||||
|
prefix = "--" |
||||
|
} |
||||
|
|
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Returns the placeholder, if any, and the unquoted usage string.
|
||||
|
func unquoteUsage(usage string) (string, string) { |
||||
|
for i := 0; i < len(usage); i++ { |
||||
|
if usage[i] == '`' { |
||||
|
for j := i + 1; j < len(usage); j++ { |
||||
|
if usage[j] == '`' { |
||||
|
name := usage[i+1 : j] |
||||
|
usage = usage[:i] + name + usage[j+1:] |
||||
|
return name, usage |
||||
|
} |
||||
|
} |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
return "", usage |
||||
|
} |
||||
|
|
||||
|
func prefixedNames(fullName, placeholder string) string { |
||||
|
var prefixed string |
||||
|
parts := strings.Split(fullName, ",") |
||||
|
for i, name := range parts { |
||||
|
name = strings.Trim(name, " ") |
||||
|
prefixed += prefixFor(name) + name |
||||
|
if placeholder != "" { |
||||
|
prefixed += " " + placeholder |
||||
|
} |
||||
|
if i < len(parts)-1 { |
||||
|
prefixed += ", " |
||||
|
} |
||||
|
} |
||||
|
return prefixed |
||||
|
} |
||||
|
|
||||
|
func withEnvHint(envVar, str string) string { |
||||
|
envText := "" |
||||
|
if envVar != "" { |
||||
|
prefix := "$" |
||||
|
suffix := "" |
||||
|
sep := ", $" |
||||
|
if runtime.GOOS == "windows" { |
||||
|
prefix = "%" |
||||
|
suffix = "%" |
||||
|
sep = "%, %" |
||||
|
} |
||||
|
envText = " [" + prefix + strings.Join(strings.Split(envVar, ","), sep) + suffix + "]" |
||||
|
} |
||||
|
return str + envText |
||||
|
} |
||||
|
|
||||
|
func withFileHint(filePath, str string) string { |
||||
|
fileText := "" |
||||
|
if filePath != "" { |
||||
|
fileText = fmt.Sprintf(" [%s]", filePath) |
||||
|
} |
||||
|
return str + fileText |
||||
|
} |
||||
|
|
||||
|
func flagValue(f Flag) reflect.Value { |
||||
|
fv := reflect.ValueOf(f) |
||||
|
for fv.Kind() == reflect.Ptr { |
||||
|
fv = reflect.Indirect(fv) |
||||
|
} |
||||
|
return fv |
||||
|
} |
||||
|
|
||||
|
func stringifyFlag(f Flag) string { |
||||
|
fv := flagValue(f) |
||||
|
|
||||
|
switch f.(type) { |
||||
|
case IntSliceFlag: |
||||
|
return FlagFileHinter( |
||||
|
fv.FieldByName("FilePath").String(), |
||||
|
FlagEnvHinter( |
||||
|
fv.FieldByName("EnvVar").String(), |
||||
|
stringifyIntSliceFlag(f.(IntSliceFlag)), |
||||
|
), |
||||
|
) |
||||
|
case Int64SliceFlag: |
||||
|
return FlagFileHinter( |
||||
|
fv.FieldByName("FilePath").String(), |
||||
|
FlagEnvHinter( |
||||
|
fv.FieldByName("EnvVar").String(), |
||||
|
stringifyInt64SliceFlag(f.(Int64SliceFlag)), |
||||
|
), |
||||
|
) |
||||
|
case StringSliceFlag: |
||||
|
return FlagFileHinter( |
||||
|
fv.FieldByName("FilePath").String(), |
||||
|
FlagEnvHinter( |
||||
|
fv.FieldByName("EnvVar").String(), |
||||
|
stringifyStringSliceFlag(f.(StringSliceFlag)), |
||||
|
), |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) |
||||
|
|
||||
|
needsPlaceholder := false |
||||
|
defaultValueString := "" |
||||
|
|
||||
|
if val := fv.FieldByName("Value"); val.IsValid() { |
||||
|
needsPlaceholder = true |
||||
|
defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface()) |
||||
|
|
||||
|
if val.Kind() == reflect.String && val.String() != "" { |
||||
|
defaultValueString = fmt.Sprintf(" (default: %q)", val.String()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if defaultValueString == " (default: )" { |
||||
|
defaultValueString = "" |
||||
|
} |
||||
|
|
||||
|
if needsPlaceholder && placeholder == "" { |
||||
|
placeholder = defaultPlaceholder |
||||
|
} |
||||
|
|
||||
|
usageWithDefault := strings.TrimSpace(usage + defaultValueString) |
||||
|
|
||||
|
return FlagFileHinter( |
||||
|
fv.FieldByName("FilePath").String(), |
||||
|
FlagEnvHinter( |
||||
|
fv.FieldByName("EnvVar").String(), |
||||
|
FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder)+"\t"+usageWithDefault, |
||||
|
), |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
func stringifyIntSliceFlag(f IntSliceFlag) string { |
||||
|
defaultVals := []string{} |
||||
|
if f.Value != nil && len(f.Value.Value()) > 0 { |
||||
|
for _, i := range f.Value.Value() { |
||||
|
defaultVals = append(defaultVals, strconv.Itoa(i)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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, strconv.FormatInt(i, 10)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return stringifySliceFlag(f.Usage, f.Name, defaultVals) |
||||
|
} |
||||
|
|
||||
|
func stringifyStringSliceFlag(f StringSliceFlag) string { |
||||
|
defaultVals := []string{} |
||||
|
if f.Value != nil && len(f.Value.Value()) > 0 { |
||||
|
for _, s := range f.Value.Value() { |
||||
|
if len(s) > 0 { |
||||
|
defaultVals = append(defaultVals, strconv.Quote(s)) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return stringifySliceFlag(f.Usage, f.Name, defaultVals) |
||||
|
} |
||||
|
|
||||
|
func stringifySliceFlag(usage, name string, defaultVals []string) string { |
||||
|
placeholder, usage := unquoteUsage(usage) |
||||
|
if placeholder == "" { |
||||
|
placeholder = defaultPlaceholder |
||||
|
} |
||||
|
|
||||
|
defaultVal := "" |
||||
|
if len(defaultVals) > 0 { |
||||
|
defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", ")) |
||||
|
} |
||||
|
|
||||
|
usageWithDefault := strings.TrimSpace(usage + defaultVal) |
||||
|
return FlagNamePrefixer(name, placeholder) + "\t" + usageWithDefault |
||||
|
} |
||||
|
|
||||
|
func flagFromFileEnv(filePath, envName string) (val string, ok bool) { |
||||
|
for _, envVar := range strings.Split(envName, ",") { |
||||
|
envVar = strings.TrimSpace(envVar) |
||||
|
if envVal, ok := syscall.Getenv(envVar); ok { |
||||
|
return envVal, true |
||||
|
} |
||||
|
} |
||||
|
for _, fileVar := range strings.Split(filePath, ",") { |
||||
|
if data, err := ioutil.ReadFile(fileVar); err == nil { |
||||
|
return string(data), true |
||||
|
} |
||||
|
} |
||||
|
return "", false |
||||
|
} |
@ -0,0 +1,640 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import ( |
||||
|
"flag" |
||||
|
"strconv" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
// WARNING: This file is generated!
|
||||
|
|
||||
|
// BoolFlag is a flag with type bool
|
||||
|
type BoolFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Destination *bool |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f BoolFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f BoolFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Bool looks up the value of a local BoolFlag, returns
|
||||
|
// false if not found
|
||||
|
func (c *Context) Bool(name string) bool { |
||||
|
return lookupBool(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalBool looks up the value of a global BoolFlag, returns
|
||||
|
// false if not found
|
||||
|
func (c *Context) GlobalBool(name string) bool { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupBool(name, fs) |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func lookupBool(name string, set *flag.FlagSet) bool { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := strconv.ParseBool(f.Value.String()) |
||||
|
if err != nil { |
||||
|
return false |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// BoolTFlag is a flag with type bool that is true by default
|
||||
|
type BoolTFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Destination *bool |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f BoolTFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f BoolTFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// BoolT looks up the value of a local BoolTFlag, returns
|
||||
|
// false if not found
|
||||
|
func (c *Context) BoolT(name string) bool { |
||||
|
return lookupBoolT(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalBoolT looks up the value of a global BoolTFlag, returns
|
||||
|
// false if not found
|
||||
|
func (c *Context) GlobalBoolT(name string) bool { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupBoolT(name, fs) |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func lookupBoolT(name string, set *flag.FlagSet) bool { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := strconv.ParseBool(f.Value.String()) |
||||
|
if err != nil { |
||||
|
return false |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
|
||||
|
type DurationFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value time.Duration |
||||
|
Destination *time.Duration |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f DurationFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f DurationFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Duration looks up the value of a local DurationFlag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) Duration(name string) time.Duration { |
||||
|
return lookupDuration(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalDuration looks up the value of a global DurationFlag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) GlobalDuration(name string) time.Duration { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupDuration(name, fs) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
func lookupDuration(name string, set *flag.FlagSet) time.Duration { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := time.ParseDuration(f.Value.String()) |
||||
|
if err != nil { |
||||
|
return 0 |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
// Float64Flag is a flag with type float64
|
||||
|
type Float64Flag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value float64 |
||||
|
Destination *float64 |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f Float64Flag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f Float64Flag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Float64 looks up the value of a local Float64Flag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) Float64(name string) float64 { |
||||
|
return lookupFloat64(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalFloat64 looks up the value of a global Float64Flag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) GlobalFloat64(name string) float64 { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupFloat64(name, fs) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
func lookupFloat64(name string, set *flag.FlagSet) float64 { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := strconv.ParseFloat(f.Value.String(), 64) |
||||
|
if err != nil { |
||||
|
return 0 |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
// GenericFlag is a flag with type Generic
|
||||
|
type GenericFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value Generic |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f GenericFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f GenericFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Generic looks up the value of a local GenericFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) Generic(name string) interface{} { |
||||
|
return lookupGeneric(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalGeneric looks up the value of a global GenericFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) GlobalGeneric(name string) interface{} { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupGeneric(name, fs) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func lookupGeneric(name string, set *flag.FlagSet) interface{} { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := f.Value, error(nil) |
||||
|
if err != nil { |
||||
|
return nil |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Int64Flag is a flag with type int64
|
||||
|
type Int64Flag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value int64 |
||||
|
Destination *int64 |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f Int64Flag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f Int64Flag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Int64 looks up the value of a local Int64Flag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) Int64(name string) int64 { |
||||
|
return lookupInt64(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalInt64 looks up the value of a global Int64Flag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) GlobalInt64(name string) int64 { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupInt64(name, fs) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
func lookupInt64(name string, set *flag.FlagSet) int64 { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) |
||||
|
if err != nil { |
||||
|
return 0 |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
// IntFlag is a flag with type int
|
||||
|
type IntFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value int |
||||
|
Destination *int |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f IntFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f IntFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Int looks up the value of a local IntFlag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) Int(name string) int { |
||||
|
return lookupInt(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalInt looks up the value of a global IntFlag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) GlobalInt(name string) int { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupInt(name, fs) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
func lookupInt(name string, set *flag.FlagSet) int { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) |
||||
|
if err != nil { |
||||
|
return 0 |
||||
|
} |
||||
|
return int(parsed) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
// IntSliceFlag is a flag with type *IntSlice
|
||||
|
type IntSliceFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value *IntSlice |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f IntSliceFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f IntSliceFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// IntSlice looks up the value of a local IntSliceFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) IntSlice(name string) []int { |
||||
|
return lookupIntSlice(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalIntSlice looks up the value of a global IntSliceFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) GlobalIntSlice(name string) []int { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupIntSlice(name, fs) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func lookupIntSlice(name string, set *flag.FlagSet) []int { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := (f.Value.(*IntSlice)).Value(), error(nil) |
||||
|
if err != nil { |
||||
|
return nil |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Int64SliceFlag is a flag with type *Int64Slice
|
||||
|
type Int64SliceFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value *Int64Slice |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f Int64SliceFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f Int64SliceFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Int64Slice looks up the value of a local Int64SliceFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) Int64Slice(name string) []int64 { |
||||
|
return lookupInt64Slice(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) GlobalInt64Slice(name string) []int64 { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupInt64Slice(name, fs) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil) |
||||
|
if err != nil { |
||||
|
return nil |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// StringFlag is a flag with type string
|
||||
|
type StringFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value string |
||||
|
Destination *string |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f StringFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f StringFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// String looks up the value of a local StringFlag, returns
|
||||
|
// "" if not found
|
||||
|
func (c *Context) String(name string) string { |
||||
|
return lookupString(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalString looks up the value of a global StringFlag, returns
|
||||
|
// "" if not found
|
||||
|
func (c *Context) GlobalString(name string) string { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupString(name, fs) |
||||
|
} |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
func lookupString(name string, set *flag.FlagSet) string { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := f.Value.String(), error(nil) |
||||
|
if err != nil { |
||||
|
return "" |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return "" |
||||
|
} |
||||
|
|
||||
|
// StringSliceFlag is a flag with type *StringSlice
|
||||
|
type StringSliceFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value *StringSlice |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f StringSliceFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f StringSliceFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// StringSlice looks up the value of a local StringSliceFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) StringSlice(name string) []string { |
||||
|
return lookupStringSlice(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalStringSlice looks up the value of a global StringSliceFlag, returns
|
||||
|
// nil if not found
|
||||
|
func (c *Context) GlobalStringSlice(name string) []string { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupStringSlice(name, fs) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func lookupStringSlice(name string, set *flag.FlagSet) []string { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := (f.Value.(*StringSlice)).Value(), error(nil) |
||||
|
if err != nil { |
||||
|
return nil |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// Uint64Flag is a flag with type uint64
|
||||
|
type Uint64Flag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value uint64 |
||||
|
Destination *uint64 |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f Uint64Flag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f Uint64Flag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Uint64 looks up the value of a local Uint64Flag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) Uint64(name string) uint64 { |
||||
|
return lookupUint64(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalUint64 looks up the value of a global Uint64Flag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) GlobalUint64(name string) uint64 { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupUint64(name, fs) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
func lookupUint64(name string, set *flag.FlagSet) uint64 { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) |
||||
|
if err != nil { |
||||
|
return 0 |
||||
|
} |
||||
|
return parsed |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
// UintFlag is a flag with type uint
|
||||
|
type UintFlag struct { |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
Value uint |
||||
|
Destination *uint |
||||
|
} |
||||
|
|
||||
|
// String returns a readable representation of this value
|
||||
|
// (for usage defaults)
|
||||
|
func (f UintFlag) String() string { |
||||
|
return FlagStringer(f) |
||||
|
} |
||||
|
|
||||
|
// GetName returns the name of the flag
|
||||
|
func (f UintFlag) GetName() string { |
||||
|
return f.Name |
||||
|
} |
||||
|
|
||||
|
// Uint looks up the value of a local UintFlag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) Uint(name string) uint { |
||||
|
return lookupUint(name, c.flagSet) |
||||
|
} |
||||
|
|
||||
|
// GlobalUint looks up the value of a global UintFlag, returns
|
||||
|
// 0 if not found
|
||||
|
func (c *Context) GlobalUint(name string) uint { |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil { |
||||
|
return lookupUint(name, fs) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
||||
|
|
||||
|
func lookupUint(name string, set *flag.FlagSet) uint { |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil { |
||||
|
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) |
||||
|
if err != nil { |
||||
|
return 0 |
||||
|
} |
||||
|
return uint(parsed) |
||||
|
} |
||||
|
return 0 |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
package cli |
||||
|
|
||||
|
// BashCompleteFunc is an action to execute when the bash-completion flag is set
|
||||
|
type BashCompleteFunc func(*Context) |
||||
|
|
||||
|
// BeforeFunc is an action to execute before any subcommands are run, but after
|
||||
|
// the context is ready if a non-nil error is returned, no subcommands are run
|
||||
|
type BeforeFunc func(*Context) error |
||||
|
|
||||
|
// AfterFunc is an action to execute after any subcommands are run, but after the
|
||||
|
// subcommand has finished it is run even if Action() panics
|
||||
|
type AfterFunc func(*Context) error |
||||
|
|
||||
|
// ActionFunc is the action to execute when no subcommands are specified
|
||||
|
type ActionFunc func(*Context) error |
||||
|
|
||||
|
// CommandNotFoundFunc is executed if the proper command cannot be found
|
||||
|
type CommandNotFoundFunc func(*Context, string) |
||||
|
|
||||
|
// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
|
||||
|
// customized usage error messages. This function is able to replace the
|
||||
|
// original error messages. If this function is not set, the "Incorrect usage"
|
||||
|
// is displayed and the execution is interrupted.
|
||||
|
type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error |
||||
|
|
||||
|
// ExitErrHandlerFunc is executed if provided in order to handle ExitError values
|
||||
|
// returned by Actions and Before/After functions.
|
||||
|
type ExitErrHandlerFunc func(context *Context, err error) |
||||
|
|
||||
|
// FlagStringFunc is used by the help generation to display a flag, which is
|
||||
|
// expected to be a single line.
|
||||
|
type FlagStringFunc func(Flag) string |
||||
|
|
||||
|
// FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix
|
||||
|
// text for a flag's full name.
|
||||
|
type FlagNamePrefixFunc func(fullName, placeholder string) string |
||||
|
|
||||
|
// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help
|
||||
|
// with the environment variable details.
|
||||
|
type FlagEnvHintFunc func(envVar, str string) string |
||||
|
|
||||
|
// FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help
|
||||
|
// with the file path details.
|
||||
|
type FlagFileHintFunc func(filePath, str string) string |
@ -0,0 +1,256 @@ |
|||||
|
#!/usr/bin/env python |
||||
|
""" |
||||
|
The flag types that ship with the cli library have many things in common, and |
||||
|
so we can take advantage of the `go generate` command to create much of the |
||||
|
source code from a list of definitions. These definitions attempt to cover |
||||
|
the parts that vary between flag types, and should evolve as needed. |
||||
|
|
||||
|
An example of the minimum definition needed is: |
||||
|
|
||||
|
{ |
||||
|
"name": "SomeType", |
||||
|
"type": "sometype", |
||||
|
"context_default": "nil" |
||||
|
} |
||||
|
|
||||
|
In this example, the code generated for the `cli` package will include a type |
||||
|
named `SomeTypeFlag` that is expected to wrap a value of type `sometype`. |
||||
|
Fetching values by name via `*cli.Context` will default to a value of `nil`. |
||||
|
|
||||
|
A more complete, albeit somewhat redundant, example showing all available |
||||
|
definition keys is: |
||||
|
|
||||
|
{ |
||||
|
"name": "VeryMuchType", |
||||
|
"type": "*VeryMuchType", |
||||
|
"value": true, |
||||
|
"dest": false, |
||||
|
"doctail": " which really only wraps a []float64, oh well!", |
||||
|
"context_type": "[]float64", |
||||
|
"context_default": "nil", |
||||
|
"parser": "parseVeryMuchType(f.Value.String())", |
||||
|
"parser_cast": "[]float64(parsed)" |
||||
|
} |
||||
|
|
||||
|
The meaning of each field is as follows: |
||||
|
|
||||
|
name (string) - The type "name", which will be suffixed with |
||||
|
`Flag` when generating the type definition |
||||
|
for `cli` and the wrapper type for `altsrc` |
||||
|
type (string) - The type that the generated `Flag` type for `cli` |
||||
|
is expected to "contain" as its `.Value` member |
||||
|
value (bool) - Should the generated `cli` type have a `Value` |
||||
|
member? |
||||
|
dest (bool) - Should the generated `cli` type support a |
||||
|
destination pointer? |
||||
|
doctail (string) - Additional docs for the `cli` flag type comment |
||||
|
context_type (string) - The literal type used in the `*cli.Context` |
||||
|
reader func signature |
||||
|
context_default (string) - The literal value used as the default by the |
||||
|
`*cli.Context` reader funcs when no value is |
||||
|
present |
||||
|
parser (string) - Literal code used to parse the flag `f`, |
||||
|
expected to have a return signature of |
||||
|
(value, error) |
||||
|
parser_cast (string) - Literal code used to cast the `parsed` value |
||||
|
returned from the `parser` code |
||||
|
""" |
||||
|
|
||||
|
from __future__ import print_function, unicode_literals |
||||
|
|
||||
|
import argparse |
||||
|
import json |
||||
|
import os |
||||
|
import subprocess |
||||
|
import sys |
||||
|
import tempfile |
||||
|
import textwrap |
||||
|
|
||||
|
|
||||
|
class _FancyFormatter(argparse.ArgumentDefaultsHelpFormatter, |
||||
|
argparse.RawDescriptionHelpFormatter): |
||||
|
pass |
||||
|
|
||||
|
|
||||
|
def main(sysargs=sys.argv[:]): |
||||
|
parser = argparse.ArgumentParser( |
||||
|
description='Generate flag type code!', |
||||
|
formatter_class=_FancyFormatter) |
||||
|
parser.add_argument( |
||||
|
'package', |
||||
|
type=str, default='cli', choices=_WRITEFUNCS.keys(), |
||||
|
help='Package for which flag types will be generated' |
||||
|
) |
||||
|
parser.add_argument( |
||||
|
'-i', '--in-json', |
||||
|
type=argparse.FileType('r'), |
||||
|
default=sys.stdin, |
||||
|
help='Input JSON file which defines each type to be generated' |
||||
|
) |
||||
|
parser.add_argument( |
||||
|
'-o', '--out-go', |
||||
|
type=argparse.FileType('w'), |
||||
|
default=sys.stdout, |
||||
|
help='Output file/stream to which generated source will be written' |
||||
|
) |
||||
|
parser.epilog = __doc__ |
||||
|
|
||||
|
args = parser.parse_args(sysargs[1:]) |
||||
|
_generate_flag_types(_WRITEFUNCS[args.package], args.out_go, args.in_json) |
||||
|
return 0 |
||||
|
|
||||
|
|
||||
|
def _generate_flag_types(writefunc, output_go, input_json): |
||||
|
types = json.load(input_json) |
||||
|
|
||||
|
tmp = tempfile.NamedTemporaryFile(suffix='.go', delete=False) |
||||
|
writefunc(tmp, types) |
||||
|
tmp.close() |
||||
|
|
||||
|
new_content = subprocess.check_output( |
||||
|
['goimports', tmp.name] |
||||
|
).decode('utf-8') |
||||
|
|
||||
|
print(new_content, file=output_go, end='') |
||||
|
output_go.flush() |
||||
|
os.remove(tmp.name) |
||||
|
|
||||
|
|
||||
|
def _set_typedef_defaults(typedef): |
||||
|
typedef.setdefault('doctail', '') |
||||
|
typedef.setdefault('context_type', typedef['type']) |
||||
|
typedef.setdefault('dest', True) |
||||
|
typedef.setdefault('value', True) |
||||
|
typedef.setdefault('parser', 'f.Value, error(nil)') |
||||
|
typedef.setdefault('parser_cast', 'parsed') |
||||
|
|
||||
|
|
||||
|
def _write_cli_flag_types(outfile, types): |
||||
|
_fwrite(outfile, """\ |
||||
|
package cli |
||||
|
|
||||
|
// WARNING: This file is generated! |
||||
|
|
||||
|
""") |
||||
|
|
||||
|
for typedef in types: |
||||
|
_set_typedef_defaults(typedef) |
||||
|
|
||||
|
_fwrite(outfile, """\ |
||||
|
// {name}Flag is a flag with type {type}{doctail} |
||||
|
type {name}Flag struct {{ |
||||
|
Name string |
||||
|
Usage string |
||||
|
EnvVar string |
||||
|
FilePath string |
||||
|
Hidden bool |
||||
|
""".format(**typedef)) |
||||
|
|
||||
|
if typedef['value']: |
||||
|
_fwrite(outfile, """\ |
||||
|
Value {type} |
||||
|
""".format(**typedef)) |
||||
|
|
||||
|
if typedef['dest']: |
||||
|
_fwrite(outfile, """\ |
||||
|
Destination *{type} |
||||
|
""".format(**typedef)) |
||||
|
|
||||
|
_fwrite(outfile, "\n}\n\n") |
||||
|
|
||||
|
_fwrite(outfile, """\ |
||||
|
// String returns a readable representation of this value |
||||
|
// (for usage defaults) |
||||
|
func (f {name}Flag) String() string {{ |
||||
|
return FlagStringer(f) |
||||
|
}} |
||||
|
|
||||
|
// GetName returns the name of the flag |
||||
|
func (f {name}Flag) GetName() string {{ |
||||
|
return f.Name |
||||
|
}} |
||||
|
|
||||
|
// {name} looks up the value of a local {name}Flag, returns |
||||
|
// {context_default} if not found |
||||
|
func (c *Context) {name}(name string) {context_type} {{ |
||||
|
return lookup{name}(name, c.flagSet) |
||||
|
}} |
||||
|
|
||||
|
// Global{name} looks up the value of a global {name}Flag, returns |
||||
|
// {context_default} if not found |
||||
|
func (c *Context) Global{name}(name string) {context_type} {{ |
||||
|
if fs := lookupGlobalFlagSet(name, c); fs != nil {{ |
||||
|
return lookup{name}(name, fs) |
||||
|
}} |
||||
|
return {context_default} |
||||
|
}} |
||||
|
|
||||
|
func lookup{name}(name string, set *flag.FlagSet) {context_type} {{ |
||||
|
f := set.Lookup(name) |
||||
|
if f != nil {{ |
||||
|
parsed, err := {parser} |
||||
|
if err != nil {{ |
||||
|
return {context_default} |
||||
|
}} |
||||
|
return {parser_cast} |
||||
|
}} |
||||
|
return {context_default} |
||||
|
}} |
||||
|
""".format(**typedef)) |
||||
|
|
||||
|
|
||||
|
def _write_altsrc_flag_types(outfile, types): |
||||
|
_fwrite(outfile, """\ |
||||
|
package altsrc |
||||
|
|
||||
|
import ( |
||||
|
"gopkg.in/urfave/cli.v1" |
||||
|
) |
||||
|
|
||||
|
// WARNING: This file is generated! |
||||
|
|
||||
|
""") |
||||
|
|
||||
|
for typedef in types: |
||||
|
_set_typedef_defaults(typedef) |
||||
|
|
||||
|
_fwrite(outfile, """\ |
||||
|
// {name}Flag is the flag type that wraps cli.{name}Flag to allow |
||||
|
// for other values to be specified |
||||
|
type {name}Flag struct {{ |
||||
|
cli.{name}Flag |
||||
|
set *flag.FlagSet |
||||
|
}} |
||||
|
|
||||
|
// New{name}Flag creates a new {name}Flag |
||||
|
func New{name}Flag(fl cli.{name}Flag) *{name}Flag {{ |
||||
|
return &{name}Flag{{{name}Flag: fl, set: nil}} |
||||
|
}} |
||||
|
|
||||
|
// Apply saves the flagSet for later usage calls, then calls the |
||||
|
// wrapped {name}Flag.Apply |
||||
|
func (f *{name}Flag) Apply(set *flag.FlagSet) {{ |
||||
|
f.set = set |
||||
|
f.{name}Flag.Apply(set) |
||||
|
}} |
||||
|
|
||||
|
// ApplyWithError saves the flagSet for later usage calls, then calls the |
||||
|
// wrapped {name}Flag.ApplyWithError |
||||
|
func (f *{name}Flag) ApplyWithError(set *flag.FlagSet) error {{ |
||||
|
f.set = set |
||||
|
return f.{name}Flag.ApplyWithError(set) |
||||
|
}} |
||||
|
""".format(**typedef)) |
||||
|
|
||||
|
|
||||
|
def _fwrite(outfile, text): |
||||
|
print(textwrap.dedent(text), end='', file=outfile) |
||||
|
|
||||
|
|
||||
|
_WRITEFUNCS = { |
||||
|
'cli': _write_cli_flag_types, |
||||
|
'altsrc': _write_altsrc_flag_types |
||||
|
} |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.exit(main()) |
@ -0,0 +1,345 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"os" |
||||
|
"strings" |
||||
|
"text/tabwriter" |
||||
|
"text/template" |
||||
|
) |
||||
|
|
||||
|
// AppHelpTemplate is the text template for the Default help topic.
|
||||
|
// cli.go uses text/template to render templates. You can
|
||||
|
// render custom help text by setting this variable.
|
||||
|
var AppHelpTemplate = `NAME: |
||||
|
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}} |
||||
|
|
||||
|
USAGE: |
||||
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} |
||||
|
|
||||
|
VERSION: |
||||
|
{{.Version}}{{end}}{{end}}{{if .Description}} |
||||
|
|
||||
|
DESCRIPTION: |
||||
|
{{.Description}}{{end}}{{if len .Authors}} |
||||
|
|
||||
|
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: |
||||
|
{{range $index, $author := .Authors}}{{if $index}} |
||||
|
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} |
||||
|
|
||||
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}} |
||||
|
|
||||
|
{{.Name}}:{{end}}{{range .VisibleCommands}} |
||||
|
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} |
||||
|
|
||||
|
GLOBAL OPTIONS: |
||||
|
{{range $index, $option := .VisibleFlags}}{{if $index}} |
||||
|
{{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} |
||||
|
|
||||
|
COPYRIGHT: |
||||
|
{{.Copyright}}{{end}} |
||||
|
` |
||||
|
|
||||
|
// CommandHelpTemplate is the text template for the command help topic.
|
||||
|
// cli.go uses text/template to render templates. You can
|
||||
|
// render custom help text by setting this variable.
|
||||
|
var CommandHelpTemplate = `NAME: |
||||
|
{{.HelpName}} - {{.Usage}} |
||||
|
|
||||
|
USAGE: |
||||
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} |
||||
|
|
||||
|
CATEGORY: |
||||
|
{{.Category}}{{end}}{{if .Description}} |
||||
|
|
||||
|
DESCRIPTION: |
||||
|
{{.Description}}{{end}}{{if .VisibleFlags}} |
||||
|
|
||||
|
OPTIONS: |
||||
|
{{range .VisibleFlags}}{{.}} |
||||
|
{{end}}{{end}} |
||||
|
` |
||||
|
|
||||
|
// SubcommandHelpTemplate is the text template for the subcommand help topic.
|
||||
|
// cli.go uses text/template to render templates. You can
|
||||
|
// render custom help text by setting this variable.
|
||||
|
var SubcommandHelpTemplate = `NAME: |
||||
|
{{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} |
||||
|
|
||||
|
USAGE: |
||||
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} |
||||
|
|
||||
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}} |
||||
|
{{.Name}}:{{end}}{{range .VisibleCommands}} |
||||
|
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}} |
||||
|
{{end}}{{if .VisibleFlags}} |
||||
|
OPTIONS: |
||||
|
{{range .VisibleFlags}}{{.}} |
||||
|
{{end}}{{end}} |
||||
|
` |
||||
|
|
||||
|
var helpCommand = Command{ |
||||
|
Name: "help", |
||||
|
Aliases: []string{"h"}, |
||||
|
Usage: "Shows a list of commands or help for one command", |
||||
|
ArgsUsage: "[command]", |
||||
|
Action: func(c *Context) error { |
||||
|
args := c.Args() |
||||
|
if args.Present() { |
||||
|
return ShowCommandHelp(c, args.First()) |
||||
|
} |
||||
|
|
||||
|
ShowAppHelp(c) |
||||
|
return nil |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
var helpSubcommand = Command{ |
||||
|
Name: "help", |
||||
|
Aliases: []string{"h"}, |
||||
|
Usage: "Shows a list of commands or help for one command", |
||||
|
ArgsUsage: "[command]", |
||||
|
Action: func(c *Context) error { |
||||
|
args := c.Args() |
||||
|
if args.Present() { |
||||
|
return ShowCommandHelp(c, args.First()) |
||||
|
} |
||||
|
|
||||
|
return ShowSubcommandHelp(c) |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
// Prints help for the App or Command
|
||||
|
type helpPrinter func(w io.Writer, templ string, data interface{}) |
||||
|
|
||||
|
// Prints help for the App or Command with custom template function.
|
||||
|
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) |
||||
|
|
||||
|
// HelpPrinter is a function that writes the help output. If not set a default
|
||||
|
// is used. The function signature is:
|
||||
|
// func(w io.Writer, templ string, data interface{})
|
||||
|
var HelpPrinter helpPrinter = printHelp |
||||
|
|
||||
|
// HelpPrinterCustom is same as HelpPrinter but
|
||||
|
// takes a custom function for template function map.
|
||||
|
var HelpPrinterCustom helpPrinterCustom = printHelpCustom |
||||
|
|
||||
|
// VersionPrinter prints the version for the App
|
||||
|
var VersionPrinter = printVersion |
||||
|
|
||||
|
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
|
||||
|
func ShowAppHelpAndExit(c *Context, exitCode int) { |
||||
|
ShowAppHelp(c) |
||||
|
os.Exit(exitCode) |
||||
|
} |
||||
|
|
||||
|
// ShowAppHelp is an action that displays the help.
|
||||
|
func ShowAppHelp(c *Context) (err error) { |
||||
|
if c.App.CustomAppHelpTemplate == "" { |
||||
|
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) |
||||
|
return |
||||
|
} |
||||
|
customAppData := func() map[string]interface{} { |
||||
|
if c.App.ExtraInfo == nil { |
||||
|
return nil |
||||
|
} |
||||
|
return map[string]interface{}{ |
||||
|
"ExtraInfo": c.App.ExtraInfo, |
||||
|
} |
||||
|
} |
||||
|
HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData()) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
||||
|
func DefaultAppComplete(c *Context) { |
||||
|
for _, command := range c.App.Commands { |
||||
|
if command.Hidden { |
||||
|
continue |
||||
|
} |
||||
|
if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { |
||||
|
for _, name := range command.Names() { |
||||
|
fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) |
||||
|
} |
||||
|
} else { |
||||
|
for _, name := range command.Names() { |
||||
|
fmt.Fprintf(c.App.Writer, "%s\n", name) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ShowCommandHelpAndExit - exits with code after showing help
|
||||
|
func ShowCommandHelpAndExit(c *Context, command string, code int) { |
||||
|
ShowCommandHelp(c, command) |
||||
|
os.Exit(code) |
||||
|
} |
||||
|
|
||||
|
// ShowCommandHelp prints help for the given command
|
||||
|
func ShowCommandHelp(ctx *Context, command string) error { |
||||
|
// show the subcommand help for a command with subcommands
|
||||
|
if command == "" { |
||||
|
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
for _, c := range ctx.App.Commands { |
||||
|
if c.HasName(command) { |
||||
|
if c.CustomHelpTemplate != "" { |
||||
|
HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil) |
||||
|
} else { |
||||
|
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ctx.App.CommandNotFound == nil { |
||||
|
return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) |
||||
|
} |
||||
|
|
||||
|
ctx.App.CommandNotFound(ctx, command) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// ShowSubcommandHelp prints help for the given subcommand
|
||||
|
func ShowSubcommandHelp(c *Context) error { |
||||
|
return ShowCommandHelp(c, c.Command.Name) |
||||
|
} |
||||
|
|
||||
|
// ShowVersion prints the version number of the App
|
||||
|
func ShowVersion(c *Context) { |
||||
|
VersionPrinter(c) |
||||
|
} |
||||
|
|
||||
|
func printVersion(c *Context) { |
||||
|
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) |
||||
|
} |
||||
|
|
||||
|
// ShowCompletions prints the lists of commands within a given context
|
||||
|
func ShowCompletions(c *Context) { |
||||
|
a := c.App |
||||
|
if a != nil && a.BashComplete != nil { |
||||
|
a.BashComplete(c) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// ShowCommandCompletions prints the custom completions for a given command
|
||||
|
func ShowCommandCompletions(ctx *Context, command string) { |
||||
|
c := ctx.App.Command(command) |
||||
|
if c != nil && c.BashComplete != nil { |
||||
|
c.BashComplete(ctx) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { |
||||
|
funcMap := template.FuncMap{ |
||||
|
"join": strings.Join, |
||||
|
} |
||||
|
if customFunc != nil { |
||||
|
for key, value := range customFunc { |
||||
|
funcMap[key] = value |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) |
||||
|
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) |
||||
|
err := t.Execute(w, data) |
||||
|
if err != nil { |
||||
|
// If the writer is closed, t.Execute will fail, and there's nothing
|
||||
|
// we can do to recover.
|
||||
|
if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { |
||||
|
fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
w.Flush() |
||||
|
} |
||||
|
|
||||
|
func printHelp(out io.Writer, templ string, data interface{}) { |
||||
|
printHelpCustom(out, templ, data, nil) |
||||
|
} |
||||
|
|
||||
|
func checkVersion(c *Context) bool { |
||||
|
found := false |
||||
|
if VersionFlag.GetName() != "" { |
||||
|
eachName(VersionFlag.GetName(), func(name string) { |
||||
|
if c.GlobalBool(name) || c.Bool(name) { |
||||
|
found = true |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
return found |
||||
|
} |
||||
|
|
||||
|
func checkHelp(c *Context) bool { |
||||
|
found := false |
||||
|
if HelpFlag.GetName() != "" { |
||||
|
eachName(HelpFlag.GetName(), func(name string) { |
||||
|
if c.GlobalBool(name) || c.Bool(name) { |
||||
|
found = true |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
return found |
||||
|
} |
||||
|
|
||||
|
func checkCommandHelp(c *Context, name string) bool { |
||||
|
if c.Bool("h") || c.Bool("help") { |
||||
|
ShowCommandHelp(c, name) |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func checkSubcommandHelp(c *Context) bool { |
||||
|
if c.Bool("h") || c.Bool("help") { |
||||
|
ShowSubcommandHelp(c) |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { |
||||
|
if !a.EnableBashCompletion { |
||||
|
return false, arguments |
||||
|
} |
||||
|
|
||||
|
pos := len(arguments) - 1 |
||||
|
lastArg := arguments[pos] |
||||
|
|
||||
|
if lastArg != "--"+BashCompletionFlag.GetName() { |
||||
|
return false, arguments |
||||
|
} |
||||
|
|
||||
|
return true, arguments[:pos] |
||||
|
} |
||||
|
|
||||
|
func checkCompletions(c *Context) bool { |
||||
|
if !c.shellComplete { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
if args := c.Args(); args.Present() { |
||||
|
name := args.First() |
||||
|
if cmd := c.App.Command(name); cmd != nil { |
||||
|
// let the command handle the completion
|
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ShowCompletions(c) |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
func checkCommandCompletions(c *Context, name string) bool { |
||||
|
if !c.shellComplete { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
ShowCommandCompletions(c, name) |
||||
|
return true |
||||
|
} |
@ -0,0 +1,122 @@ |
|||||
|
#!/usr/bin/env python |
||||
|
from __future__ import print_function |
||||
|
|
||||
|
import argparse |
||||
|
import os |
||||
|
import sys |
||||
|
import tempfile |
||||
|
|
||||
|
from subprocess import check_call, check_output |
||||
|
|
||||
|
|
||||
|
PACKAGE_NAME = os.environ.get( |
||||
|
'CLI_PACKAGE_NAME', 'github.com/urfave/cli' |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def main(sysargs=sys.argv[:]): |
||||
|
targets = { |
||||
|
'vet': _vet, |
||||
|
'test': _test, |
||||
|
'gfmrun': _gfmrun, |
||||
|
'toc': _toc, |
||||
|
'gen': _gen, |
||||
|
} |
||||
|
|
||||
|
parser = argparse.ArgumentParser() |
||||
|
parser.add_argument( |
||||
|
'target', nargs='?', choices=tuple(targets.keys()), default='test' |
||||
|
) |
||||
|
args = parser.parse_args(sysargs[1:]) |
||||
|
|
||||
|
targets[args.target]() |
||||
|
return 0 |
||||
|
|
||||
|
|
||||
|
def _test(): |
||||
|
if check_output('go version'.split()).split()[2] < 'go1.2': |
||||
|
_run('go test -v .') |
||||
|
return |
||||
|
|
||||
|
coverprofiles = [] |
||||
|
for subpackage in ['', 'altsrc']: |
||||
|
coverprofile = 'cli.coverprofile' |
||||
|
if subpackage != '': |
||||
|
coverprofile = '{}.coverprofile'.format(subpackage) |
||||
|
|
||||
|
coverprofiles.append(coverprofile) |
||||
|
|
||||
|
_run('go test -v'.split() + [ |
||||
|
'-coverprofile={}'.format(coverprofile), |
||||
|
('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') |
||||
|
]) |
||||
|
|
||||
|
combined_name = _combine_coverprofiles(coverprofiles) |
||||
|
_run('go tool cover -func={}'.format(combined_name)) |
||||
|
os.remove(combined_name) |
||||
|
|
||||
|
|
||||
|
def _gfmrun(): |
||||
|
go_version = check_output('go version'.split()).split()[2] |
||||
|
if go_version < 'go1.3': |
||||
|
print('runtests: skip on {}'.format(go_version), file=sys.stderr) |
||||
|
return |
||||
|
_run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) |
||||
|
|
||||
|
|
||||
|
def _vet(): |
||||
|
_run('go vet ./...') |
||||
|
|
||||
|
|
||||
|
def _toc(): |
||||
|
_run('node_modules/.bin/markdown-toc -i README.md') |
||||
|
_run('git diff --exit-code') |
||||
|
|
||||
|
|
||||
|
def _gen(): |
||||
|
go_version = check_output('go version'.split()).split()[2] |
||||
|
if go_version < 'go1.5': |
||||
|
print('runtests: skip on {}'.format(go_version), file=sys.stderr) |
||||
|
return |
||||
|
|
||||
|
_run('go generate ./...') |
||||
|
_run('git diff --exit-code') |
||||
|
|
||||
|
|
||||
|
def _run(command): |
||||
|
if hasattr(command, 'split'): |
||||
|
command = command.split() |
||||
|
print('runtests: {}'.format(' '.join(command)), file=sys.stderr) |
||||
|
check_call(command) |
||||
|
|
||||
|
|
||||
|
def _gfmrun_count(): |
||||
|
with open('README.md') as infile: |
||||
|
lines = infile.read().splitlines() |
||||
|
return len(filter(_is_go_runnable, lines)) |
||||
|
|
||||
|
|
||||
|
def _is_go_runnable(line): |
||||
|
return line.startswith('package main') |
||||
|
|
||||
|
|
||||
|
def _combine_coverprofiles(coverprofiles): |
||||
|
combined = tempfile.NamedTemporaryFile( |
||||
|
suffix='.coverprofile', delete=False |
||||
|
) |
||||
|
combined.write('mode: set\n') |
||||
|
|
||||
|
for coverprofile in coverprofiles: |
||||
|
with open(coverprofile, 'r') as infile: |
||||
|
for line in infile.readlines(): |
||||
|
if not line.startswith('mode: '): |
||||
|
combined.write(line) |
||||
|
|
||||
|
combined.flush() |
||||
|
name = combined.name |
||||
|
combined.close() |
||||
|
return name |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
sys.exit(main()) |
@ -0,0 +1,29 @@ |
|||||
|
package cli |
||||
|
|
||||
|
import "unicode" |
||||
|
|
||||
|
// lexicographicLess compares strings alphabetically considering case.
|
||||
|
func lexicographicLess(i, j string) bool { |
||||
|
iRunes := []rune(i) |
||||
|
jRunes := []rune(j) |
||||
|
|
||||
|
lenShared := len(iRunes) |
||||
|
if lenShared > len(jRunes) { |
||||
|
lenShared = len(jRunes) |
||||
|
} |
||||
|
|
||||
|
for index := 0; index < lenShared; index++ { |
||||
|
ir := iRunes[index] |
||||
|
jr := jRunes[index] |
||||
|
|
||||
|
if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr { |
||||
|
return lir < ljr |
||||
|
} |
||||
|
|
||||
|
if ir != jr { |
||||
|
return ir < jr |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return i < j |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"comment": "", |
||||
|
"ignore": "test", |
||||
|
"package": [ |
||||
|
{ |
||||
|
"checksumSHA1": "9/2qvuFPQxW7ZXzQSOYwqZb5cNg=", |
||||
|
"path": "github.com/urfave/cli", |
||||
|
"revision": "8e01ec4cd3e2d84ab2fe90d8210528ffbb06d8ff", |
||||
|
"revisionTime": "2018-02-26T03:02:53Z" |
||||
|
} |
||||
|
], |
||||
|
"rootPath": "github.com/appleboy/drone-packer" |
||||
|
} |
Loading…
Reference in new issue