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