Merge master @1.22.1 with v2
commitc71fbcefd2
Merge:61f3ae3
ef47250
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Thu Sep 12 05:35:50 2019 +0530 Merge pull request #887 from urfave/asahasrabuddhe-patch-1 Release 1.22.1 commitef47250cda
Merge:71eaf37
61f3ae3
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Thu Sep 12 05:19:58 2019 +0530 Merge branch 'master' into asahasrabuddhe-patch-1 commit61f3ae353b
Merge:388c2dd
fa858dc
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Thu Sep 12 05:19:33 2019 +0530 Merge pull request #890 from urfave/issue-878 Fix #878 commitfa858dcc26
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Sep 11 15:10:14 2019 +0530 Ensure flag is not blank commitf8bb66ae7d
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Sep 11 14:42:38 2019 +0530 Fix Typo commit056aef13fe
Merge:c6ee3b4
82a84fc
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Sep 11 14:37:06 2019 +0530 Merge branch 'issue-878' of https://github.com/urfave/cli into issue-878 commitc6ee3b4904
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Sep 11 14:34:41 2019 +0530 Use iterative logic to determine missing flag commit82a84fc187
Merge:1547ac2
388c2dd
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 14:17:11 2019 +0530 Merge branch 'master' into issue-878 commit1547ac2f6a
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Sep 11 14:15:20 2019 +0530 Modify variable names commit388c2dd0f4
Merge:e19126a
6d888d6
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 14:13:40 2019 +0530 Merge pull request #891 from saschagrunert/fish-hidden Don't generate fish completion for hidden commands commit71eaf37e33
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 14:09:50 2019 +0530 Update CHANGELOG.md commit6d888d693d
Merge:bac5bde
e19126a
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 14:04:44 2019 +0530 Merge branch 'master' into fish-hidden commite19126a819
Merge:b207e20
35eb598
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 14:01:44 2019 +0530 Merge pull request #883 from urfave/remove-flag-generation Remove flag generation commitbac5bde38c
Author: Sascha Grunert <sgrunert@suse.com> Date: Wed Sep 11 09:06:02 2019 +0200 Don't generate fish completion for hidden commands Added the missing test case as well. Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit36cdaa9964
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 10:34:00 2019 +0530 Update CHANGELOG.md commitcbb9e015b8
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 09:21:45 2019 +0530 Improve Code and Add Test Case commit7d6a604106
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 08:59:51 2019 +0530 Fix #878 commitbe37c2cbda
Merge:0aee120
b207e20
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 08:27:45 2019 +0530 Merge branch 'master' into asahasrabuddhe-patch-1 commit35eb598d43
Merge:8575558
b207e20
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 05:39:35 2019 +0530 Merge branch 'master' into remove-flag-generation commit0aee120c32
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 05:37:41 2019 +0530 Update CHANGELOG.md commit5c019b10ca
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Sep 11 05:33:46 2019 +0530 Update CHANGELOG.md commitb207e20873
Merge:249cb33
487be14
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Tue Sep 10 21:28:35 2019 +0100 Merge pull request #889 from crosbymichael/hidden-man Don't output hidden commands for man pages commit487be14dce
Author: Michael Crosby <crosbymichael@gmail.com> Date: Tue Sep 10 13:49:11 2019 -0400 Don't output hidden commands for man pages Signed-off-by: Michael Crosby <crosbymichael@gmail.com> commit85755588ac
Merge:024692c
249cb33
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Tue Sep 10 15:04:12 2019 +0530 Merge branch 'master' into remove-flag-generation commit249cb33392
Merge:bfe2e92
abfb13b
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Tue Sep 10 08:28:09 2019 +0100 Merge pull request #885 from urfave/go-modules-support Go modules support commitabfb13b854
Merge:534d60b
bfe2e92
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Tue Sep 10 06:56:41 2019 +0530 Merge branch 'master' into go-modules-support commit054fbefec3
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Tue Sep 10 06:42:34 2019 +0530 Update CHANGELOG.md commit534d60bb9b
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Tue Sep 10 06:40:45 2019 +0530 Bump minimum supported version of Go to 1.11 commit024692c172
Merge:4a9e440
bfe2e92
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Tue Sep 10 06:33:21 2019 +0530 Merge branch 'master' into remove-flag-generation commitbfe2e925cf
Merge:3eca109
238c80f
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Tue Sep 10 06:24:10 2019 +0530 Merge pull request #882 from urfave/lynncyrin-patch-1 Release 1.22.0 commit426e21c150
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Mon Sep 9 13:15:47 2019 +0530 Update .travis.yml Set GOPROXY in Travis environment commit39bd617664
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Mon Sep 9 08:37:16 2019 +0530 Cleanup after before_script to avoid git diff errors remove windows build commitedbf66c25c
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Mon Sep 9 08:23:04 2019 +0530 Update gfmrun import command to suite Go Modules pattern Fix test command typo in travis script commitafd0ecbbf2
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Mon Sep 9 08:19:06 2019 +0530 Add support for Go 1.13 Drop support for Go 1.11 Use md2man v2 to avoid dependency issues when building with Go Modules Enabled Update TravisCI build environment images (trusty was deprecated) Add optional Windows build commit4a9e440503
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Mon Sep 9 08:12:09 2019 +0530 Fix AppVeyor build commit5c81af9f10
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Mon Sep 9 07:47:18 2019 +0530 Remove generate script from travis flow Remove unused dependencies from travis script commitb6c5d17a83
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Mon Sep 9 07:44:07 2019 +0530 Remove Flag Generation Remove Legacy Python Scripts commit238c80f9b5
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Sep 7 20:44:48 2019 -0700 Update CHANGELOG.md commit980742b7cc
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Sep 7 20:43:56 2019 -0700 typo commit890d49ca7a
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Sep 7 20:41:44 2019 -0700 Release 1.22.0 - adds the changelog for 1.22.0 - updates the changelog for 1.21.0. some PRs were missed, as was mentioned here https://github.com/urfave/cli/pull/829#issuecomment-517968795 - closes https://github.com/urfave/cli/issues/867 commit3eca1090a3
Merge:38a6c56
4bbff84
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Fri Aug 30 15:53:55 2019 +0100 Merge pull request #879 from saschagrunert/escape-single-quotes Escape single quotes in fish shell completion commit4bbff84169
Author: Sascha Grunert <sgrunert@suse.com> Date: Thu Aug 29 14:45:32 2019 +0200 Escape single quotes in fish shell completion Single quotes can break the generated fish shell completion and should be escaped correctly. Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit38a6c560b3
Merge:fa6797b
687f721
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Thu Aug 29 20:52:07 2019 +0100 Merge pull request #857 from saschagrunert/takes-file-fish Add `TakesFile` to fish shell completion commit687f721eaa
Author: Sascha Grunert <sgrunert@suse.com> Date: Mon Aug 26 10:07:50 2019 +0200 Update function alignment Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit0c01922a12
Author: Sascha Grunert <sgrunert@suse.com> Date: Mon Aug 26 08:46:55 2019 +0200 Add type switch Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit38d0ac6296
Author: Sascha Grunert <mail@saschagrunert.de> Date: Sun Aug 25 17:50:18 2019 +0200 Removed GetTakesFile and stick to type assertions Signed-off-by: Sascha Grunert <mail@saschagrunert.de> commita1cf7f44b6
Author: Sascha Grunert <sgrunert@suse.com> Date: Mon Aug 12 09:42:12 2019 +0200 Add `TakesFile` to fish shell completion The new `TakesFile` flag will be now consumed by the fish shell completion generator. Signed-off-by: Sascha Grunert <sgrunert@suse.com> commitfa6797beef
Merge:2344c98
82eb0d7
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 24 18:58:52 2019 -0700 Merge pull request #876 from urfave/lynncyrin-patch-1 Bump go version to 1.10 in readme commit82eb0d70cb
Merge:edd8cb2
2344c98
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sun Aug 25 01:04:33 2019 +0100 Merge branch 'master' into lynncyrin-patch-1 commit2344c98f67
Merge:55de011
68ee2bc
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sun Aug 25 01:04:17 2019 +0100 Merge pull request #860 from saschagrunert/takes-file-not-all Update `TakesFile` flag to apply only to supported flags commitedd8cb2068
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 24 14:44:56 2019 -0700 Bump go version to 1.10 in readme Closes https://github.com/urfave/cli/issues/875 commit68ee2bc4af
Merge:959d9ec
55de011
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 24 14:34:15 2019 -0700 Merge branch 'master' into takes-file-not-all commit55de011cf8
Merge:392c1de
d3edef8
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sat Aug 24 11:55:28 2019 +0100 Merge pull request #873 from urfave/show-test-failures build: show failures when running tests commitd3edef887a
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sat Aug 24 11:34:03 2019 +0100 Update build.go commitc2d1a13208
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Aug 24 03:05:45 2019 -0700 Revert "check length" This reverts commit1095838cca
. commit959d9ec36b
Merge:3681b05
392c1de
Author: Sascha Grunert <sgrunert@suse.com> Date: Sat Aug 24 11:23:51 2019 +0200 Merge branch 'master' into takes-file-not-all commit7d62a9d054
Merge:1095838
392c1de
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 24 00:50:42 2019 -0700 Merge branch 'master' into show-test-failures commit1095838cca
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Aug 24 00:49:29 2019 -0700 check length commit29ad6ee6ad
Author: [[ BOT ]] Lynn Cyrin <lynncyrin@gmail.com> Date: Fri Aug 23 20:09:08 2019 -0700 DRY commit392c1de1a2
Merge:23c8303
487c723
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Fri Aug 23 22:51:39 2019 +0100 Merge pull request #874 from saschagrunert/go-mod-cleanup Cleanup go modules commit487c723673
Author: Sascha Grunert <sgrunert@suse.com> Date: Fri Aug 23 10:28:32 2019 +0200 Cleanup go modules These two dependencies are not really needed, which can be reproduced via: ``` > export GO111MODULE=on && go mod tidy ``` Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit8469a9de07
Author: [[ BOT ]] Lynn Cyrin <lynncyrin@gmail.com> Date: Thu Aug 22 21:42:07 2019 -0700 show test failures commit23c8303026
Merge:ecd576e
6a25af9
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 17 11:24:05 2019 -0700 Merge pull request #862 from russoj88/UpdateREADME_gopkg.in_v1 Rewrite the "pinning to v1" section. commit6a25af9641
Merge:3bc62c4
ecd576e
Author: russoj88 <russoj88@users.noreply.github.com> Date: Sat Aug 17 10:01:35 2019 -0700 Merge branch 'master' into UpdateREADME_gopkg.in_v1 commitecd576e779
Merge:6cc7e98
e11183f
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sat Aug 17 16:51:43 2019 +0100 Merge pull request #868 from urfave/lynncyrin-patch-1 Modernize readme commite11183fe50
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 17 02:44:49 2019 -0700 Modernize readme I assume that people no longer care about what the package was named many years ago commit3bc62c4fde
Author: russoj88 <russoj88@gmail.com> Date: Thu Aug 15 12:30:29 2019 -0700 Mimic v2 example code from above. commit62b8a7cc2c
Author: russoj88 <russoj88@gmail.com> Date: Wed Aug 14 11:20:09 2019 -0700 Add "Using v1 releases" to table of contents. commitcc091db561
Author: russoj88 <russoj88@users.noreply.github.com> Date: Wed Aug 14 11:21:40 2019 -0700 Update README.md Only instruct on right way to use library. Co-Authored-By: Lynn Cyrin (they/them) <lynncyrin@gmail.com> commitf529dad70c
Author: russoj88 <russoj88@users.noreply.github.com> Date: Wed Aug 14 11:20:58 2019 -0700 Update README.md Include suggestion to put example in. Co-Authored-By: Lynn Cyrin (they/them) <lynncyrin@gmail.com> commitf2c26bab77
Author: russoj88 <russoj88@gmail.com> Date: Tue Aug 13 21:10:38 2019 -0700 Rewrite the "pinning to v1" section. commit3681b057c5
Author: Sascha Grunert <sgrunert@suse.com> Date: Tue Aug 13 09:43:57 2019 +0200 Update `TakesFile` flag to apply only to supported flags Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit6cc7e987c4
Merge:7e49cc2
08c24e2
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Mon Aug 12 21:30:37 2019 +0100 Merge pull request #856 from FaranIdo/master Add Subcommand fallback call to ExitErrHandler, fixing #816 commit08c24e22ed
Author: FaranIdo <idoosbron@gmail.com> Date: Mon Aug 12 00:29:46 2019 +0300 add missing ExitErrHandler in command + matching test, fixing #816 commit7e49cc210a
Merge:8b18c71
4e42a2f
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 10 09:01:16 2019 +0000 Merge pull request #848 from saschagrunert/fish-shell Add fish shell completion support commit4e42a2f02c
Merge:56d12d0
8b18c71
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 10 08:47:31 2019 +0000 Merge branch 'master' into fish-shell commit8b18c71e1a
Merge:7058c58
c6c2008
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 10 08:47:23 2019 +0000 Merge pull request #851 from saschagrunert/takes-file Add `TakesFile` indicator to flag commit56d12d0c2f
Merge:7506b11
7058c58
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Fri Aug 9 17:21:24 2019 +0530 Merge branch 'master' into fish-shell commitc6c200864d
Merge:e9e9e0a
7058c58
Author: Sascha Grunert <sgrunert@suse.com> Date: Fri Aug 9 13:48:36 2019 +0200 Merge branch 'master' into takes-file commit7058c58eb6
Merge:2e0e39a
de0fa70
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Fri Aug 9 17:16:13 2019 +0530 Merge pull request #847 from saschagrunert/remove-date-var Remove unused `Date` variable from `cliTemplate` commitde0fa70433
Merge:0d79d1d
2e0e39a
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Fri Aug 9 12:38:50 2019 +0100 Merge branch 'master' into remove-date-var commite9e9e0ac5d
Author: Sascha Grunert <sgrunert@suse.com> Date: Fri Aug 9 09:05:55 2019 +0200 Add `TakesFile` indicator to flag This new member of `Flag` indicates if the flag expects a file as input. This is especially useful for documentation and shell completion purposes. Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit7506b11da7
Author: Sascha Grunert <sgrunert@suse.com> Date: Thu Aug 8 15:50:36 2019 +0200 Add fish shell completion support This commit adds a new method `ToFishCompletion` to the `*App` which can be used to generate a fish completion string for the application. Relates to: #351 Signed-off-by: Sascha Grunert <sgrunert@suse.com> commit2e0e39a03b
Merge:946f918
aed704a
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Fri Aug 9 10:34:28 2019 +0530 Merge pull request #845 from urfave/lint-fixes linter fixes commit0d79d1d9d9
Author: Sascha Grunert <sgrunert@suse.com> Date: Thu Aug 8 14:04:21 2019 +0200 Remove unused `Date` variable from `cliTemplate` Signed-off-by: Sascha Grunert <sgrunert@suse.com> commitaed704a9d0
Merge:0990ca2
946f918
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Thu Aug 8 14:44:02 2019 +0530 Merge branch 'master' into lint-fixes commit946f918365
Merge:2c477e7
286133f
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Thu Aug 8 09:13:09 2019 +0100 Merge pull request #735 from rliebz/combined Add app-wide support for combining short flags commit0990ca2391
Merge:fdba7e0
2c477e7
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Thu Aug 8 13:36:30 2019 +0530 Merge branch 'master' into lint-fixes commit286133fee5
Merge:815c29f
2c477e7
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Thu Aug 8 13:33:32 2019 +0530 Merge branch 'master' into combined commit2c477e720e
Merge:e0057bb
99fad61
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Thu Aug 8 08:41:19 2019 +0100 Merge pull request #830 from saschagrunert/docs-gen Add markdown and man page docs generation methods commit99fad61ded
Merge:40d4a25
e0057bb
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Thu Aug 8 07:06:08 2019 +0100 Merge branch 'master' into docs-gen commite0057bb597
Merge:521735b
fd39578
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Thu Aug 8 07:04:25 2019 +0100 Merge pull request #846 from urfave/asahasrabuddhe-patch-1 Update README.md commit815c29ffc7
Merge:a77c440
521735b
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Thu Aug 8 07:03:49 2019 +0100 Merge branch 'master' into combined commitfd395786a2
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Aug 7 21:00:07 2019 +0530 Update README.md remove quotes around coverage badge commitfdba7e0f8c
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Aug 7 20:14:50 2019 +0530 linter fixes code cleanup changing some test code to ensure uniformity commit40d4a25a01
Author: Sascha Grunert <mail@saschagrunert.de> Date: Sat Aug 3 12:41:50 2019 +0200 Add markdown and man page docs generation methods This adds two new methods to the `App` struct: - `ToMarkdown`: creates a markdown documentation string - `ToMan`: creates a man page string Signed-off-by: Sascha Grunert <mail@saschagrunert.de> commit521735b760
Merge:97179ca
22e1fc8
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Wed Aug 7 12:10:34 2019 +0100 Merge pull request #844 from urfave/asahasrabuddhe-patch-1 Update README.md commit22e1fc8419
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Aug 7 14:02:52 2019 +0530 Update README.md add codecov.io badge commit97179ca390
Merge:b6f7dd9
3a41d6d
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Aug 7 13:58:51 2019 +0530 Merge pull request #843 from lafriks/patch-1 Support GoLang 1.10 to 1.12 commit3a41d6d785
Author: Lauris BH <lauris@nix.lv> Date: Wed Aug 7 11:21:31 2019 +0300 Lower support to GoLang compiler version 1.10 commit1f4473209d
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Aug 7 13:50:04 2019 +0530 Update .travis.yml support go versions in line with go's release policy commite3fa7e8566
Author: Lauris BH <lauris@nix.lv> Date: Wed Aug 7 11:06:15 2019 +0300 Support also GoLang 1.11 compiler commitb6f7dd9359
Merge:93392d1
e2de8c7
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Aug 7 13:28:34 2019 +0530 Merge pull request #836 from urfave/flag-type-generation-golang Flag Generation in the CLI commite2de8c7458
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Aug 7 12:38:17 2019 +0530 update readme with correct error message, add 1.12 and 1.11 to travis commita77c440b84
Merge:8d31c5e
93392d1
Author: Robert Liebowitz <rliebz@gmail.com> Date: Tue Aug 6 22:33:49 2019 -0400 Merge branch 'master' into combined commit24de27b05e
Merge:c19938f
93392d1
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Wed Aug 7 01:54:44 2019 +0530 Merge branch 'master' into flag-type-generation-golang commitc19938fbbf
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Aug 7 01:51:02 2019 +0530 update ci commands commit6ee5b89e03
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Aug 7 01:50:50 2019 +0530 move build.go to root commite8bbb4c3b5
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Wed Aug 7 01:46:22 2019 +0530 remove unnecessary sprintf commit5070d00811
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Tue Aug 6 12:33:33 2019 +0530 move packages slice to global scope commit93392d12e8
Merge:26945c5
1db0496
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Tue Aug 6 07:59:53 2019 +0100 Merge pull request #808 from yogeshlonkar/master Add support for flags bash completion commitadfe6a09c1
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Tue Aug 6 12:14:18 2019 +0530 indenting fix commit1db049685a
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Mon Aug 5 20:22:52 2019 +0200 Fix unused regex commit2be2bc755e
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Mon Aug 5 20:18:08 2019 +0200 Add additional test for log flag completion and comments commitc3f51bed6f
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Mon Aug 5 17:07:46 2019 +0200 Fix SC2199: Arrays implicitly concatenate in commitc5612e8cd2
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Mon Aug 5 16:58:04 2019 +0200 Fix review comments commit8d31c5e167
Author: Robert Liebowitz <rliebz@gmail.com> Date: Mon Aug 5 07:05:07 2019 -0400 Update README.md Co-Authored-By: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> commit03153b9cf8
Author: Robert Liebowitz <rliebz@gmail.com> Date: Mon Aug 5 06:16:30 2019 -0400 Allow combining short flags globally commitd6523cf869
Merge:e949dc2
26945c5
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Mon Aug 5 11:00:26 2019 +0200 Merge branch 'master' into master commit26945c58ed
Merge:d09efb5
c25e4ca
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sun Aug 4 12:36:23 2019 -0700 Merge pull request #823 from xordspar0/master Make the exit code example more clear commitc25e4cab32
Merge:b1a7c50
d09efb5
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sun Aug 4 12:30:28 2019 -0700 Merge branch 'master' into master commitac5c97b418
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 20:45:28 2019 +0530 add latest assets file commit489d92d2e2
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 20:44:15 2019 +0530 add missing os package commita7f0d86509
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 20:33:21 2019 +0530 add zero mod fs back and commit file with latest ts commit798e1f3d3a
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 20:29:53 2019 +0530 fix spacing issue commit7a6f3d4394
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 20:23:32 2019 +0530 fix tests commit58ae5eb590
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 20:06:44 2019 +0530 move entire build system to go commit8547458f1d
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 19:01:47 2019 +0530 remove zero mod fs commitd09efb5fbd
Merge:7745000
1327f58
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sun Aug 4 18:00:11 2019 +0530 Merge pull request #837 from urfave/codeowners Use codeowners instead of maintainers commit1327f58314
Merge:9938dec
7745000
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sun Aug 4 17:52:42 2019 +0530 Merge branch 'master' into codeowners commite949dc2cc0
Merge:11c9e59
7745000
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Sun Aug 4 10:38:56 2019 +0200 Merge branch 'master' into master commit4b0a410463
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 12:26:48 2019 +0530 fix travis build commitfb4cea5f30
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 12:22:45 2019 +0530 add new generation logic to travis commit365557021f
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 12:17:27 2019 +0530 remove legacy version check code commitb6bfbe97f8
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 12:16:07 2019 +0530 update appveyor to go 1.11, add support for code coverage generation in tests commit826954c979
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 11:09:34 2019 +0530 update app name and remove version commit04948f2152
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 11:06:19 2019 +0530 generate test with go generate commit86e10211de
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 11:05:50 2019 +0530 remove redundant go generate from altsrc update go generate in cli package to generate both files regeneration test commitc676ed4caa
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 11:04:23 2019 +0530 indentation fixes in template regeneration test commitc4fc88e46d
Merge:2a08494
9260850
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 10:29:34 2019 +0530 Merge branch 'flag-type-generation-golang' of https://github.com/urfave/cli into flag-type-generation-golang commit2a084945a4
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 10:28:08 2019 +0530 move around code change package to flag-gen to avoid conflict with flag-generator binary test code generation commit065fe9e9af
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 10:05:44 2019 +0530 change structure to embed source json and template files restructure code to have defaults in place of choices commitd1ded77768
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sun Aug 4 08:45:29 2019 +0530 rename utility from fg > flag-generator commit7745000a0e
Merge:e6cf83e
81acbeb
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sun Aug 4 08:15:00 2019 +0530 Merge pull request #774 from whereswaldon/patch-1 Clarify that altsrc supports both TOML and JSON commit81acbeb629
Merge:8abc5a2
e6cf83e
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sun Aug 4 08:12:24 2019 +0530 Merge branch 'master' into patch-1 commite6cf83ec39
Merge:244eba7
eee6ce8
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 3 19:37:52 2019 -0700 Merge pull request #829 from urfave/lynncyrin-patch-2 Release 1.21.0 commit8abc5a2e49
Merge:b2421d1
244eba7
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sun Aug 4 08:04:13 2019 +0530 Merge branch 'master' into patch-1 commit9938dec695
Author: [[ BOT ]] Lynn Cyrin <lynncyrin@gmail.com> Date: Sat Aug 3 10:26:07 2019 -0700 update contributing docs commit97dbddb32d
Author: [[ BOT ]] Lynn Cyrin <lynncyrin@gmail.com> Date: Sat Aug 3 10:23:29 2019 -0700 use codeowners instead of maintainers commit92608509a4
Merge:d209be3
244eba7
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 3 22:49:24 2019 +0530 Merge branch 'master' into flag-type-generation-golang commitd209be3245
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sat Aug 3 22:32:36 2019 +0530 update go generate command test file generation commitadd69c7d4f
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sat Aug 3 22:16:25 2019 +0530 updated flag types generated courtesy fg cli commitc133a5aeb1
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sat Aug 3 22:15:43 2019 +0530 add explicit true/false choices for value and dest keys due to go default false for bool types commit16c7a60528
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sat Aug 3 22:14:58 2019 +0530 finish generation of flag types for altsrc package rename package to fg (flag generator) commit32ddef5ca7
Author: Ajitem Sahasrabuddhe <ajitem.sahasrabuddhe@perennialsys.com> Date: Sat Aug 3 21:48:48 2019 +0530 finish generation of flag types for cli package commit9766be8d3e
Author: mingrammer <mingrammer@gmail.com> Date: Thu Mar 7 00:04:18 2019 +0900 get latest changes from master commite01e3c540c
Author: mingrammer <mingrammer@gmail.com> Date: Wed Mar 6 23:51:22 2019 +0900 Fix the unaligned indents for the commands that have no categories commitb1a7c502eb
Merge:c75a689
244eba7
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 3 09:36:27 2019 -0700 Merge branch 'master' into master commiteee6ce83c0
Merge:8a7f65e
244eba7
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 3 09:06:47 2019 -0700 Merge branch 'master' into lynncyrin-patch-2 commit11c9e598b0
Merge:01ab016
244eba7
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Sat Aug 3 15:52:08 2019 +0200 Merge branch 'master' into master commit244eba7e4c
Merge:1169906
4627bbe
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 3 18:04:31 2019 +0530 Merge pull request #831 from saschagrunert/go-modules Add go module support commitb2421d1235
Merge:3e14507
1169906
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sat Aug 3 13:25:34 2019 +0100 Merge branch 'master' into patch-1 commit4627bbe109
Author: Sascha Grunert <mail@saschagrunert.de> Date: Sat Aug 3 12:55:06 2019 +0200 Add go module support This adds a go.{mod,sum} file to official support go modules. Signed-off-by: Sascha Grunert <mail@saschagrunert.de> commit1169906f57
Merge:07c1638
94f4f83
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sat Aug 3 11:02:34 2019 +0100 Merge pull request #773 from teresy/redundant-nil-check-slice Remove redundant nil checks commit94f4f83672
Merge:da581b2
07c1638
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Sat Aug 3 02:05:33 2019 -0700 Merge branch 'master' into redundant-nil-check-slice commit07c1638969
Merge:842e3fe
7a51175
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 3 08:47:32 2019 +0530 Merge pull request #806 from mingrammer/fix-help-indentation Fix the unaligned indents for the command help messages commit7a51175ce1
Merge:330a914
842e3fe
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 3 08:37:34 2019 +0530 Merge branch 'master' into fix-help-indentation commit842e3fe1b6
Merge:7675649
fa51d00
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Sat Aug 3 08:12:05 2019 +0530 Merge pull request #828 from urfave/lynncyrin-patch-1 Update maintainers for current reality commit8a7f65e052
Author: Lynn Cyrin (they/them) <lynn@textio.com> Date: Fri Aug 2 19:30:41 2019 -0700 Update CHANGELOG.md commite8eac43d9d
Author: Lynn Cyrin <lynn@textio.com> Date: Fri Aug 2 18:26:41 2019 -0700 Update CHANGELOG.md commit330a9143fb
Merge:ddc3453
7675649
Author: Lynn Cyrin <lynn@textio.com> Date: Fri Aug 2 18:14:50 2019 -0700 Merge branch 'master' into fix-help-indentation commitfa51d00dc6
Author: Lynn Cyrin <lynn@textio.com> Date: Fri Aug 2 18:06:15 2019 -0700 Update maintainers for current reality commit01ab016427
Merge:d79d2a0
7675649
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Fri Aug 2 22:03:55 2019 +0200 Merge branch 'master' into master commitc75a689f62
Author: Jordan Christiansen <Jordan.Christiansen@target.com> Date: Fri Aug 2 14:28:57 2019 -0500 Make exit code example more clear The purpose of this example is to show that you can exit with an error code if a flag is unspecified, but with the code as it is, the only way to cause a non-zero exit is by adding the flag `--ginger-crouton=false`, which is not explained in the example. In this new version of the example, running the command with no flag will exit with an error, and running it with the flag will exit normally. commit7675649a17
Merge:656063a
f8ba505
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Fri Aug 2 22:52:32 2019 +0530 Merge pull request #819 from lynncyrin/required-flags-take-2 Required flags commitf8ba505a7c
Merge:60fb297
656063a
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Fri Aug 2 22:49:29 2019 +0530 Merge branch 'master' into required-flags-take-2 commit656063a846
Merge:693af58
6505336
Author: Ajitem Sahasrabuddhe <ajitem.s@outlook.com> Date: Fri Aug 2 22:49:09 2019 +0530 Merge pull request #788 from benzvan/master adds test coverage to context commit60fb297232
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 23:27:34 2019 -0700 remove help assertion stuff commitd7ec4e8013
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 23:26:43 2019 -0700 add env var tests commitf4128a02f3
Author: Lynn Cyrin <lynncyrin@gmail.com> Date: Thu Aug 1 22:54:15 2019 -0700 Update command.go commit38f9e1622d
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 22:52:21 2019 -0700 add environment variable support 🎉 commitf21b22dd90
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 22:10:18 2019 -0700 cleanup some issues with error display commitfdd4d10691
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 21:48:52 2019 -0700 update comments commitef9acb4a3b
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 21:46:56 2019 -0700 rename cases commit45f2b3d8e7
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 21:45:11 2019 -0700 more test cases commit78db152323
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 21:35:15 2019 -0700 add typed error assertions commitd4740d10d0
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 20:58:08 2019 -0700 more test cases commit595382c509
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 20:39:37 2019 -0700 expand test cases commit3d6eec825a
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 20:35:23 2019 -0700 add test cases commit7b9e16b6b5
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 20:30:43 2019 -0700 update test names commit95d3a8624d
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 20:27:51 2019 -0700 update test to reflect app flag usage commit714a73f028
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 19:57:14 2019 -0700 remove unused thing commit9438aba3b8
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Aug 1 19:54:57 2019 -0700 remove showFlagError, we can use the help printer assertion to accomplish the same goal commit386b379d19
Author: Lynn Cyrin <lynn@textio.com> Date: Sun Jul 28 22:45:43 2019 -0700 Revert "reset generated flags changes" This reverts commit9ec594d529
. commit9ec594d529
Author: Lynn Cyrin <lynn@textio.com> Date: Sun Jul 28 22:34:07 2019 -0700 reset generated flags changes commit23f09ac1e8
Author: Lynn Cyrin <lynn@textio.com> Date: Sun Jul 28 22:19:35 2019 -0700 cleanup tests, check required flags in more places commitd79d2a0424
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Wed Jul 24 16:08:47 2019 +0200 Fix issue with source command completion Avoid competion for bash builtin `source` and fallback to default implementation as it throws below error ``` -bash: source: --: invalid option source: usage: source filename [arguments] ``` commit7ce0af189e
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 18 00:52:24 2019 -0700 remove unused code commitd8985dc6d5
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 18 00:51:16 2019 -0700 reduce diff commit19140e1fb5
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 18 00:48:09 2019 -0700 show errors commit2299852c3c
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 18 00:47:18 2019 -0700 cleanup subcommand and specs commit300288670f
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 18 00:20:32 2019 -0700 add subcommand commitcc1cf8c459
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 18 00:09:07 2019 -0700 wording shift commit32d84d8e87
Author: Lynn Cyrin <lynncyrin@gmail.com> Date: Wed Jul 17 00:25:13 2019 -0700 copy update commit01d5cfab70
Author: Lynn Cyrin <lynn@textio.com> Date: Wed Jul 17 00:20:44 2019 -0700 use strings.Join commitcdc7af744e
Author: Lynn Cyrin <lynn@textio.com> Date: Wed Jul 17 00:16:40 2019 -0700 add handling for multiple required flags commit9293f5b3cc
Author: Lynn Cyrin <lynn@textio.com> Date: Sun Jul 14 21:00:16 2019 -0700 visually shorten logic commitf00f35ce8c
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Jul 13 14:02:45 2019 -0700 docs commit17108e1db4
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Jul 13 13:59:29 2019 -0700 tabs commitcf824804c2
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Jul 13 13:57:06 2019 -0700 update tests commit80d7e91191
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Jul 13 03:51:26 2019 -0700 fill out test cases commit746866c10d
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Jul 13 03:44:39 2019 -0700 add update integration with the help output commit550ed20ea4
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Jul 13 01:26:47 2019 -0700 update tests commitf6777bf4bf
Author: Lynn Cyrin <lynn@textio.com> Date: Sat Jul 13 01:03:46 2019 -0700 quote the flag name commit6a2ae78373
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 21:53:10 2019 -0700 backwards compatible RequiredFlag implementation commit922d231891
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 21:28:09 2019 -0700 ./generate-flag-types cli -i flag-types.json -o flag_generated.go commit8a58b7e039
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:47:47 2019 -0700 remove manual isRequired funcs commit62e99ad1c1
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:46:22 2019 -0700 add IsRequired to generator commit310bfeb194
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:44:41 2019 -0700 add required attr to generator commitaf627c73c3
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:34:17 2019 -0700 update func name commit3d2d6975b4
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:32:42 2019 -0700 reduce diff commit0608059cc7
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:32:15 2019 -0700 reduce diff commit9c299e7e8a
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:28:29 2019 -0700 reduce diff commit30a71dc427
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:25:52 2019 -0700 update Run command commitf7d5e2c21e
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:22:16 2019 -0700 reduce diff commite6842c0b75
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:21:05 2019 -0700 merge in test file commitfa8187f2ce
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:19:42 2019 -0700 reduce diff commitce1630141e
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:18:52 2019 -0700 reduce diff??? commit138dbaafec
Merge:aba73ce
693af58
Author: Lynn Cyrin <lynn@textio.com> Date: Thu Jul 11 20:07:55 2019 -0700 Merge branch 'master' into required_flags commitda581b24e8
Merge:6aa7f35
693af58
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Fri Jun 28 07:55:04 2019 +0100 Merge branch 'master' into redundant-nil-check-slice commit65053360c7
Author: Ben Zvan <ben.zvan@target.com> Date: Wed Jun 26 09:41:11 2019 -0500 Revert "Created using Colaboratory" This reverts commit83b99c4109
. This commit was randomly created here when I connected to colaboratory commit83b99c4109
Author: Ben Zvan <ben@zvan.net> Date: Tue Jun 25 18:47:58 2019 -0500 Created using Colaboratory commit23042d3707
Merge:4a76377
693af58
Author: Ben Zvan <ben@zvan.net> Date: Fri Jun 14 10:14:07 2019 -0500 Merge branch 'master' into master commit62f02f21ef
Author: Yogesh Lonkar <ylonkar@whitehedge.com> Date: Thu Apr 11 10:57:58 2019 +0530 Don't complete hidden flags commit1d7a2b08d6
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Thu Mar 21 13:01:48 2019 +0530 Add default completion on commands, test cases, refactor code commitfb1421d903
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Wed Mar 20 21:34:56 2019 +0530 Fix duplicate completion of existing flag commit58a072d573
Author: Yogesh Lonkar <lonkar.yogeshr@gmail.com> Date: Wed Mar 20 20:28:51 2019 +0530 Add bash completion support for flags commitddc3453179
Author: mingrammer <mingrammer@gmail.com> Date: Thu Mar 7 00:04:18 2019 +0900 Update README.md commita0453b2200
Author: mingrammer <mingrammer@gmail.com> Date: Wed Mar 6 23:51:22 2019 +0900 Fix the unaligned indents for the commands that have no categories commit693af58b4d
Merge:e229212
d7c3be8
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sun Feb 3 18:40:40 2019 +0000 Merge pull request #766 from agis/patch-1 Fix README typo commit6aa7f352fa
Merge:21dfc6e
e229212
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sun Feb 3 18:39:49 2019 +0000 Merge branch 'master' into redundant-nil-check-slice commite229212769
Merge:b67dcf9
5b83c89
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Sun Feb 3 18:37:18 2019 +0000 Merge pull request #798 from Quasilyte/patch-1 use type switch instead of if/else commit5b83c895a7
Author: Iskander (Alex) Sharipov <quasilyte@gmail.com> Date: Tue Jan 29 22:51:02 2019 +0300 use type switch instead of if/else This reduces the syntax noise of the code by removing excessive type assertions. Signed-off-by: Iskander Sharipov <quasilyte@gmail.com> commit4a76377775
Author: Ben Zvan <benjamin.zvan@target.com> Date: Wed Dec 26 12:48:12 2018 -0600 go fmt commitd63733fe14
Author: Ben Zvan <benjamin.zvan@target.com> Date: Wed Dec 26 12:41:27 2018 -0600 adds test coverage to context commitb67dcf995b
Merge:cbebba9
11ab68f
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Mon Oct 29 21:32:00 2018 +0000 Merge pull request #776 from gliptak/patch-2 Bring Go version current commit11ab68f24d
Merge:769f6d5
cbebba9
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Mon Oct 29 21:19:45 2018 +0000 Merge branch 'master' into patch-2 commitcbebba941b
Merge:934abfb
9587fc2
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Mon Oct 29 21:18:40 2018 +0000 Merge pull request #775 from gliptak/patch-1 Correct typo commit769f6d543b
Author: Gábor Lipták <gliptak@gmail.com> Date: Thu Oct 18 21:00:02 2018 -0400 Bring Go version current commit9587fc27bd
Author: Gábor Lipták <gliptak@gmail.com> Date: Thu Oct 18 20:56:13 2018 -0400 Correct typo commit3e145076ab
Author: Christopher Waldon <christopher.waldon.dev@gmail.com> Date: Fri Oct 12 11:30:46 2018 -0400 Clarify that altsrc supports both TOML and JSON commit21dfc6eb83
Author: teresy <hi.teresy@gmail.com> Date: Wed Oct 10 14:54:48 2018 -0400 Remove redundant nil checks commitd7c3be8267
Author: Agis Anastasopoulos <827224+agis@users.noreply.github.com> Date: Tue Aug 21 11:19:37 2018 +0300 Fix README typo commit934abfb2f1
Merge:8e01ec4
3e5a935
Author: Audrius Butkevicius <audrius.butkevicius@gmail.com> Date: Tue Aug 21 07:40:27 2018 +0100 Merge pull request #758 from vrothberg/fix-short-opts-parsing short opt handling: fix parsing commit3e5a935ed3
Author: Valentin Rothberg <vrothberg@suse.com> Date: Tue Aug 21 08:33:42 2018 +0200 fix `go vet` warning command_test.go:342:3 value declared but not used Signed-off-by: Valentin Rothberg <vrothberg@suse.com> commitc23dfba701
Author: Valentin Rothberg <vrothberg@suse.com> Date: Thu Jun 28 16:41:02 2018 +0200 short opt handling: fix parsing Only split a given string (e.g., "-abc") into short options (e.g., "-a", "-b", "-c") if all those are flags. To further avoid mistakenly transform common arguments, catch "flag provided but not defined" errors to iteratively transform short options. Signed-off-by: Valentin Rothberg <vrothberg@suse.com> Fixes: https://github.com/projectatomic/libpod/issues/714 commit8e01ec4cd3
Merge:d4bf9ce
8dc47eb
Author: Dan Buch <dan@meatballhat.com> Date: Sun Feb 25 22:02:53 2018 -0500 Merge pull request #598 from urfave/backport-json-support Backport JSON InputSource to v1 commit8dc47eb3cb
Merge:f551359
d4bf9ce
Author: Dan Buch <dan@meatballhat.com> Date: Sun Feb 25 16:09:48 2018 -0500 Merge branch 'master' into backport-json-support commitd4bf9ce860
Merge:b09aafd
e59e474
Author: Dan Buch <dan@meatballhat.com> Date: Sun Feb 25 16:09:04 2018 -0500 Merge pull request #498 from urfave/merging-jereksel-zsh Merging #489 (plus hack) commite59e4743b8
Merge:5fc8124
b09aafd
Author: Dan Buch <dan@meatballhat.com> Date: Sat Feb 24 22:02:40 2018 -0500 Merge branch 'master' into merging-jereksel-zsh commitb09aafdfe9
Merge:446f49e
bc77a15
Author: Dan Buch <dan@meatballhat.com> Date: Sat Feb 24 22:02:19 2018 -0500 Merge pull request #681 from urfave/check-run-error-in-readme Update README examples to check for errors commit5fc8124af1
Merge:688c5a9
446f49e
Author: Dan Buch <dan@meatballhat.com> Date: Sat Feb 24 21:58:26 2018 -0500 Merge branch 'master' into merging-jereksel-zsh commitbc77a15c69
Merge:59e1ddb
446f49e
Author: Dan Buch <dan@meatballhat.com> Date: Sat Feb 24 21:54:06 2018 -0500 Merge branch 'master' into check-run-error-in-readme commit446f49e78f
Merge:a1c7408
45289ea
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Tue Feb 20 21:16:31 2018 -0800 Merge pull request #715 from urfave/maintainers-notice Adjust contribution and maintainer prose per current reality commit45289ea7a0
Author: Dan Buch <dan@meatballhat.com> Date: Tue Feb 20 12:40:43 2018 -0500 Adjust contribution and maintainer prose per current reality commit59e1ddb43e
Merge:9838c8b
a1c7408
Author: Dan Buch <dan@meatballhat.com> Date: Tue Feb 13 15:27:04 2018 -0500 Merge branch 'master' into check-run-error-in-readme commita1c7408de3
Merge:803d066
3a87b13
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sat Feb 10 18:18:39 2018 -0800 Merge pull request #712 from windler/fix_args_reorder Fix args reordering when bool flags are present commit3a87b13b01
Author: Nico Windler <nico.windler@gmail.com> Date: Sat Feb 10 13:35:23 2018 +0100 Fix args reordering when bool flags are present commit803d066579
Merge:75104e9
d7555e1
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Fri Feb 2 13:13:18 2018 -0800 Merge pull request #704 from dolmen/replace-unneeded-Sprintf Fix unnecessary uses of Sprintf commitd7555e1729
Author: Olivier Mengué <dolmen@cpan.org> Date: Fri Jan 26 21:14:34 2018 +0100 Fix unnecessary uses of Sprintf - use strconv directly - use concatenation for "%s%s" commit75104e932a
Merge:39908eb
e38e4ae
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sat Jan 6 11:10:48 2018 -0800 Merge pull request #697 from urfave/fix-skip-flag-parsing Fix regression of SkipFlagParsing behavior commite38e4ae2d0
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Fri Dec 29 13:38:18 2017 -0500 Fix regression of SkipFlagParsing behavior Introduced bydf562bf1a8
Was mistakenly prepending the command name. commit39908eb08f
Merge:119bb65
2610681
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Tue Dec 12 08:34:29 2017 -0800 Merge pull request #691 from urfave/refactor-686 Refactor flag handling logic commit2610681040
Merge:0671b16
119bb65
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Mon Dec 11 18:51:46 2017 -0800 Merge branch 'master' into refactor-686 commit0671b166dc
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Mon Dec 4 09:23:40 2017 -0800 Add tests for flag reordering commit119bb65648
Merge:c9eba3f
c6eb2a0
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sun Dec 3 13:42:37 2017 -0800 Merge pull request #690 from gliptak/patch-1 Correct go vet for Go tip commitdf562bf1a8
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sun Dec 3 13:38:50 2017 -0800 Refactor flag handling logic Refactor logic introduced by #686 commitc9eba3f37a
Merge:c6af884
ceaac7c
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sun Dec 3 12:48:28 2017 -0800 Merge pull request #686 from baude/shortoptionSkipArg Handle ShortOptions and SkipArgReorder commitc6eb2a0510
Author: Gábor Lipták <gliptak@gmail.com> Date: Thu Nov 30 19:43:12 2017 -0500 Correct go vet for Go tip https://travis-ci.org/cloudflare/logshare/jobs/309796141#L646 commitceaac7c915
Author: baude <bbaude@redhat.com> Date: Mon Nov 20 09:32:03 2017 -0600 Handle ShortOptions and SkipArgReorder There was a bug in parsing when both ShortOptions and SkipArgReorder were being used together. Signed-off-by: baude <bbaude@redhat.com> commitc6af8847eb
Merge:7ace96b
37b7abb
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Mon Nov 27 19:55:04 2017 -0800 Merge pull request #687 from joshuarubin/master Don't clobber slices with EnvVar commit37b7abb1c4
Author: Joshua Rubin <jrubin@zvelo.com> Date: Tue Nov 21 15:21:31 2017 -0700 dont clobber slices with envvar Signed-off-by: Joshua Rubin <jrubin@zvelo.com> commit7ace96b43d
Merge:44cb242
fd5382e
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Wed Nov 15 20:56:12 2017 -0800 Merge pull request #684 from baude/shortOptionHandling Combine bool short names commitfd5382e7a5
Author: baude <bbaude@redhat.com> Date: Mon Nov 13 15:28:23 2017 -0600 Combine bool short names Adds the ability to allow the combination of bool short-name options. For example, cmd foobar -ov This is done through a bool "UseShortOptionHandler" set in the command struct. Built upon PR #621 Signed-off-by: baude <bbaude@redhat.com> commit9838c8bcaa
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sat Nov 11 16:23:24 2017 -0800 Update README examples to check for errors To encourage good practices. commit43c8c02cf5
Author: zhuchensong <zhuchensong93@163.com> Date: Mon Apr 17 00:47:04 2017 +0800 Support POSIX-style short flag combining commit44cb242eeb
Merge:7f4b273
f971fca
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Fri Nov 3 19:35:40 2017 -0700 Merge pull request #675 from jmccann/continue3 Ability to load variable from file - UPDATED commitf971fca2b2
Author: Jacob McCann <jacob.mccann2@target.com> Date: Thu Oct 26 13:08:03 2017 -0500 Allow FilePath to take []string commit18a556e192
Author: Brad Rydzewski <brad.rydzewski@gmail.com> Date: Mon Apr 10 16:45:51 2017 +0200 fix FilePath documentation in README.md commit4cc453ba67
Author: Brad Rydzewski <brad.rydzewski@gmail.com> Date: Sat Apr 1 12:55:46 2017 +0900 document field in README commitc698b821b8
Author: Brad Rydzewski <brad.rydzewski@gmail.com> Date: Sat Apr 1 12:37:06 2017 +0900 unit tests for load from file commit21fcab0dee
Author: Brad Rydzewski <brad.rydzewski@gmail.com> Date: Fri Mar 31 16:24:15 2017 +0900 ability to load variable from file commit7f4b273a05
Merge:7bc6a0a
b44660a
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Mon Oct 30 19:55:34 2017 -0700 Merge pull request #676 from rliebz/lexicographic-sort Consider case when sorting strings commitb44660ac3d
Author: Robert Liebowitz <rliebz@gmail.com> Date: Sat Oct 28 03:00:11 2017 -0400 Consider case when sorting strings This makes sorting flags and other sections consistent with how most command line tools function, by placing both flags `-A` and `-a` before a flag `-B`. commit7bc6a0acff
Merge:2997500
40263f4
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sat Oct 14 13:27:26 2017 -0700 Merge pull request #628 from phinnaeus/master Allow custom ExitError handler function commit40263f4d6a
Merge:7233c50
2997500
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Fri Oct 13 12:05:14 2017 -0700 Merge branch 'master' into master commit2997500ba5
Merge:ac24947
c202606
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sat Oct 7 13:56:23 2017 -0700 Merge pull request #672 from sierraechobravo/master fix go report card issues commitc202606a17
Author: Sebastian Sprenger <Sebastian.Sprenger@booxware.de> Date: Fri Oct 6 07:29:13 2017 +0200 fix golint issues commitc3cc74dac7
Author: Sebastian Sprenger <Sebastian.Sprenger@booxware.de> Date: Fri Oct 6 07:28:43 2017 +0200 fix ineffective assigns commit67ee172e6d
Author: Sebastian Sprenger <Sebastian.Sprenger@booxware.de> Date: Fri Oct 6 07:28:18 2017 +0200 fix misspelling issue commitac249472b7
Merge:7fb9c86
cbbe4c1
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Mon Sep 25 20:41:18 2017 -0700 Merge pull request #661 from rliebz/custom-flag-help Allow customization of prefixes and environment variable hints in flag help strings commit7233c502e3
Merge:5dc55f2
7fb9c86
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Fri Sep 22 10:08:36 2017 -0700 Merge branch 'master' into master commitcbbe4c1a2c
Author: Robert Liebowitz <rliebz@gmail.com> Date: Mon Sep 18 00:44:42 2017 -0400 Add tests for custom flag prefix/env hints commit11d45572f9
Author: rliebz <rliebz@gmail.com> Date: Sat Aug 26 07:42:25 2017 -0400 Export funcs to configure flag prefix/env hints This will allow users to customize the prefix section or env hint section of the flag entries in the help menu without having to reimplement the rest of the logic required in defining FlagStringer. commit7fb9c86b14
Merge:f017f86
1d334f1
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Sun Sep 10 21:08:19 2017 -0700 Merge pull request #664 from maguro/master Add newline before command categories commit1d334f10ce
Author: Alan D. Cabrera <adc@toolazydogs.com> Date: Fri Sep 8 10:37:48 2017 -0700 Add newline before command categories The simple formatting change adds a nice blank line before each command category. Documentation in README.md is also updated to be more accurate. commit5dc55f2287
Merge:10e81ba
f017f86
Author: Dan Buch <dan@meatballhat.com> Date: Sun Aug 13 12:42:49 2017 -0400 Merge branch 'master' into master commitf017f86fcc
Merge:cfb3883
44c6487
Author: Dan Buch <dan@meatballhat.com> Date: Sun Aug 13 10:59:49 2017 -0400 Merge pull request #659 from urfave/define-flag-precedence Define flag source precedence in README commit44c648739b
Merge:e1fa109
cfb3883
Author: Dan Buch <dan@meatballhat.com> Date: Sun Aug 13 10:54:04 2017 -0400 Merge branch 'master' into define-flag-precedence commitcfb3883072
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Thu Aug 10 18:42:03 2017 -0700 Prepare CHANGELOG for v1.20.0 release commitf5513590f5
Merge:6a70c4c
b99aa81
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Thu Aug 10 18:06:12 2017 -0700 Merge branch 'master' into backport-json-support commite1fa109a31
Author: Jesse Szwedko <jesse.szwedko@gmail.com> Date: Thu Aug 10 17:54:24 2017 -0700 Define flag source precedence in README Fixes #646 commit688c5a9d4f
Merge:7250c97
4b90d79
Author: Dan Buch <dan@meatballhat.com> Date: Thu Aug 3 14:38:20 2017 -0400 Merge branch 'master' into merging-jereksel-zsh commit10e81bacd1
Merge:5d528e2
4b90d79
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Thu Jul 20 12:44:56 2017 -0700 Merge branch 'master' into master commit5d528e2052
Author: Tyler Davis <tyler.davis@gmail.com> Date: Wed Jun 28 13:04:09 2017 -0700 use exit errors in uts commit58450552ee
Author: Tyler Davis <tyler.davis@gmail.com> Date: Wed Jun 28 12:52:50 2017 -0700 Add Test commit71bdf81f5a
Author: Tyler Davis <tyler.davis@gmail.com> Date: Wed Jun 28 10:10:11 2017 -0700 sigh... fix one more named parameter issue commit172bb92059
Author: Tyler Davis <tyler.davis@gmail.com> Date: Wed Jun 28 10:07:25 2017 -0700 fix named parameter issue commit530df59178
Author: Tyler Davis <tyler.davis@gmail.com> Date: Wed Jun 28 09:52:12 2017 -0700 Pass context into handleExitCoder commit9d61cbad02
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Tue Apr 25 12:45:08 2017 -0700 Updated command.go to use App handleExitCoder commitceee6408d5
Author: Tyler Davis <tyler.davis@gmail.com> Date: Tue Apr 25 13:02:05 2017 -0700 Revert "Fix how to do defaults in app.go" This reverts commit 8906567dc2ad52fd31c50cf02fa606505a1323ba. commit80b09a4d11
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Tue Apr 25 11:20:41 2017 -0700 Fix how to do defaults in app.go commit827da610b4
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Tue Apr 25 09:33:54 2017 -0700 Add a bit more documentation commit538742687b
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Tue Apr 25 09:31:53 2017 -0700 Add ExitErrHandlerFunc type commitc48a829640
Author: Tyler Davis <phinnaeus@users.noreply.github.com> Date: Tue Apr 25 09:29:43 2017 -0700 Allow custom exit err handlers commit6a70c4cc92
Author: John Weldon <johnweldon4@gmail.com> Date: Sat Jul 2 12:35:48 2016 -0700 Add JSON InputSource to altsrc package - Implement NewJSONSource* functions for returning an InputSource from various JSON data sources. - Copy and modify YAML tests for the JSON InputSource Changes: * Reverted the method calls and structs to match the v1 interface commit7250c97913
Merge:363d9c9
0bdedde
Author: Dan Buch <dan@meatballhat.com> Date: Wed Dec 21 15:11:00 2016 -0500 Merge branch 'master' into merging-jereksel-zsh commit363d9c9a31
Author: Dan Buch <daniel.buch@gmail.com> Date: Sun Jul 24 17:29:13 2016 -0400 Add a hack so that zsh completion only runs for zsh commit1cbb9a7f30
Merge:e43a9fb
ceeebab
Author: Dan Buch <daniel.buch@gmail.com> Date: Sun Jul 24 17:12:43 2016 -0400 Merge branch 'zsh' of https://github.com/jereksel/cli into jereksel-zsh commitceeebaba04
Author: Andrzej Ressel <jereksel@gmail.com> Date: Thu Jul 21 00:02:16 2016 +0200 [PoC] Improve zsh autocompletions commitaba73cedac
Author: jhowarth <jhowarth@riotgames.com> Date: Tue Mar 3 14:02:42 2015 -0800 Copy the writer of the App to the subcommand App commita6482d2687
Merge:b5844af
50c77ec
Author: jhowarth <jhowarth@riotgames.com> Date: Mon Mar 2 15:21:01 2015 -0800 Merge remote-tracking branch 'upstream/master' Conflicts: app.go command.go flag.go commitb5844af298
Merge:8f1fb06
145da32
Author: Jesse Howarth <jahowarth@gmail.com> Date: Mon Mar 2 14:53:57 2015 -0800 Merge pull request #2 from ivey/requiredFlags Required flags commit145da3210f
Author: jhowarth <jhowarth@riotgames.com> Date: Mon Mar 2 12:06:42 2015 -0800 don't require flags when the help flag is included commit6023f370c1
Author: jhowarth <jhowarth@riotgames.com> Date: Mon Mar 2 12:00:21 2015 -0800 dry error messages commite67e05f617
Author: jhowarth <jhowarth@riotgames.com> Date: Mon Mar 2 11:56:29 2015 -0800 DRY error handling commitcbd95292ac
Author: jhowarth <jhowarth@riotgames.com> Date: Mon Mar 2 11:18:59 2015 -0800 Remove debugging commit8f1fb06a58
Merge:9908e96
4b2fcdb
Author: Jesse Howarth <jahowarth@gmail.com> Date: Tue Dec 2 15:23:01 2014 -0800 Merge pull request #1 from ivey/required_flags Required flags commit4b2fcdb1ad
Author: Jesse Howarth and Michael Ivey <datdevs+jhowarth+michael.ivey@riotgames.com> Date: Tue Dec 2 21:08:24 2014 +0000 Add tests for required flags commit73e64a14fd
Author: Jesse Howarth and Michael Ivey <datdevs+jhowarth+michael.ivey@riotgames.com> Date: Tue Dec 2 19:02:56 2014 +0000 Add (required) to help of flags that are required. commit7e05320026
Author: Jesse Howarth and Michael Ivey <datdevs+jhowarth+michael.ivey@riotgames.com> Date: Tue Dec 2 17:44:55 2014 +0000 Implement required flags
This commit is contained in:
parent
b626059537
commit
c75fee9224
4
.github/CODEOWNERS
vendored
Normal file
4
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# See https://help.github.com/articles/about-codeowners/
|
||||
# for more info about CODEOWNERS file
|
||||
|
||||
* @urfave/cli
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
*.coverprofile
|
||||
node_modules/
|
||||
vendor
|
41
.travis.yml
41
.travis.yml
@ -1,28 +1,35 @@
|
||||
language: go
|
||||
sudo: false
|
||||
dist: trusty
|
||||
osx_image: xcode8.3
|
||||
go: 1.11.x
|
||||
dist: bionic
|
||||
osx_image: xcode10
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- linux
|
||||
- osx
|
||||
|
||||
env:
|
||||
GO111MODULE=on
|
||||
GOPROXY=https://proxy.golang.org
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- node_modules
|
||||
|
||||
before_script:
|
||||
- if [[ $(uname) == Darwin ]]; then
|
||||
sudo pip2 install flake8;
|
||||
else
|
||||
pip install --user flake8;
|
||||
fi
|
||||
- mkdir -p ${GOPATH%%:*}/src/gopkg.in/urfave
|
||||
- rm -rvf ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2
|
||||
- rm -rvf ${GOPATH%%:*}/pkg/*/gopkg.in/urfave/cli.v2.a
|
||||
- ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH%%:*}/src/gopkg.in/urfave/cli.v2
|
||||
- go get github.com/urfave/gfmrun/cmd/gfmrun
|
||||
- go get golang.org/x/tools/cmd/goimports
|
||||
- npm install markdown-toc
|
||||
- go mod tidy
|
||||
|
||||
script:
|
||||
- flake8 runtests cli-v1-to-v2 generate-flag-types
|
||||
- make all
|
||||
- go run build.go vet
|
||||
- go run build.go test
|
||||
- go run build.go gfmrun
|
||||
- go run build.go toc
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
111
CHANGELOG.md
111
CHANGELOG.md
@ -39,11 +39,110 @@
|
||||
|
||||
### Removed
|
||||
|
||||
## [1.22.1] - 2019-09-11
|
||||
|
||||
### Fixed
|
||||
|
||||
### Deprecated
|
||||
* Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael)
|
||||
* Don't generate fish completion for hidden commands [urfave/cli/pull/891](https://github.com/urfave/891) via [@saschagrunert](https://github.com/saschagrunert)
|
||||
* Using short flag names for required flags throws an error in [urfave/cli/pull/890](https://github.com/urfave/cli/pull/890) via [@asahasrabuddhe](https://github.com/asahasrabuddhe)
|
||||
|
||||
### Security
|
||||
### Changed
|
||||
|
||||
* Remove flag code generation logic, legacy python test runner in [urfave/cli/pull/883](https://github.com/urfave/cli/pull/883) via [@asahasrabuddhe](https://github.com/asahasrabuddhe)
|
||||
* Enable Go Modules support, drop support for `Go 1.10` add support for `Go 1.13` in [urfave/cli/pull/885](https://github.com/urfave/cli/pull/885) via [@asahasrabuddhe](https://github.com/asahasrabuddhe)
|
||||
|
||||
## [1.22.0] - 2019-09-07
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix Subcommands not falling back to `app.ExitEventHandler` in [urfave/cli/pull/856](https://github.com/urfave/cli/pull/856) via [@FaranIdo](https://github.com/FaranIdo)
|
||||
|
||||
### Changed
|
||||
|
||||
* Clarify that altsrc supports both TOML and JSON in [urfave/cli/pull/774](https://github.com/urfave/cli/pull/774) via [@whereswaldon](https://github.com/whereswaldon)
|
||||
* Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0)
|
||||
* Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe)
|
||||
* Changed the supported go versions to `1.10`, `1.11`, `1.12` in [urfave/cli/pull/843](https://github.com/urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks)
|
||||
* Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88)
|
||||
* Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert)
|
||||
|
||||
### Added
|
||||
|
||||
* Added `UseShortOptionHandling` for combining short flags in [urfave/cli/pull/735](https://github.com/urfave/cli/pull/735) via [@rliebz](https://github.com/rliebz)
|
||||
* Added support for flags bash completion in [urfave/cli/pull/808](https://github.com/urfave/cli/pull/808) via [@yogeshlonkar](https://github.com/yogeshlonkar)
|
||||
* Added the `TakesFile` indicator to flag in [urfave/cli/pull/851](https://github.com/urfave/cli/pull/851) via [@saschagrunert](https://github.com/saschagrunert)
|
||||
* Added fish shell completion support in [urfave/cli/pull/848](https://github.com/urfave/cli/pull/848) via [@saschagrunert](https://github.com/saschagrunert)
|
||||
|
||||
## [1.21.0] - 2019-08-02
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix using "slice" flag types with `EnvVar` in [urfave/cli/pull/687](https://github.com/urfave/cli/pull/687) via [@joshuarubin](https://github.com/joshuarubin)
|
||||
* Fix regression of `SkipFlagParsing` behavior in [urfave/cli/pull/697](https://github.com/urfave/cli/pull/697) via [@jszwedko](https://github.com/jszwedko)
|
||||
* Fix handling `ShortOptions` and `SkipArgReorder` in [urfave/cli/pull/686](https://github.com/urfave/cli/pull/686) via [@baude](https://github.com/baude)
|
||||
* Fix args reordering when bool flags are present in [urfave/cli/pull/712](https://github.com/urfave/cli/pull/712) via [@windler](https://github.com/windler)
|
||||
* Fix parsing of short options in [urfave/cli/pull/758](https://github.com/urfave/cli/pull/758) via [@vrothberg](https://github.com/vrothberg)
|
||||
* Fix unaligned indents for the command help messages in [urfave/cli/pull/806](https://github.com/urfave/cli/pull/806) via [@mingrammer](https://github.com/mingrammer)
|
||||
|
||||
### Changed
|
||||
|
||||
* Cleaned up help output in [urfave/cli/pull/664](https://github.com/urfave/cli/pull/664) via [@maguro](https://github.com/maguro)
|
||||
* Remove redundant nil checks in [urfave/cli/pull/773](https://github.com/urfave/cli/pull/773) via [@teresy](https://github.com/teresy)
|
||||
* Case is now considered when sorting strings in [urfave/cli/pull/676](https://github.com/urfave/cli/pull/676) via [@rliebz](https://github.com/rliebz)
|
||||
|
||||
### Added
|
||||
|
||||
* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/)
|
||||
* Backport JSON `InputSource` to v1 in [urfave/cli/pull/598](https://github.com/urfave/cli/pull/598) via [@jszwedko](https://github.com/jszwedko)
|
||||
* Allow more customization of flag help strings in [urfave/cli/pull/661](https://github.com/urfave/cli/pull/661) via [@rliebz](https://github.com/rliebz)
|
||||
* Allow custom `ExitError` handler function in [urfave/cli/pull/628](https://github.com/urfave/cli/pull/628) via [@phinnaeus](https://github.com/phinnaeus)
|
||||
* Allow loading a variable from a file in [urfave/cli/pull/675](https://github.com/urfave/cli/pull/675) via [@jmccann](https://github.com/jmccann)
|
||||
* Allow combining short bool names in [urfave/cli/pull/684](https://github.com/urfave/cli/pull/684) via [@baude](https://github.com/baude)
|
||||
* Added test coverage to context in [urfave/cli/pull/788](https://github.com/urfave/cli/pull/788) via [@benzvan](https://github.com/benzvan)
|
||||
* Added go module support in [urfave/cli/pull/831](https://github.com/urfave/cli/pull/831) via [@saschagrunert](https://github.com/saschagrunert)
|
||||
|
||||
## [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
|
||||
@ -403,7 +502,13 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`.
|
||||
### Added
|
||||
- Initial implementation.
|
||||
|
||||
[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD
|
||||
[Unreleased]: https://github.com/urfave/cli/compare/v1.22.1...HEAD
|
||||
[1.22.1]: https://github.com/urfave/cli/compare/v1.22.0...v1.22.1
|
||||
[1.22.0]: https://github.com/urfave/cli/compare/v1.21.0...v1.22.0
|
||||
[1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0
|
||||
[1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0
|
||||
[1.19.1]: https://github.com/urfave/cli/compare/v1.19.0...v1.19.1
|
||||
[1.19.0]: https://github.com/urfave/cli/compare/v1.18.0...v1.19.0
|
||||
[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
|
||||
|
74
CODE_OF_CONDUCT.md
Normal file
74
CODE_OF_CONDUCT.md
Normal file
@ -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
|
||||
|
18
CONTRIBUTING.md
Normal file
18
CONTRIBUTING.md
Normal file
@ -0,0 +1,18 @@
|
||||
## Contributing
|
||||
|
||||
Use @urfave/cli to ping the maintainers.
|
||||
|
||||
Feel free to put up a pull request to fix a bug or maybe add a feature. We will
|
||||
give it a code review and make sure that it does not break backwards
|
||||
compatibility. If collaborators agree that it is in line with
|
||||
the vision of the project, we will work with you to get the code into
|
||||
a mergeable state and merge it into the 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!
|
366
README.md
366
README.md
@ -3,15 +3,11 @@ cli
|
||||
|
||||
[![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli)
|
||||
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli)
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
|
||||
[![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli)
|
||||
[![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli)
|
||||
[![top level coverage](https://gocover.io/_badge/github.com/urfave/cli?0 "top level coverage")](http://gocover.io/github.com/urfave/cli) /
|
||||
[![altsrc coverage](https://gocover.io/_badge/github.com/urfave/cli/altsrc?0 "altsrc coverage")](http://gocover.io/github.com/urfave/cli/altsrc)
|
||||
|
||||
**Notice:** This is the library formerly known as
|
||||
`github.com/codegangsta/cli` -- Github will automatically redirect requests
|
||||
to this repository, but we recommend updating your references for clarity.
|
||||
[![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli)
|
||||
|
||||
cli is a simple, fast, and fun package for building command line apps in Go. The
|
||||
goal is to enable developers to write fast and distributable command line
|
||||
@ -23,7 +19,7 @@ applications in an expressive way.
|
||||
- [Installation](#installation)
|
||||
* [Supported platforms](#supported-platforms)
|
||||
* [Using the `v2` branch](#using-the-v2-branch)
|
||||
* [Pinning to the `v1` releases](#pinning-to-the-v1-releases)
|
||||
* [Using `v1` releases](#using-v1-releases)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Examples](#examples)
|
||||
* [Arguments](#arguments)
|
||||
@ -32,12 +28,22 @@ applications in an expressive way.
|
||||
+ [Alternate Names](#alternate-names)
|
||||
+ [Ordering](#ordering)
|
||||
+ [Values from the Environment](#values-from-the-environment)
|
||||
+ [Values from files](#values-from-files)
|
||||
+ [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others)
|
||||
<<<<<<< HEAD
|
||||
+ [Default Values for help output](#default-values-for-help-output)
|
||||
* [Subcommands](#subcommands)
|
||||
* [Subcommands categories](#subcommands-categories)
|
||||
* [Exit code](#exit-code)
|
||||
* [Shell Completion](#shell-completion)
|
||||
=======
|
||||
+ [Precedence](#precedence)
|
||||
* [Subcommands](#subcommands)
|
||||
* [Subcommands categories](#subcommands-categories)
|
||||
* [Exit code](#exit-code)
|
||||
* [Combining short options](#combining-short-options)
|
||||
* [Bash Completion](#bash-completion)
|
||||
>>>>>>> master
|
||||
+ [Enabling](#enabling)
|
||||
+ [Distribution](#distribution)
|
||||
+ [Customization](#customization)
|
||||
@ -62,12 +68,12 @@ organized, and expressive!
|
||||
|
||||
## Installation
|
||||
|
||||
Make sure you have a working Go environment. Go version 1.2+ is supported. [See
|
||||
Make sure you have a working Go environment. Go version 1.10+ is supported. [See
|
||||
the install instructions for Go](http://golang.org/doc/install.html).
|
||||
|
||||
To install cli, simply run:
|
||||
```
|
||||
$ go get github.com/urfave/cli
|
||||
$ go get github.com/urfave/cli/v2
|
||||
```
|
||||
|
||||
Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can
|
||||
@ -91,21 +97,21 @@ the new `master` branch once development there has settled down. The current
|
||||
`master` branch (mirrored as `v1`) is being manually merged into `v2` on
|
||||
an irregular human-based schedule, but generally if one wants to "upgrade" to
|
||||
`v2` *now* and accept the volatility (read: "awesomeness") that comes along with
|
||||
that, please use whatever version pinning of your preference, such as via
|
||||
`gopkg.in`:
|
||||
that, please use:
|
||||
|
||||
```
|
||||
$ go get gopkg.in/urfave/cli.v2
|
||||
$ go get github.com/urfave/cli/v2
|
||||
```
|
||||
|
||||
``` go
|
||||
...
|
||||
import (
|
||||
"gopkg.in/urfave/cli.v2" // imports as package "cli"
|
||||
"github.com/urfave/cli/v2" // imports as package "cli"
|
||||
)
|
||||
...
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
**NOTE**: There is a [migrator (python) script](./cli-v1-to-v2) available to aid
|
||||
with the transition from the v1 to v2 API.
|
||||
|
||||
@ -114,20 +120,22 @@ with the transition from the v1 to v2 API.
|
||||
Similarly to the section above describing use of the `v2` branch, if one wants
|
||||
to avoid any unexpected compatibility pains once `v2` becomes `master`, then
|
||||
pinning to `v1` is an acceptable option, e.g.:
|
||||
=======
|
||||
### Using `v1` releases
|
||||
>>>>>>> master
|
||||
|
||||
```
|
||||
$ go get gopkg.in/urfave/cli.v1
|
||||
$ go get github.com/urfave/cli
|
||||
```
|
||||
|
||||
``` go
|
||||
```go
|
||||
...
|
||||
import (
|
||||
"gopkg.in/urfave/cli.v1" // imports as package "cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
...
|
||||
```
|
||||
|
||||
This will pull the latest tagged `v1` release (e.g. `v1.18.1` at the time of writing).
|
||||
|
||||
## Getting Started
|
||||
|
||||
@ -142,9 +150,10 @@ discovery. So a cli app can be as little as one line of code in `main()`.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -163,9 +172,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -178,7 +188,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -202,9 +215,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -217,7 +231,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -266,9 +283,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -279,7 +297,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -295,9 +316,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -323,7 +345,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -337,10 +362,11 @@ scanned.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -369,7 +395,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -390,9 +419,10 @@ For example this:
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -406,7 +436,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -432,9 +465,10 @@ list for the `Name`. e.g.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -449,7 +483,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -473,10 +510,11 @@ For example this:
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -515,7 +553,10 @@ func main() {
|
||||
sort.Sort(cli.FlagsByName(app.Flags))
|
||||
sort.Sort(cli.CommandsByName(app.Commands))
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -538,9 +579,10 @@ You can also have the default value set from the environment via `EnvVars`. e.g
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -556,7 +598,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -571,9 +616,10 @@ resolves is used as the default.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -589,10 +635,52 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Values from files
|
||||
|
||||
You can also have the default value set from file via `FilePath`. e.g.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "password for the mysql database"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "password, p",
|
||||
Usage: "password for the mysql database",
|
||||
FilePath: "/etc/mysql/password",
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that default values set from file (e.g. `FilePath`) take precedence over
|
||||
default values set from the environment (e.g. `EnvVar`).
|
||||
|
||||
#### Values from alternate input sources (YAML, TOML, and others)
|
||||
|
||||
There is a separate package altsrc that adds support for getting flag values
|
||||
@ -600,6 +688,7 @@ from other file input sources.
|
||||
|
||||
Currently supported input source formats:
|
||||
* YAML
|
||||
* JSON
|
||||
* TOML
|
||||
|
||||
In order to get values for a flag from an alternate input source the following
|
||||
@ -622,9 +711,9 @@ the yaml input source for any flags that are defined on that command. As a note
|
||||
the "load" flag used would also have to be defined on the command flags in order
|
||||
for this code snipped to work.
|
||||
|
||||
Currently only the aboved specified formats are supported but developers can
|
||||
add support for other input sources by implementing the
|
||||
altsrc.InputSourceContext for their given sources.
|
||||
Currently only YAML, JSON, and TOML files are supported but developers can add support
|
||||
for other input sources by implementing the altsrc.InputSourceContext for their
|
||||
given sources.
|
||||
|
||||
Here is a more complete sample of a command using YAML support:
|
||||
|
||||
@ -637,10 +726,11 @@ package notmain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"gopkg.in/urfave/cli.v2/altsrc"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v2/altsrc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -678,7 +768,7 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -693,7 +783,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -703,6 +796,14 @@ Will result in help output like:
|
||||
--port value Use a randomized port (default: random)
|
||||
```
|
||||
|
||||
#### Precedence
|
||||
|
||||
The precedence for flag value sources is as follows (highest to lowest):
|
||||
|
||||
0. Command line flag value from user
|
||||
0. Environment variable (if specified)
|
||||
0. Configuration file (if specified)
|
||||
0. Default defined on the flag
|
||||
|
||||
### Subcommands
|
||||
|
||||
@ -717,9 +818,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -769,7 +871,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -785,9 +890,10 @@ E.g.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -807,7 +913,10 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -815,7 +924,7 @@ Will include:
|
||||
|
||||
```
|
||||
COMMANDS:
|
||||
noop
|
||||
noop
|
||||
|
||||
Template actions:
|
||||
add
|
||||
@ -828,14 +937,17 @@ Calling `App.Run` will not automatically call `os.Exit`, which means that by
|
||||
default the exit code will "fall through" to being `0`. An explicit exit code
|
||||
may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
|
||||
`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
|
||||
|
||||
<!-- {
|
||||
"error": "Ginger croutons are not in the soup"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -855,13 +967,86 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Shell Completion
|
||||
### Combining short options
|
||||
|
||||
You can enable completion commands by setting the `EnableShellCompletion`
|
||||
Traditional use of options using their shortnames look like this:
|
||||
|
||||
```
|
||||
$ cmd -s -o -m "Some message"
|
||||
```
|
||||
|
||||
Suppose you want users to be able to combine options with their shortnames. This
|
||||
can be done using the `UseShortOptionHandling` bool in your app configuration,
|
||||
or for individual commands by attaching it to the command configuration. For
|
||||
example:
|
||||
|
||||
<!-- {
|
||||
"args": ["short", "-som", "Some message"],
|
||||
"output": "serve: true\noption: true\nmessage: Some message\n"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{}
|
||||
app.UseShortOptionHandling = true
|
||||
app.Commands = []*cli.Command{
|
||||
{
|
||||
Name: "short",
|
||||
Usage: "complete a task on the list",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "serve, s"},
|
||||
cli.BoolFlag{Name: "option, o"},
|
||||
cli.StringFlag{Name: "message, m"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("serve:", c.Bool("serve"))
|
||||
fmt.Println("option:", c.Bool("option"))
|
||||
fmt.Println("message:", c.String("message"))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your program has any number of bool flags such as `serve` and `option`, and
|
||||
optionally one non-bool flag `message`, with the short options of `-s`, `-o`,
|
||||
and `-m` respectively, setting `UseShortOptionHandling` will also support the
|
||||
following syntax:
|
||||
|
||||
```
|
||||
$ cmd -som "Some message"
|
||||
```
|
||||
|
||||
If you enable `UseShortOptionHandling`, then you must not use any flags that
|
||||
have a single leading `-` or this will result in failures. For example,
|
||||
`-option` can no longer be used. Flags with two leading dashes (such as
|
||||
`--options`) are still valid.
|
||||
|
||||
### Bash Completion
|
||||
|
||||
You can enable completion commands by setting the `EnableBashCompletion`
|
||||
flag on the `App` object. By default, this setting will only auto-complete to
|
||||
show an app's subcommands, but you can write your own completion methods for
|
||||
the App or its subcommands.
|
||||
@ -875,16 +1060,17 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
||||
|
||||
app := &cli.App{
|
||||
EnableShellCompletion: true,
|
||||
EnableBashCompletion: true,
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "complete",
|
||||
@ -894,7 +1080,7 @@ func main() {
|
||||
fmt.Println("completed task: ", c.Args().First())
|
||||
return nil
|
||||
},
|
||||
ShellComplete: func(c *cli.Context) {
|
||||
BashComplete: func(c *cli.Context) {
|
||||
// This will complete if no args are passed
|
||||
if c.NArg() > 0 {
|
||||
return
|
||||
@ -907,23 +1093,19 @@ func main() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Enabling
|
||||
|
||||
You can generate bash or zsh completion code by using the flag `--init-completion bash` or `--init-completion zsh`.
|
||||
|
||||
To setup for bash:
|
||||
Source the `autocomplete/bash_autocomplete` file in your .bashrc file while setting the `PROG` variable to the name of your program:
|
||||
|
||||
```
|
||||
eval "`myprogram --init-completion bash`"
|
||||
```
|
||||
|
||||
Alternatively, you can put the completion code in your `.bashrc` file:
|
||||
```
|
||||
myprogram --init-completion bash >> ~/.bashrc
|
||||
PROG=myprogram source /.../cli/autocomplete/bash_autocomplete
|
||||
```
|
||||
|
||||
#### Distribution
|
||||
@ -955,9 +1137,10 @@ The default shell completion flag (`--generate-completion`) is defined as
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -974,7 +1157,10 @@ func main() {
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Run(os.Args)
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -1000,10 +1186,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -1042,7 +1229,7 @@ VERSION:
|
||||
cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
|
||||
fmt.Println("Ha HA. I pwnd the help!!1")
|
||||
}
|
||||
|
||||
|
||||
(&cli.App{}).Run(os.Args)
|
||||
}
|
||||
```
|
||||
@ -1058,9 +1245,10 @@ setting `cli.HelpFlag`, e.g.:
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -1069,7 +1257,7 @@ func main() {
|
||||
Usage: "HALP",
|
||||
EnvVars: []string{"SHOW_HALP", "HALPPLZ"},
|
||||
}
|
||||
|
||||
|
||||
(&cli.App{}).Run(os.Args)
|
||||
}
|
||||
```
|
||||
@ -1093,9 +1281,10 @@ setting `cli.VersionFlag`, e.g.:
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -1103,7 +1292,7 @@ func main() {
|
||||
Name: "print-version", Aliases: []string{"V"},
|
||||
Usage: "print only the version",
|
||||
}
|
||||
|
||||
|
||||
app := &cli.App{
|
||||
Name: "partay",
|
||||
Version: "v19.99.0",
|
||||
@ -1123,9 +1312,10 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -1136,7 +1326,7 @@ func main() {
|
||||
cli.VersionPrinter = func(c *cli.Context) {
|
||||
fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
|
||||
}
|
||||
|
||||
|
||||
app := &cli.App{
|
||||
Name: "partay",
|
||||
Version: "v19.99.0",
|
||||
@ -1165,7 +1355,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -1217,7 +1407,7 @@ func (g *genericType) String() string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.App{
|
||||
app := &cli.App{
|
||||
Name: "kənˈtrīv",
|
||||
Version: "v19.99.0",
|
||||
Compiled: time.Now(),
|
||||
@ -1406,7 +1596,7 @@ func main() {
|
||||
app.Writer = &hexWriter{}
|
||||
app.ErrWriter = &hexWriter{}
|
||||
}
|
||||
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
@ -1418,16 +1608,4 @@ func wopAction(c *cli.Context) error {
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
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, please open an issue.
|
||||
See [./CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
@ -1,3 +0,0 @@
|
||||
package altsrc
|
||||
|
||||
//go:generate python ../generate-flag-types altsrc -i ../flag-types.json -o flag_generated.go
|
@ -6,7 +6,7 @@ import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// FlagInputSourceExtension is an extension interface of cli.Flag that
|
||||
@ -72,7 +72,7 @@ func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourc
|
||||
}
|
||||
if value != nil {
|
||||
for _, name := range f.Names() {
|
||||
f.set.Set(name, value.String())
|
||||
_ = f.set.Set(name, value.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,7 +135,7 @@ func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo
|
||||
}
|
||||
if value {
|
||||
for _, name := range f.Names() {
|
||||
f.set.Set(name, strconv.FormatBool(value))
|
||||
_ = f.set.Set(name, strconv.FormatBool(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,7 +153,7 @@ func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSource
|
||||
}
|
||||
if value != "" {
|
||||
for _, name := range f.Names() {
|
||||
f.set.Set(name, value)
|
||||
_ = f.set.Set(name, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,7 +181,7 @@ func (f *PathFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCo
|
||||
value = filepath.Join(filepath.Dir(basePathAbs), value)
|
||||
}
|
||||
|
||||
f.set.Set(name, value)
|
||||
_ = f.set.Set(name, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -199,7 +199,7 @@ func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceCon
|
||||
}
|
||||
if value > 0 {
|
||||
for _, name := range f.Names() {
|
||||
f.set.Set(name, strconv.FormatInt(int64(value), 10))
|
||||
_ = f.set.Set(name, strconv.FormatInt(int64(value), 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -217,7 +217,7 @@ func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSour
|
||||
}
|
||||
if value > 0 {
|
||||
for _, name := range f.Names() {
|
||||
f.set.Set(name, value.String())
|
||||
_ = f.set.Set(name, value.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,7 +236,7 @@ func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourc
|
||||
if value > 0 {
|
||||
floatStr := float64ToString(value)
|
||||
for _, name := range f.Names() {
|
||||
f.set.Set(name, floatStr)
|
||||
_ = f.set.Set(name, floatStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,9 @@ package altsrc
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// WARNING: This file is generated!
|
||||
|
||||
// BoolFlag is the flag type that wraps cli.BoolFlag to allow
|
||||
// for other values to be specified
|
||||
type BoolFlag struct {
|
||||
@ -20,18 +17,11 @@ func NewBoolFlag(fl *cli.BoolFlag) *BoolFlag {
|
||||
return &BoolFlag{BoolFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped BoolFlag.Apply
|
||||
func (f *BoolFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped BoolFlag.Apply
|
||||
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.BoolFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped BoolFlag.ApplyWithError
|
||||
func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.BoolFlag.ApplyWithError(set)
|
||||
return f.BoolFlag.Apply(set)
|
||||
}
|
||||
|
||||
// DurationFlag is the flag type that wraps cli.DurationFlag to allow
|
||||
@ -46,18 +36,11 @@ func NewDurationFlag(fl *cli.DurationFlag) *DurationFlag {
|
||||
return &DurationFlag{DurationFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped DurationFlag.Apply
|
||||
func (f *DurationFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped DurationFlag.Apply
|
||||
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.DurationFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped DurationFlag.ApplyWithError
|
||||
func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.DurationFlag.ApplyWithError(set)
|
||||
return f.DurationFlag.Apply(set)
|
||||
}
|
||||
|
||||
// Float64Flag is the flag type that wraps cli.Float64Flag to allow
|
||||
@ -72,18 +55,11 @@ func NewFloat64Flag(fl *cli.Float64Flag) *Float64Flag {
|
||||
return &Float64Flag{Float64Flag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Float64Flag.Apply
|
||||
func (f *Float64Flag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped Float64Flag.Apply
|
||||
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.Float64Flag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Float64Flag.ApplyWithError
|
||||
func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.Float64Flag.ApplyWithError(set)
|
||||
return f.Float64Flag.Apply(set)
|
||||
}
|
||||
|
||||
// GenericFlag is the flag type that wraps cli.GenericFlag to allow
|
||||
@ -98,18 +74,11 @@ func NewGenericFlag(fl *cli.GenericFlag) *GenericFlag {
|
||||
return &GenericFlag{GenericFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped GenericFlag.Apply
|
||||
func (f *GenericFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped GenericFlag.Apply
|
||||
func (f *GenericFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.GenericFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped GenericFlag.ApplyWithError
|
||||
func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.GenericFlag.ApplyWithError(set)
|
||||
return f.GenericFlag.Apply(set)
|
||||
}
|
||||
|
||||
// Int64Flag is the flag type that wraps cli.Int64Flag to allow
|
||||
@ -124,18 +93,11 @@ func NewInt64Flag(fl *cli.Int64Flag) *Int64Flag {
|
||||
return &Int64Flag{Int64Flag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Int64Flag.Apply
|
||||
func (f *Int64Flag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped Int64Flag.Apply
|
||||
func (f *Int64Flag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.Int64Flag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Int64Flag.ApplyWithError
|
||||
func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.Int64Flag.ApplyWithError(set)
|
||||
return f.Int64Flag.Apply(set)
|
||||
}
|
||||
|
||||
// IntFlag is the flag type that wraps cli.IntFlag to allow
|
||||
@ -150,18 +112,11 @@ func NewIntFlag(fl *cli.IntFlag) *IntFlag {
|
||||
return &IntFlag{IntFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped IntFlag.Apply
|
||||
func (f *IntFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped IntFlag.Apply
|
||||
func (f *IntFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.IntFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped IntFlag.ApplyWithError
|
||||
func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.IntFlag.ApplyWithError(set)
|
||||
return f.IntFlag.Apply(set)
|
||||
}
|
||||
|
||||
// IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow
|
||||
@ -176,18 +131,11 @@ func NewIntSliceFlag(fl *cli.IntSliceFlag) *IntSliceFlag {
|
||||
return &IntSliceFlag{IntSliceFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped IntSliceFlag.Apply
|
||||
func (f *IntSliceFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped IntSliceFlag.Apply
|
||||
func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.IntSliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped IntSliceFlag.ApplyWithError
|
||||
func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.IntSliceFlag.ApplyWithError(set)
|
||||
return f.IntSliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow
|
||||
@ -202,18 +150,11 @@ func NewInt64SliceFlag(fl *cli.Int64SliceFlag) *Int64SliceFlag {
|
||||
return &Int64SliceFlag{Int64SliceFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Int64SliceFlag.Apply
|
||||
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped Int64SliceFlag.Apply
|
||||
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.Int64SliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Int64SliceFlag.ApplyWithError
|
||||
func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.Int64SliceFlag.ApplyWithError(set)
|
||||
return f.Int64SliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// Float64SliceFlag is the flag type that wraps cli.Float64SliceFlag to allow
|
||||
@ -230,16 +171,9 @@ func NewFloat64SliceFlag(fl *cli.Float64SliceFlag) *Float64SliceFlag {
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Float64SliceFlag.Apply
|
||||
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) {
|
||||
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.Float64SliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Float64SliceFlag.ApplyWithError
|
||||
func (f *Float64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.Float64SliceFlag.ApplyWithError(set)
|
||||
return f.Float64SliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// StringFlag is the flag type that wraps cli.StringFlag to allow
|
||||
@ -254,18 +188,11 @@ func NewStringFlag(fl *cli.StringFlag) *StringFlag {
|
||||
return &StringFlag{StringFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped StringFlag.Apply
|
||||
func (f *StringFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped StringFlag.Apply
|
||||
func (f *StringFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.StringFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped StringFlag.ApplyWithError
|
||||
func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.StringFlag.ApplyWithError(set)
|
||||
return f.StringFlag.Apply(set)
|
||||
}
|
||||
|
||||
// PathFlag is the flag type that wraps cli.PathFlag to allow
|
||||
@ -282,16 +209,9 @@ func NewPathFlag(fl *cli.PathFlag) *PathFlag {
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped PathFlag.Apply
|
||||
func (f *PathFlag) Apply(set *flag.FlagSet) {
|
||||
func (f *PathFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.PathFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped PathFlag.ApplyWithError
|
||||
func (f *PathFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.PathFlag.ApplyWithError(set)
|
||||
return f.PathFlag.Apply(set)
|
||||
}
|
||||
|
||||
// StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow
|
||||
@ -306,18 +226,11 @@ func NewStringSliceFlag(fl *cli.StringSliceFlag) *StringSliceFlag {
|
||||
return &StringSliceFlag{StringSliceFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped StringSliceFlag.Apply
|
||||
func (f *StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped StringSliceFlag.Apply
|
||||
func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.StringSliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped StringSliceFlag.ApplyWithError
|
||||
func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.StringSliceFlag.ApplyWithError(set)
|
||||
return f.StringSliceFlag.Apply(set)
|
||||
}
|
||||
|
||||
// Uint64Flag is the flag type that wraps cli.Uint64Flag to allow
|
||||
@ -332,18 +245,11 @@ func NewUint64Flag(fl *cli.Uint64Flag) *Uint64Flag {
|
||||
return &Uint64Flag{Uint64Flag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Uint64Flag.Apply
|
||||
func (f *Uint64Flag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped Uint64Flag.Apply
|
||||
func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.Uint64Flag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped Uint64Flag.ApplyWithError
|
||||
func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.Uint64Flag.ApplyWithError(set)
|
||||
return f.Uint64Flag.Apply(set)
|
||||
}
|
||||
|
||||
// UintFlag is the flag type that wraps cli.UintFlag to allow
|
||||
@ -358,16 +264,9 @@ func NewUintFlag(fl *cli.UintFlag) *UintFlag {
|
||||
return &UintFlag{UintFlag: fl, set: nil}
|
||||
}
|
||||
|
||||
// Apply saves the flagSet for later usage calls, then calls the
|
||||
// wrapped UintFlag.Apply
|
||||
func (f *UintFlag) Apply(set *flag.FlagSet) {
|
||||
// Apply saves the flagSet for later usage calls, then calls
|
||||
// the wrapped UintFlag.Apply
|
||||
func (f *UintFlag) Apply(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
f.UintFlag.Apply(set)
|
||||
}
|
||||
|
||||
// ApplyWithError saves the flagSet for later usage calls, then calls the
|
||||
// wrapped UintFlag.ApplyWithError
|
||||
func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
f.set = set
|
||||
return f.UintFlag.ApplyWithError(set)
|
||||
return f.UintFlag.Apply(set)
|
||||
}
|
||||
|
@ -3,13 +3,12 @@ package altsrc
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/urfave/cli/v2"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
type testApplyInputSource struct {
|
||||
@ -252,30 +251,30 @@ func TestDurationApplyInputSourceMethodSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: time.Duration(30 * time.Second),
|
||||
MapValue: 30 * time.Second,
|
||||
})
|
||||
expect(t, time.Duration(30*time.Second), c.Duration("test"))
|
||||
expect(t, 30*time.Second, c.Duration("test"))
|
||||
}
|
||||
|
||||
func TestDurationApplyInputSourceMethodContextSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewDurationFlag(&cli.DurationFlag{Name: "test"}),
|
||||
FlagName: "test",
|
||||
MapValue: time.Duration(30 * time.Second),
|
||||
ContextValueString: time.Duration(15 * time.Second).String(),
|
||||
MapValue: 30 * time.Second,
|
||||
ContextValueString: (15 * time.Second).String(),
|
||||
})
|
||||
expect(t, time.Duration(15*time.Second), c.Duration("test"))
|
||||
expect(t, 15*time.Second, c.Duration("test"))
|
||||
}
|
||||
|
||||
func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) {
|
||||
c := runTest(t, testApplyInputSource{
|
||||
Flag: NewDurationFlag(&cli.DurationFlag{Name: "test", EnvVars: []string{"TEST"}}),
|
||||
FlagName: "test",
|
||||
MapValue: time.Duration(30 * time.Second),
|
||||
MapValue: 30 * time.Second,
|
||||
EnvVarName: "TEST",
|
||||
EnvVarValue: time.Duration(15 * time.Second).String(),
|
||||
EnvVarValue: (15 * time.Second).String(),
|
||||
})
|
||||
expect(t, time.Duration(15*time.Second), c.Duration("test"))
|
||||
expect(t, 15*time.Second, c.Duration("test"))
|
||||
}
|
||||
|
||||
func TestFloat64ApplyInputSourceMethodSet(t *testing.T) {
|
||||
@ -316,19 +315,19 @@ func runTest(t *testing.T, test testApplyInputSource) *cli.Context {
|
||||
set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError)
|
||||
c := cli.NewContext(nil, set, nil)
|
||||
if test.EnvVarName != "" && test.EnvVarValue != "" {
|
||||
os.Setenv(test.EnvVarName, test.EnvVarValue)
|
||||
_ = os.Setenv(test.EnvVarName, test.EnvVarValue)
|
||||
defer os.Setenv(test.EnvVarName, "")
|
||||
}
|
||||
|
||||
test.Flag.Apply(set)
|
||||
if test.ContextValue != nil {
|
||||
flag := set.Lookup(test.FlagName)
|
||||
flag.Value = test.ContextValue
|
||||
f := set.Lookup(test.FlagName)
|
||||
f.Value = test.ContextValue
|
||||
}
|
||||
if test.ContextValueString != "" {
|
||||
set.Set(test.FlagName, test.ContextValueString)
|
||||
_ = set.Set(test.FlagName, test.ContextValueString)
|
||||
}
|
||||
test.Flag.ApplyInputSourceValue(c, inputSource)
|
||||
_ = test.Flag.ApplyInputSourceValue(c, inputSource)
|
||||
|
||||
return c
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
package altsrc
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
// InputSourceContext is an interface used to allow
|
||||
|
@ -2,11 +2,10 @@ package altsrc
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/urfave/cli/v2"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -22,7 +21,7 @@ func TestCommandJSONFileTest(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"test-cmd", "--load", fileName}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -52,11 +51,11 @@ func TestCommandJSONFileTestGlobalEnvVarWins(t *testing.T) {
|
||||
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
os.Setenv("THE_TEST", "10")
|
||||
_ = os.Setenv("THE_TEST", "10")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", fileName}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -87,11 +86,11 @@ func TestCommandJSONFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
||||
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
os.Setenv("THE_TEST", "10")
|
||||
_ = os.Setenv("THE_TEST", "10")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", fileName}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -123,7 +122,7 @@ func TestCommandJSONFileTestSpecifiedFlagWins(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"test-cmd", "--load", fileName, "--test", "7"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -155,7 +154,7 @@ func TestCommandJSONFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"test-cmd", "--load", fileName, "--top.test", "7"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -187,7 +186,7 @@ func TestCommandJSONFileTestDefaultValueFileWins(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"test-cmd", "--load", fileName}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -219,7 +218,7 @@ func TestCommandJSONFileTestDefaultValueFileWinsNested(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
test := []string{"test-cmd", "--load", fileName}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -250,11 +249,11 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWins(t *testing.T
|
||||
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
os.Setenv("THE_TEST", "11")
|
||||
_ = os.Setenv("THE_TEST", "11")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", fileName}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -284,11 +283,11 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *tes
|
||||
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
os.Setenv("THE_TEST", "11")
|
||||
_ = os.Setenv("THE_TEST", "11")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", fileName}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
|
@ -3,12 +3,11 @@ package altsrc
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/urfave/cli/v2"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
// NewJSONSourceFromFlagFunc returns a func that takes a cli.Context
|
||||
@ -29,13 +28,8 @@ func NewJSONSourceFromFile(f string) (InputSourceContext, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s, err := newJSONSource(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.file = f
|
||||
return s, nil
|
||||
return NewJSONSource(data)
|
||||
}
|
||||
|
||||
// NewJSONSourceFromReader returns an InputSourceContext suitable for
|
||||
@ -51,10 +45,6 @@ func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) {
|
||||
// NewJSONSource returns an InputSourceContext suitable for retrieving
|
||||
// config variables from raw JSON data.
|
||||
func NewJSONSource(data []byte) (InputSourceContext, error) {
|
||||
return newJSONSource(data)
|
||||
}
|
||||
|
||||
func newJSONSource(data []byte) (*jsonSource, error) {
|
||||
var deserialized map[string]interface{}
|
||||
if err := json.Unmarshal(data, &deserialized); err != nil {
|
||||
return nil, err
|
||||
@ -77,9 +67,9 @@ func (x *jsonSource) Int(name string) (int, error) {
|
||||
case int:
|
||||
return v, nil
|
||||
case float64:
|
||||
return int(float64(v)), nil
|
||||
return int(v), nil
|
||||
case float32:
|
||||
return int(float32(v)), nil
|
||||
return int(v), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,10 @@ package altsrc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/urfave/cli/v2"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
// MapInputSource implements InputSourceContext to return
|
||||
@ -23,15 +22,15 @@ func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool
|
||||
if sections := strings.Split(name, "."); len(sections) > 1 {
|
||||
node := tree
|
||||
for _, section := range sections[:len(sections)-1] {
|
||||
if child, ok := node[section]; !ok {
|
||||
child, ok := node[section]
|
||||
if !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
if ctype, ok := child.(map[interface{}]interface{}); !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
node = ctype
|
||||
}
|
||||
}
|
||||
ctype, ok := child.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
node = ctype
|
||||
}
|
||||
if val, ok := node[sections[len(sections)-1]]; ok {
|
||||
return val, true
|
||||
|
@ -7,20 +7,19 @@ package altsrc
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/urfave/cli/v2"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
)
|
||||
|
||||
func TestCommandTomFileTest(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
test := []string{"test-cmd", "--load", "current.toml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -45,15 +44,15 @@ func TestCommandTomFileTest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
os.Setenv("THE_TEST", "10")
|
||||
_ = os.Setenv("THE_TEST", "10")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
test := []string{"test-cmd", "--load", "current.toml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -79,15 +78,15 @@ func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
os.Setenv("THE_TEST", "10")
|
||||
_ = os.Setenv("THE_TEST", "10")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
test := []string{"test-cmd", "--load", "current.toml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -113,13 +112,13 @@ func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.toml", "--test", "7"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -145,14 +144,14 @@ func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte(`[top]
|
||||
_ = ioutil.WriteFile("current.toml", []byte(`[top]
|
||||
test = 15`), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.toml", "--top.test", "7"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -178,13 +177,13 @@ func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.toml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -210,13 +209,13 @@ func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.toml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -244,14 +243,14 @@ func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
||||
func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("test = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
os.Setenv("THE_TEST", "11")
|
||||
_ = os.Setenv("THE_TEST", "11")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.toml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -276,16 +275,16 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T
|
||||
}
|
||||
|
||||
func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *testing.T) {
|
||||
app := (&cli.App{})
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666)
|
||||
defer os.Remove("current.toml")
|
||||
|
||||
os.Setenv("THE_TEST", "11")
|
||||
_ = os.Setenv("THE_TEST", "11")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.toml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type tomlMap struct {
|
||||
@ -28,7 +28,7 @@ func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) {
|
||||
case reflect.String:
|
||||
ret[key] = val.(string)
|
||||
case reflect.Int:
|
||||
ret[key] = int(val.(int))
|
||||
ret[key] = val.(int)
|
||||
case reflect.Int8:
|
||||
ret[key] = int(val.(int8))
|
||||
case reflect.Int16:
|
||||
@ -50,7 +50,7 @@ func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) {
|
||||
case reflect.Float32:
|
||||
ret[key] = float64(val.(float32))
|
||||
case reflect.Float64:
|
||||
ret[key] = float64(val.(float64))
|
||||
ret[key] = val.(float64)
|
||||
case reflect.Map:
|
||||
if tmp, err := unmarshalMap(val); err == nil {
|
||||
ret[key] = tmp
|
||||
@ -66,9 +66,9 @@ func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *tomlMap) UnmarshalTOML(i interface{}) error {
|
||||
func (tm *tomlMap) UnmarshalTOML(i interface{}) error {
|
||||
if tmp, err := unmarshalMap(i); err == nil {
|
||||
self.Map = tmp
|
||||
tm.Map = tmp
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
@ -10,17 +10,16 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func TestCommandYamlFileTest(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -47,13 +46,13 @@ func TestCommandYamlFileTest(t *testing.T) {
|
||||
func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
os.Setenv("THE_TEST", "10")
|
||||
_ = os.Setenv("THE_TEST", "10")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -81,14 +80,14 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) {
|
||||
func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
_ = ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
test: 15`), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
os.Setenv("THE_TEST", "10")
|
||||
_ = os.Setenv("THE_TEST", "10")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -116,11 +115,11 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) {
|
||||
func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml", "--test", "7"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -148,12 +147,12 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) {
|
||||
func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
_ = ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
test: 15`), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml", "--top.test", "7"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -181,11 +180,11 @@ func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) {
|
||||
func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -213,12 +212,12 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) {
|
||||
func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
_ = ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
test: 15`), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -246,14 +245,14 @@ func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) {
|
||||
func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
_ = ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
os.Setenv("THE_TEST", "11")
|
||||
_ = os.Setenv("THE_TEST", "11")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
@ -280,15 +279,15 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T
|
||||
func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *testing.T) {
|
||||
app := &cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
_ = ioutil.WriteFile("current.yaml", []byte(`top:
|
||||
test: 15`), 0666)
|
||||
defer os.Remove("current.yaml")
|
||||
|
||||
os.Setenv("THE_TEST", "11")
|
||||
_ = os.Setenv("THE_TEST", "11")
|
||||
defer os.Setenv("THE_TEST", "")
|
||||
|
||||
test := []string{"test-cmd", "--load", "current.yaml"}
|
||||
set.Parse(test)
|
||||
_ = set.Parse(test)
|
||||
|
||||
c := cli.NewContext(app, set, nil)
|
||||
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/urfave/cli.v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
@ -86,7 +86,7 @@ func loadDataFrom(filePath string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath)
|
||||
}
|
||||
return ioutil.ReadFile(filePath)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to determine how to load from path %s", filePath)
|
||||
}
|
||||
|
195
app.go
195
app.go
@ -1,9 +1,9 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@ -11,7 +11,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// App is the main structure of a cli application.
|
||||
var (
|
||||
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
|
||||
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
|
||||
// unused variable. commented for now. will remove in future if agreed upon by everyone
|
||||
//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
|
||||
@ -31,8 +45,8 @@ type App struct {
|
||||
Commands []*Command
|
||||
// List of flags to parse
|
||||
Flags []Flag
|
||||
// Boolean to enable shell completion commands
|
||||
EnableShellCompletion bool
|
||||
// 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
|
||||
@ -40,7 +54,7 @@ type App struct {
|
||||
// Categories contains the categorized commands and is populated on app startup
|
||||
Categories CommandCategories
|
||||
// An action to execute when the shell completion flag is set
|
||||
ShellComplete ShellCompleteFunc
|
||||
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
|
||||
@ -63,6 +77,9 @@ type App struct {
|
||||
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.
|
||||
@ -71,6 +88,10 @@ type App struct {
|
||||
// cli.go uses text/template to render templates. You can
|
||||
// render custom help text by setting this variable.
|
||||
CustomAppHelpTemplate string
|
||||
// Boolean to enable short-option handling so user can combine several
|
||||
// single-character bool arguements into one
|
||||
// i.e. foobar -o -v -> foobar -ov
|
||||
UseShortOptionHandling bool
|
||||
|
||||
didSetup bool
|
||||
}
|
||||
@ -85,6 +106,22 @@ func compileTime() time.Time {
|
||||
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.
|
||||
@ -111,8 +148,8 @@ func (a *App) Setup() {
|
||||
a.Version = "0.0.0"
|
||||
}
|
||||
|
||||
if a.ShellComplete == nil {
|
||||
a.ShellComplete = DefaultAppComplete
|
||||
if a.BashComplete == nil {
|
||||
a.BashComplete = DefaultAppComplete
|
||||
}
|
||||
|
||||
if a.Action == nil {
|
||||
@ -127,14 +164,15 @@ func (a *App) Setup() {
|
||||
a.Writer = os.Stdout
|
||||
}
|
||||
|
||||
newCmds := []*Command{}
|
||||
var newCommands []*Command
|
||||
|
||||
for _, c := range a.Commands {
|
||||
if c.HelpName == "" {
|
||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
||||
}
|
||||
newCmds = append(newCmds, c)
|
||||
newCommands = append(newCommands, c)
|
||||
}
|
||||
a.Commands = newCmds
|
||||
a.Commands = newCommands
|
||||
|
||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
||||
a.appendCommand(helpCommand)
|
||||
@ -144,10 +182,10 @@ func (a *App) Setup() {
|
||||
}
|
||||
}
|
||||
|
||||
if a.EnableShellCompletion {
|
||||
a.appendFlag(GenerateCompletionFlag)
|
||||
a.appendFlag(InitCompletionFlag)
|
||||
}
|
||||
//if a.EnableShellCompletion {
|
||||
// a.appendFlag(GenerateCompletionFlag)
|
||||
// a.appendFlag(InitCompletionFlag)
|
||||
//}
|
||||
|
||||
if !a.HideVersion {
|
||||
a.appendFlag(VersionFlag)
|
||||
@ -168,6 +206,14 @@ func (a *App) Setup() {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) newFlagSet() (*flag.FlagSet, error) {
|
||||
return flagSet(a.Name, a.Flags)
|
||||
}
|
||||
|
||||
func (a *App) useShortOptionHandling() bool {
|
||||
return a.UseShortOptionHandling
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@ -181,19 +227,17 @@ func (a *App) Run(arguments []string) (err error) {
|
||||
// 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)
|
||||
_, err = a.newFlagSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.SetOutput(ioutil.Discard)
|
||||
err = set.Parse(arguments[1:])
|
||||
set, err := parseIter(a, arguments[1:])
|
||||
nerr := normalizeFlags(a.Flags, set)
|
||||
context := NewContext(a, set, nil)
|
||||
if nerr != nil {
|
||||
fmt.Fprintln(a.Writer, nerr)
|
||||
ShowAppHelp(context)
|
||||
_, _ = fmt.Fprintln(a.Writer, nerr)
|
||||
_ = ShowAppHelp(context)
|
||||
return nerr
|
||||
}
|
||||
context.shellComplete = shellComplete
|
||||
@ -212,17 +256,22 @@ func (a *App) Run(arguments []string) (err error) {
|
||||
|
||||
if err != nil {
|
||||
if a.OnUsageError != nil {
|
||||
//<<<<<<< HEAD
|
||||
err = a.OnUsageError(context, err, false)
|
||||
HandleExitCoder(err)
|
||||
//=======
|
||||
// err := a.OnUsageError(context, err, false)
|
||||
// a.handleExitCoder(context, err)
|
||||
//>>>>>>> master
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||
ShowAppHelp(context)
|
||||
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||
_ = ShowAppHelp(context)
|
||||
return err
|
||||
}
|
||||
|
||||
if !a.HideHelp && checkHelp(context) {
|
||||
ShowAppHelp(context)
|
||||
_ = ShowAppHelp(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -231,6 +280,12 @@ func (a *App) Run(arguments []string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
cerr := checkRequiredFlags(a.Flags, context)
|
||||
if cerr != nil {
|
||||
_ = ShowAppHelp(context)
|
||||
return cerr
|
||||
}
|
||||
|
||||
if a.After != nil {
|
||||
defer func() {
|
||||
if afterErr := a.After(context); afterErr != nil {
|
||||
@ -246,8 +301,9 @@ func (a *App) Run(arguments []string) (err error) {
|
||||
if a.Before != nil {
|
||||
beforeErr := a.Before(context)
|
||||
if beforeErr != nil {
|
||||
ShowAppHelp(context)
|
||||
HandleExitCoder(beforeErr)
|
||||
_, _ = fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
|
||||
_ = ShowAppHelp(context)
|
||||
a.handleExitCoder(context, beforeErr)
|
||||
err = beforeErr
|
||||
return err
|
||||
}
|
||||
@ -269,10 +325,22 @@ func (a *App) Run(arguments []string) (err error) {
|
||||
// Run default Action
|
||||
err = a.Action(context)
|
||||
|
||||
HandleExitCoder(err)
|
||||
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) {
|
||||
@ -298,29 +366,32 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||
}
|
||||
a.Commands = newCmds
|
||||
|
||||
// append flags
|
||||
if a.EnableShellCompletion {
|
||||
a.appendFlag(GenerateCompletionFlag)
|
||||
}
|
||||
|
||||
// parse flags
|
||||
set, err := flagSet(a.Name, a.Flags)
|
||||
//<<<<<<< HEAD
|
||||
// // append flags
|
||||
// if a.EnableShellCompletion {
|
||||
// a.appendFlag(GenerateCompletionFlag)
|
||||
// }
|
||||
//
|
||||
// // parse flags
|
||||
// set, err := flagSet(a.Name, a.Flags)
|
||||
//=======
|
||||
_, err = a.newFlagSet()
|
||||
//>>>>>>> master
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.SetOutput(ioutil.Discard)
|
||||
err = set.Parse(ctx.Args().Tail())
|
||||
set, err := parseIter(a, 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)
|
||||
_, _ = fmt.Fprintln(a.Writer, nerr)
|
||||
_, _ = fmt.Fprintln(a.Writer)
|
||||
if len(a.Commands) > 0 {
|
||||
ShowSubcommandHelp(context)
|
||||
_ = ShowSubcommandHelp(context)
|
||||
} else {
|
||||
ShowCommandHelp(ctx, context.Args().First())
|
||||
_ = ShowCommandHelp(ctx, context.Args().First())
|
||||
}
|
||||
return nerr
|
||||
}
|
||||
@ -332,11 +403,11 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||
if err != nil {
|
||||
if a.OnUsageError != nil {
|
||||
err = a.OnUsageError(context, err, true)
|
||||
HandleExitCoder(err)
|
||||
a.handleExitCoder(context, err)
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||
ShowSubcommandHelp(context)
|
||||
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||
_ = ShowSubcommandHelp(context)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -350,11 +421,17 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
cerr := checkRequiredFlags(a.Flags, context)
|
||||
if cerr != nil {
|
||||
_ = ShowSubcommandHelp(context)
|
||||
return cerr
|
||||
}
|
||||
|
||||
if a.After != nil {
|
||||
defer func() {
|
||||
afterErr := a.After(context)
|
||||
if afterErr != nil {
|
||||
HandleExitCoder(err)
|
||||
a.handleExitCoder(context, err)
|
||||
if err != nil {
|
||||
err = newMultiError(err, afterErr)
|
||||
} else {
|
||||
@ -367,7 +444,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||
if a.Before != nil {
|
||||
beforeErr := a.Before(context)
|
||||
if beforeErr != nil {
|
||||
HandleExitCoder(beforeErr)
|
||||
a.handleExitCoder(context, beforeErr)
|
||||
err = beforeErr
|
||||
return err
|
||||
}
|
||||
@ -385,7 +462,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
||||
// Run default Action
|
||||
err = a.Action(context)
|
||||
|
||||
HandleExitCoder(err)
|
||||
a.handleExitCoder(context, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -419,7 +496,7 @@ func (a *App) VisibleCategories() []CommandCategory {
|
||||
|
||||
// VisibleCommands returns a slice of the Commands with Hidden=false
|
||||
func (a *App) VisibleCommands() []*Command {
|
||||
ret := []*Command{}
|
||||
var ret []*Command
|
||||
for _, command := range a.Commands {
|
||||
if !command.Hidden {
|
||||
ret = append(ret, command)
|
||||
@ -444,7 +521,6 @@ func (a *App) hasFlag(flag Flag) bool {
|
||||
}
|
||||
|
||||
func (a *App) errWriter() io.Writer {
|
||||
|
||||
// When the app ErrWriter is nil use the package level one.
|
||||
if a.ErrWriter == nil {
|
||||
return ErrWriter
|
||||
@ -465,6 +541,14 @@ func (a *App) appendCommand(c *Command) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -488,3 +572,20 @@ func DefaultCommand(name string) ActionFunc {
|
||||
return ctx.App.Command(name).Run(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
switch a := action.(type) {
|
||||
case ActionFunc:
|
||||
return a(context)
|
||||
case func(*Context) error:
|
||||
return a(context)
|
||||
case func(*Context): // deprecated function signature
|
||||
a(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errInvalidActionType
|
||||
}
|
||||
|
598
app_test.go
598
app_test.go
@ -86,7 +86,7 @@ func ExampleApp_Run_subcommand() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// Hello, Jeremy
|
||||
}
|
||||
@ -119,7 +119,7 @@ func ExampleApp_Run_appHelp() {
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Run(os.Args)
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// NAME:
|
||||
// greet - A new cli application
|
||||
@ -138,8 +138,8 @@ func ExampleApp_Run_appHelp() {
|
||||
// Oliver Allen <oliver@toyshop.com>
|
||||
//
|
||||
// COMMANDS:
|
||||
// describeit, d use it to see a description
|
||||
// help, h Shows a list of commands or help for one command
|
||||
// describeit, d use it to see a description
|
||||
// help, h Shows a list of commands or help for one command
|
||||
//
|
||||
// GLOBAL OPTIONS:
|
||||
// --name value a name to say (default: "bob")
|
||||
@ -169,7 +169,7 @@ func ExampleApp_Run_commandHelp() {
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Run(os.Args)
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// NAME:
|
||||
// greet describeit - use it to see a description
|
||||
@ -184,7 +184,7 @@ func ExampleApp_Run_commandHelp() {
|
||||
func ExampleApp_Run_noAction() {
|
||||
app := App{}
|
||||
app.Name = "greet"
|
||||
app.Run([]string{"greet"})
|
||||
_ = app.Run([]string{"greet"})
|
||||
// Output:
|
||||
// NAME:
|
||||
// greet - A new cli application
|
||||
@ -196,7 +196,7 @@ func ExampleApp_Run_noAction() {
|
||||
// 0.0.0
|
||||
//
|
||||
// COMMANDS:
|
||||
// help, h Shows a list of commands or help for one command
|
||||
// help, h Shows a list of commands or help for one command
|
||||
//
|
||||
// GLOBAL OPTIONS:
|
||||
// --help, -h show help (default: false)
|
||||
@ -215,7 +215,7 @@ func ExampleApp_Run_subcommandNoAction() {
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Run([]string{"greet", "describeit"})
|
||||
_ = app.Run([]string{"greet", "describeit"})
|
||||
// Output:
|
||||
// NAME:
|
||||
// greet describeit - use it to see a description
|
||||
@ -231,13 +231,103 @@ func ExampleApp_Run_subcommandNoAction() {
|
||||
|
||||
}
|
||||
|
||||
func ExampleApp_Run_shellComplete() {
|
||||
func ExampleApp_Run_bashComplete_withShortFlag() {
|
||||
os.Args = []string{"greet", "-", "--generate-bash-completion"}
|
||||
|
||||
app := NewApp()
|
||||
app.Name = "greet"
|
||||
app.EnableBashCompletion = true
|
||||
app.Flags = []Flag{
|
||||
&IntFlag{
|
||||
Name: "other",
|
||||
Aliases: []string{"o"},
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "xyz",
|
||||
Aliases: []string{"x"},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// --other
|
||||
// -o
|
||||
// --xyz
|
||||
// -x
|
||||
// --help
|
||||
// -h
|
||||
// --version
|
||||
// -v
|
||||
}
|
||||
|
||||
func ExampleApp_Run_bashComplete_withLongFlag() {
|
||||
os.Args = []string{"greet", "--s", "--generate-bash-completion"}
|
||||
|
||||
app := NewApp()
|
||||
app.Name = "greet"
|
||||
app.EnableBashCompletion = true
|
||||
app.Flags = []Flag{
|
||||
&IntFlag{
|
||||
Name: "other",
|
||||
Aliases: []string{"o"},
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "xyz",
|
||||
Aliases: []string{"x"},
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "some-flag,s",
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "similar-flag",
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// --some-flag
|
||||
// --similar-flag
|
||||
}
|
||||
func ExampleApp_Run_bashComplete_withMultipleLongFlag() {
|
||||
os.Args = []string{"greet", "--st", "--generate-bash-completion"}
|
||||
|
||||
app := NewApp()
|
||||
app.Name = "greet"
|
||||
app.EnableBashCompletion = true
|
||||
app.Flags = []Flag{
|
||||
&IntFlag{
|
||||
Name: "int-flag",
|
||||
Aliases: []string{"i"},
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "string",
|
||||
Aliases: []string{"s"},
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "string-flag-2",
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "similar-flag",
|
||||
},
|
||||
&StringFlag{
|
||||
Name: "some-flag",
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// --string
|
||||
// --string-flag-2
|
||||
}
|
||||
|
||||
func ExampleApp_Run_bashComplete() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", fmt.Sprintf("--%s", genCompName())}
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "--generate-bash-completion"}
|
||||
|
||||
app := &App{
|
||||
Name: "greet",
|
||||
EnableShellCompletion: true,
|
||||
EnableBashCompletion: true,
|
||||
Commands: []*Command{
|
||||
{
|
||||
Name: "describeit",
|
||||
@ -260,7 +350,7 @@ func ExampleApp_Run_shellComplete() {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// describeit
|
||||
// d
|
||||
@ -269,6 +359,44 @@ func ExampleApp_Run_shellComplete() {
|
||||
// h
|
||||
}
|
||||
|
||||
func ExampleApp_Run_zshComplete() {
|
||||
// set args for examples sake
|
||||
os.Args = []string{"greet", "--generate-bash-completion"}
|
||||
_ = os.Setenv("_CLI_ZSH_AUTOCOMPLETE_HACK", "1")
|
||||
|
||||
app := NewApp()
|
||||
app.Name = "greet"
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []*Command{
|
||||
{
|
||||
Name: "describeit",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "use it to see a description",
|
||||
Description: "This is how we describe describeit the function",
|
||||
Action: func(c *Context) error {
|
||||
fmt.Printf("i like to describe things")
|
||||
return nil
|
||||
},
|
||||
}, {
|
||||
Name: "next",
|
||||
Usage: "next example",
|
||||
Description: "more stuff to see when generating bash completion",
|
||||
Action: func(c *Context) error {
|
||||
fmt.Printf("the next example")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_ = app.Run(os.Args)
|
||||
// Output:
|
||||
// describeit:use it to see a description
|
||||
// d:use it to see a description
|
||||
// next:next example
|
||||
// help:Shows a list of commands or help for one command
|
||||
// h:Shows a list of commands or help for one command
|
||||
}
|
||||
|
||||
func TestApp_Run(t *testing.T) {
|
||||
s := ""
|
||||
|
||||
@ -317,6 +445,63 @@ func TestApp_Setup_defaultsWriter(t *testing.T) {
|
||||
expect(t, app.Writer, os.Stdout)
|
||||
}
|
||||
|
||||
|
||||
func TestApp_CommandWithArgBeforeFlags(t *testing.T) {
|
||||
var parsedOption, firstArg string
|
||||
|
||||
app := NewApp()
|
||||
command := &Command{
|
||||
Name: "cmd",
|
||||
Flags: []Flag{
|
||||
&StringFlag{Name: "option", Value: "", Usage: "some option"},
|
||||
},
|
||||
Action: func(c *Context) error {
|
||||
parsedOption = c.String("option")
|
||||
firstArg = c.Args().First()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
app.Commands = []*Command{command}
|
||||
|
||||
_ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"})
|
||||
|
||||
expect(t, parsedOption, "my-option")
|
||||
expect(t, firstArg, "my-arg")
|
||||
}
|
||||
|
||||
func TestApp_CommandWithArgBeforeBoolFlags(t *testing.T) {
|
||||
var parsedOption, parsedSecondOption, firstArg string
|
||||
var parsedBool, parsedSecondBool bool
|
||||
|
||||
app := NewApp()
|
||||
command := &Command{
|
||||
Name: "cmd",
|
||||
Flags: []Flag{
|
||||
&StringFlag{Name: "option", Value: "", Usage: "some option"},
|
||||
&StringFlag{Name: "secondOption", Value: "", Usage: "another option"},
|
||||
&BoolFlag{Name: "boolflag", Usage: "some bool"},
|
||||
&BoolFlag{Name: "b", Usage: "another bool"},
|
||||
},
|
||||
Action: func(c *Context) error {
|
||||
parsedOption = c.String("option")
|
||||
parsedSecondOption = c.String("secondOption")
|
||||
parsedBool = c.Bool("boolflag")
|
||||
parsedSecondBool = c.Bool("b")
|
||||
firstArg = c.Args().First()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
app.Commands = []*Command{command}
|
||||
|
||||
_ = app.Run([]string{"", "cmd", "my-arg", "--boolflag", "--option", "my-option", "-b", "--secondOption", "fancy-option"})
|
||||
|
||||
expect(t, parsedOption, "my-option")
|
||||
expect(t, parsedSecondOption, "fancy-option")
|
||||
expect(t, parsedBool, true)
|
||||
expect(t, parsedSecondBool, true)
|
||||
expect(t, firstArg, "my-arg")
|
||||
}
|
||||
|
||||
func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
|
||||
var context *Context
|
||||
|
||||
@ -339,7 +524,7 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})
|
||||
_ = a.Run([]string{"", "foo", "--lang", "spanish", "abcd"})
|
||||
|
||||
expect(t, context.Args().Get(0), "abcd")
|
||||
expect(t, context.String("lang"), "spanish")
|
||||
@ -355,7 +540,7 @@ func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) {
|
||||
}
|
||||
|
||||
set := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
set.Parse([]string{"", "---foo"})
|
||||
_ = set.Parse([]string{"", "---foo"})
|
||||
c := &Context{flagSet: set}
|
||||
|
||||
err := a.RunAsSubcommand(c)
|
||||
@ -383,7 +568,7 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"", "cmd", "--option", "my-option", "my-arg", "--", "--notARealFlag"})
|
||||
_ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"})
|
||||
|
||||
expect(t, parsedOption, "my-option")
|
||||
expect(t, args.Get(0), "my-arg")
|
||||
@ -406,7 +591,7 @@ func TestApp_CommandWithDash(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "-"})
|
||||
_ = app.Run([]string{"", "cmd", "my-arg", "-"})
|
||||
|
||||
expect(t, args.Get(0), "my-arg")
|
||||
expect(t, args.Get(1), "-")
|
||||
@ -427,7 +612,7 @@ func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
|
||||
_ = app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"})
|
||||
|
||||
expect(t, args.Get(0), "my-arg")
|
||||
expect(t, args.Get(1), "--")
|
||||
@ -485,6 +670,93 @@ func TestApp_VisibleCommands(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp_UseShortOptionHandling(t *testing.T) {
|
||||
var one, two bool
|
||||
var name string
|
||||
expected := "expectedName"
|
||||
|
||||
app := NewApp()
|
||||
app.UseShortOptionHandling = true
|
||||
app.Flags = []Flag{
|
||||
&BoolFlag{Name: "one", Aliases: []string{"o"}},
|
||||
&BoolFlag{Name: "two", Aliases: []string{"t"}},
|
||||
&StringFlag{Name: "name", Aliases: []string{"n"}},
|
||||
}
|
||||
app.Action = func(c *Context) error {
|
||||
one = c.Bool("one")
|
||||
two = c.Bool("two")
|
||||
name = c.String("name")
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = app.Run([]string{"", "-on", expected})
|
||||
expect(t, one, true)
|
||||
expect(t, two, false)
|
||||
expect(t, name, expected)
|
||||
}
|
||||
|
||||
func TestApp_UseShortOptionHandlingCommand(t *testing.T) {
|
||||
var one, two bool
|
||||
var name string
|
||||
expected := "expectedName"
|
||||
|
||||
app := NewApp()
|
||||
app.UseShortOptionHandling = true
|
||||
command := &Command{
|
||||
Name: "cmd",
|
||||
Flags: []Flag{
|
||||
&BoolFlag{Name: "one", Aliases: []string{"o"}},
|
||||
&BoolFlag{Name: "two", Aliases: []string{"t"}},
|
||||
&StringFlag{Name: "name", Aliases: []string{"n"}},
|
||||
},
|
||||
Action: func(c *Context) error {
|
||||
one = c.Bool("one")
|
||||
two = c.Bool("two")
|
||||
name = c.String("name")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
app.Commands = []*Command{command}
|
||||
|
||||
_ = app.Run([]string{"", "cmd", "-on", expected})
|
||||
expect(t, one, true)
|
||||
expect(t, two, false)
|
||||
expect(t, name, expected)
|
||||
}
|
||||
|
||||
func TestApp_UseShortOptionHandlingSubCommand(t *testing.T) {
|
||||
var one, two bool
|
||||
var name string
|
||||
expected := "expectedName"
|
||||
|
||||
app := NewApp()
|
||||
app.UseShortOptionHandling = true
|
||||
command := &Command{
|
||||
Name: "cmd",
|
||||
}
|
||||
subCommand := &Command{
|
||||
Name: "sub",
|
||||
Flags: []Flag{
|
||||
&BoolFlag{Name: "one", Aliases: []string{"o"}},
|
||||
&BoolFlag{Name: "two", Aliases: []string{"t"}},
|
||||
&StringFlag{Name: "name", Aliases: []string{"n"}},
|
||||
},
|
||||
Action: func(c *Context) error {
|
||||
one = c.Bool("one")
|
||||
two = c.Bool("two")
|
||||
name = c.String("name")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
app.Commands = []*Command{command, subCommand}
|
||||
|
||||
err := app.Run([]string{"", "cmd", "sub", "-on", expected})
|
||||
expect(t, err, nil)
|
||||
expect(t, one, true)
|
||||
expect(t, two, false)
|
||||
expect(t, name, expected)
|
||||
}
|
||||
|
||||
func TestApp_Float64Flag(t *testing.T) {
|
||||
var meters float64
|
||||
|
||||
@ -498,7 +770,7 @@ func TestApp_Float64Flag(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"", "--height", "1.93"})
|
||||
_ = app.Run([]string{"", "--height", "1.93"})
|
||||
expect(t, meters, 1.93)
|
||||
}
|
||||
|
||||
@ -528,7 +800,7 @@ func TestApp_ParseSliceFlags(t *testing.T) {
|
||||
var _ = parsedOption
|
||||
var _ = firstArg
|
||||
|
||||
app.Run([]string{"", "cmd", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4", "my-arg"})
|
||||
_ = app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"})
|
||||
|
||||
IntsEquals := func(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
@ -586,7 +858,7 @@ func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"", "cmd", "-a", "2", "-str", "A", "my-arg"})
|
||||
_ = app.Run([]string{"", "cmd", "my-arg", "-a", "2", "-str", "A"})
|
||||
|
||||
var expectedIntSlice = []int{2}
|
||||
var expectedStringSlice = []string{"A"}
|
||||
@ -818,6 +1090,145 @@ func TestAppNoHelpFlag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiredFlagAppRunBehavior(t *testing.T) {
|
||||
tdata := []struct {
|
||||
testCase string
|
||||
appFlags []Flag
|
||||
appRunInput []string
|
||||
appCommands []*Command
|
||||
expectedAnError bool
|
||||
}{
|
||||
// assertion: empty input, when a required flag is present, errors
|
||||
{
|
||||
testCase: "error_case_empty_input_with_required_flag_on_app",
|
||||
appRunInput: []string{"myCLI"},
|
||||
appFlags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "error_case_empty_input_with_required_flag_on_command",
|
||||
appRunInput: []string{"myCLI", "myCommand"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
}},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "error_case_empty_input_with_required_flag_on_subcommand",
|
||||
appRunInput: []string{"myCLI", "myCommand", "mySubCommand"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Subcommands: []*Command{{
|
||||
Name: "mySubCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
}},
|
||||
}},
|
||||
expectedAnError: true,
|
||||
},
|
||||
// assertion: inputing --help, when a required flag is present, does not error
|
||||
{
|
||||
testCase: "valid_case_help_input_with_required_flag_on_app",
|
||||
appRunInput: []string{"myCLI", "--help"},
|
||||
appFlags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
},
|
||||
{
|
||||
testCase: "valid_case_help_input_with_required_flag_on_command",
|
||||
appRunInput: []string{"myCLI", "myCommand", "--help"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
}},
|
||||
},
|
||||
{
|
||||
testCase: "valid_case_help_input_with_required_flag_on_subcommand",
|
||||
appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--help"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Subcommands: []*Command{{
|
||||
Name: "mySubCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
// assertion: giving optional input, when a required flag is present, errors
|
||||
{
|
||||
testCase: "error_case_optional_input_with_required_flag_on_app",
|
||||
appRunInput: []string{"myCLI", "--optional", "cats"},
|
||||
appFlags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}, &StringFlag{Name: "optional"}},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "error_case_optional_input_with_required_flag_on_command",
|
||||
appRunInput: []string{"myCLI", "myCommand", "--optional", "cats"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}, &StringFlag{Name: "optional"}},
|
||||
}},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "error_case_optional_input_with_required_flag_on_subcommand",
|
||||
appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--optional", "cats"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Subcommands: []*Command{{
|
||||
Name: "mySubCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}, &StringFlag{Name: "optional"}},
|
||||
}},
|
||||
}},
|
||||
expectedAnError: true,
|
||||
},
|
||||
// assertion: when a required flag is present, inputting that required flag does not error
|
||||
{
|
||||
testCase: "valid_case_required_flag_input_on_app",
|
||||
appRunInput: []string{"myCLI", "--requiredFlag", "cats"},
|
||||
appFlags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
},
|
||||
{
|
||||
testCase: "valid_case_required_flag_input_on_command",
|
||||
appRunInput: []string{"myCLI", "myCommand", "--requiredFlag", "cats"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
}},
|
||||
},
|
||||
{
|
||||
testCase: "valid_case_required_flag_input_on_subcommand",
|
||||
appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--requiredFlag", "cats"},
|
||||
appCommands: []*Command{{
|
||||
Name: "myCommand",
|
||||
Subcommands: []*Command{{
|
||||
Name: "mySubCommand",
|
||||
Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
for _, test := range tdata {
|
||||
t.Run(test.testCase, func(t *testing.T) {
|
||||
// setup
|
||||
app := NewApp()
|
||||
app.Flags = test.appFlags
|
||||
app.Commands = test.appCommands
|
||||
|
||||
// logic under test
|
||||
err := app.Run(test.appRunInput)
|
||||
|
||||
// assertions
|
||||
if test.expectedAnError && err == nil {
|
||||
t.Errorf("expected an error, but there was none")
|
||||
}
|
||||
if _, ok := err.(requiredFlagsErr); test.expectedAnError && !ok {
|
||||
t.Errorf("expected a requiredFlagsErr, but got: %s", err)
|
||||
}
|
||||
if !test.expectedAnError && err != nil {
|
||||
t.Errorf("did not expected an error, but there was one: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppHelpPrinter(t *testing.T) {
|
||||
oldPrinter := HelpPrinter
|
||||
defer func() {
|
||||
@ -830,7 +1241,7 @@ func TestAppHelpPrinter(t *testing.T) {
|
||||
}
|
||||
|
||||
app := &App{}
|
||||
app.Run([]string{"-h"})
|
||||
_ = app.Run([]string{"-h"})
|
||||
|
||||
if wasCalled == false {
|
||||
t.Errorf("Help printer expected to be called, but was not")
|
||||
@ -876,7 +1287,7 @@ func TestApp_CommandNotFound(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
app.Run([]string{"command", "foo"})
|
||||
_ = app.Run([]string{"command", "foo"})
|
||||
|
||||
expect(t, counts.CommandNotFound, 1)
|
||||
expect(t, counts.SubCommand, 0)
|
||||
@ -889,9 +1300,9 @@ func TestApp_OrderOfOperations(t *testing.T) {
|
||||
resetCounts := func() { counts = &opCounts{} }
|
||||
|
||||
app := &App{
|
||||
EnableShellCompletion: true,
|
||||
ShellComplete: func(c *Context) {
|
||||
fmt.Fprintf(os.Stderr, "---> ShellComplete(%#v)\n", c)
|
||||
EnableBashCompletion: true,
|
||||
BashComplete: func(c *Context) {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "---> BashComplete(%#v)\n", c)
|
||||
counts.Total++
|
||||
counts.ShellComplete = counts.Total
|
||||
},
|
||||
@ -956,7 +1367,7 @@ func TestApp_OrderOfOperations(t *testing.T) {
|
||||
|
||||
resetCounts()
|
||||
|
||||
_ = app.Run([]string{"command", fmt.Sprintf("--%s", genCompName())})
|
||||
_ = app.Run([]string{"command", fmt.Sprintf("--%s", "--generate-bash-completion")})
|
||||
expect(t, counts.ShellComplete, 1)
|
||||
expect(t, counts.Total, 1)
|
||||
|
||||
@ -1306,7 +1717,7 @@ func TestApp_Run_Categories(t *testing.T) {
|
||||
Writer: buf,
|
||||
}
|
||||
|
||||
app.Run([]string{"categories"})
|
||||
_ = app.Run([]string{"categories"})
|
||||
|
||||
expect := commandCategories([]*commandCategory{
|
||||
{
|
||||
@ -1569,6 +1980,18 @@ func (c *customBoolFlag) Names() []string {
|
||||
return []string{c.Nombre}
|
||||
}
|
||||
|
||||
func (c *customBoolFlag) TakesValue() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *customBoolFlag) GetValue() string {
|
||||
return "value"
|
||||
}
|
||||
|
||||
func (c *customBoolFlag) GetUsage() string {
|
||||
return "usage"
|
||||
}
|
||||
|
||||
func (c *customBoolFlag) Apply(set *flag.FlagSet) {
|
||||
set.String(c.Nombre, c.Nombre, "")
|
||||
}
|
||||
@ -1613,6 +2036,59 @@ func TestCustomHelpVersionFlags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleExitCoder_Default(t *testing.T) {
|
||||
app := NewApp()
|
||||
fs, err := flagSet(app.Name, app.Flags)
|
||||
if err != nil {
|
||||
t.Errorf("error creating FlagSet: %s", err)
|
||||
}
|
||||
|
||||
ctx := NewContext(app, fs, nil)
|
||||
app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42))
|
||||
|
||||
output := fakeErrWriter.String()
|
||||
if !strings.Contains(output, "Default") {
|
||||
t.Fatalf("Expected Default Behavior from Error Handler but got: %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleExitCoder_Custom(t *testing.T) {
|
||||
app := NewApp()
|
||||
fs, err := flagSet(app.Name, app.Flags)
|
||||
if err != nil {
|
||||
t.Errorf("error creating FlagSet: %s", err)
|
||||
}
|
||||
|
||||
app.ExitErrHandler = func(_ *Context, _ error) {
|
||||
_, _ = fmt.Fprintln(ErrWriter, "I'm a Custom error handler, I print what I want!")
|
||||
}
|
||||
|
||||
ctx := NewContext(app, fs, nil)
|
||||
app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42))
|
||||
|
||||
output := fakeErrWriter.String()
|
||||
if !strings.Contains(output, "Custom") {
|
||||
t.Fatalf("Expected Custom Behavior from Error Handler but got: %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleAction_WithUnknownPanic(t *testing.T) {
|
||||
defer func() { refute(t, recover(), nil) }()
|
||||
|
||||
var fn ActionFunc
|
||||
|
||||
app := NewApp()
|
||||
app.Action = func(ctx *Context) error {
|
||||
_ = fn(ctx)
|
||||
return nil
|
||||
}
|
||||
fs, err := flagSet(app.Name, app.Flags)
|
||||
if err != nil {
|
||||
t.Errorf("error creating FlagSet: %s", err)
|
||||
}
|
||||
_ = HandleAction(app.Action, NewContext(app, fs, nil))
|
||||
}
|
||||
|
||||
func TestShellCompletionForIncompleteFlags(t *testing.T) {
|
||||
app := &App{
|
||||
Flags: []Flag{
|
||||
@ -1620,30 +2096,30 @@ func TestShellCompletionForIncompleteFlags(t *testing.T) {
|
||||
Name: "test-completion",
|
||||
},
|
||||
},
|
||||
EnableShellCompletion: true,
|
||||
ShellComplete: func(ctx *Context) {
|
||||
EnableBashCompletion: true,
|
||||
BashComplete: func(ctx *Context) {
|
||||
for _, command := range ctx.App.Commands {
|
||||
if command.Hidden {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, name := range command.Names() {
|
||||
fmt.Fprintln(ctx.App.Writer, name)
|
||||
_, _ = fmt.Fprintln(ctx.App.Writer, name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, flag := range ctx.App.Flags {
|
||||
for _, name := range flag.Names() {
|
||||
if name == genCompName() {
|
||||
if name == BashCompletionFlag.Names()[0] {
|
||||
continue
|
||||
}
|
||||
|
||||
switch name = strings.TrimSpace(name); len(name) {
|
||||
case 0:
|
||||
case 1:
|
||||
fmt.Fprintln(ctx.App.Writer, "-"+name)
|
||||
_, _ = fmt.Fprintln(ctx.App.Writer, "-"+name)
|
||||
default:
|
||||
fmt.Fprintln(ctx.App.Writer, "--"+name)
|
||||
_, _ = fmt.Fprintln(ctx.App.Writer, "--"+name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1652,8 +2128,62 @@ func TestShellCompletionForIncompleteFlags(t *testing.T) {
|
||||
return fmt.Errorf("should not get here")
|
||||
},
|
||||
}
|
||||
err := app.Run([]string{"", "--test-completion", "--" + genCompName()})
|
||||
err := app.Run([]string{"", "--test-completion", "--" + "generate-bash-completion"})
|
||||
if err != nil {
|
||||
t.Errorf("app should not return an error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhenExitSubCommandWithCodeThenAppQuitUnexpectedly(t *testing.T) {
|
||||
testCode := 104
|
||||
|
||||
app := NewApp()
|
||||
app.Commands = []*Command{
|
||||
{
|
||||
Name: "cmd",
|
||||
Subcommands: []*Command{
|
||||
{
|
||||
Name: "subcmd",
|
||||
Action: func(c *Context) error {
|
||||
return NewExitError("exit error", testCode)
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// set user function as ExitErrHandler
|
||||
var exitCodeFromExitErrHandler int
|
||||
app.ExitErrHandler = func(c *Context, err error) {
|
||||
if exitErr, ok := err.(ExitCoder); ok {
|
||||
t.Log(exitErr)
|
||||
exitCodeFromExitErrHandler = exitErr.ExitCode()
|
||||
}
|
||||
}
|
||||
|
||||
// keep and restore original OsExiter
|
||||
origExiter := OsExiter
|
||||
defer func() {
|
||||
OsExiter = origExiter
|
||||
}()
|
||||
|
||||
// set user function as OsExiter
|
||||
var exitCodeFromOsExiter int
|
||||
OsExiter = func(exitCode int) {
|
||||
exitCodeFromOsExiter = exitCode
|
||||
}
|
||||
|
||||
_ = app.Run([]string{
|
||||
"myapp",
|
||||
"cmd",
|
||||
"subcmd",
|
||||
})
|
||||
|
||||
if exitCodeFromOsExiter != 0 {
|
||||
t.Errorf("exitCodeFromOsExiter should not change, but its value is %v", exitCodeFromOsExiter)
|
||||
}
|
||||
|
||||
if exitCodeFromExitErrHandler != testCode {
|
||||
t.Errorf("exitCodeFromOsExiter valeu should be %v, but its value is %v", testCode, exitCodeFromExitErrHandler)
|
||||
}
|
||||
}
|
||||
|
29
appveyor.yml
29
appveyor.yml
@ -11,26 +11,17 @@ cache:
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
GOVERSION: 1.8.x
|
||||
PYTHON: C:\Python36-x64
|
||||
PYTHON_VERSION: 3.6.x
|
||||
PYTHON_ARCH: 64
|
||||
GOVERSION: 1.11.x
|
||||
|
||||
install:
|
||||
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
|
||||
- go version
|
||||
- go env
|
||||
- go get github.com/urfave/gfmrun/...
|
||||
- rmdir c:\gopath\src\gopkg.in\urfave\cli.v2 /s /q
|
||||
- rmdir c:\gopath\pkg /s /q
|
||||
- git clone . c:\gopath\src\gopkg.in\urfave\cli.v2
|
||||
- go get -v -t ./...
|
||||
- if not exist node_modules\.bin\markdown-toc npm install markdown-toc
|
||||
- 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
|
||||
- python cli-v1-to-v2 --selftest
|
||||
- python runtests migrations
|
||||
- python runtests toc
|
||||
- go run build.go vet
|
||||
- go run build.go test
|
||||
- go run build.go gfmrun
|
||||
|
||||
|
@ -3,14 +3,19 @@
|
||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
||||
|
||||
_cli_bash_autocomplete() {
|
||||
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
|
||||
local cur opts base
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||
if [[ "$cur" == "-"* ]]; then
|
||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
|
||||
else
|
||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||
fi
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
complete -F _cli_bash_autocomplete $PROG
|
||||
|
||||
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG
|
||||
unset PROG
|
||||
|
@ -1,5 +1,11 @@
|
||||
autoload -U compinit && compinit
|
||||
autoload -U bashcompinit && bashcompinit
|
||||
_cli_zsh_autocomplete() {
|
||||
|
||||
script_dir=$(dirname $0)
|
||||
source ${script_dir}/bash_autocomplete
|
||||
local -a opts
|
||||
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}")
|
||||
|
||||
_describe 'values' opts
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
compdef _cli_zsh_autocomplete $PROG
|
||||
|
164
build.go
Normal file
164
build.go
Normal file
@ -0,0 +1,164 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var packages = []string{"cli", "altsrc"}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Name = "builder"
|
||||
app.Usage = "Generates a new urfave/cli build!"
|
||||
|
||||
app.Commands = cli.Commands{
|
||||
cli.Command{
|
||||
Name: "vet",
|
||||
Action: VetActionFunc,
|
||||
},
|
||||
cli.Command{
|
||||
Name: "test",
|
||||
Action: TestActionFunc,
|
||||
},
|
||||
cli.Command{
|
||||
Name: "gfmrun",
|
||||
Action: GfmrunActionFunc,
|
||||
},
|
||||
cli.Command{
|
||||
Name: "toc",
|
||||
Action: TocActionFunc,
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runCmd(arg string, args ...string) error {
|
||||
cmd := exec.Command(arg, args...)
|
||||
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func VetActionFunc(_ *cli.Context) error {
|
||||
return runCmd("go", "vet")
|
||||
}
|
||||
|
||||
func TestActionFunc(c *cli.Context) error {
|
||||
for _, pkg := range packages {
|
||||
var packageName string
|
||||
|
||||
if pkg == "cli" {
|
||||
packageName = "github.com/urfave/cli"
|
||||
} else {
|
||||
packageName = fmt.Sprintf("github.com/urfave/cli/%s", pkg)
|
||||
}
|
||||
|
||||
coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg)
|
||||
|
||||
err := runCmd("go", "test", "-v", coverProfile, packageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return testCleanup()
|
||||
}
|
||||
|
||||
func testCleanup() error {
|
||||
var out bytes.Buffer
|
||||
|
||||
for _, pkg := range packages {
|
||||
file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.Write(b)
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
outFile, err := os.Create("coverage.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = out.WriteTo(outFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = outFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GfmrunActionFunc(_ *cli.Context) error {
|
||||
file, err := os.Open("README.md")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var counter int
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
if strings.Contains(scanner.Text(), "package main") {
|
||||
counter++
|
||||
}
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md")
|
||||
}
|
||||
|
||||
func TocActionFunc(_ *cli.Context) error {
|
||||
err := runCmd("node_modules/.bin/markdown-toc", "-i", "README.md")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = runCmd("git", "diff", "--exit-code")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -15,7 +15,7 @@ func newCommandCategories() CommandCategories {
|
||||
}
|
||||
|
||||
func (c *commandCategories) Less(i, j int) bool {
|
||||
return (*c)[i].Name() < (*c)[j].Name()
|
||||
return lexicographicLess((*c)[i].Name(), (*c)[j].Name() )
|
||||
}
|
||||
|
||||
func (c *commandCategories) Len() int {
|
||||
@ -35,7 +35,7 @@ func (c *commandCategories) AddCommand(category string, command *Command) {
|
||||
}
|
||||
newVal := commandCategories(append(*c,
|
||||
&commandCategory{name: category, commands: []*Command{command}}))
|
||||
(*c) = newVal
|
||||
*c = newVal
|
||||
}
|
||||
|
||||
func (c *commandCategories) Categories() []CommandCategory {
|
||||
@ -75,7 +75,7 @@ func (c *commandCategory) VisibleCommands() []*Command {
|
||||
c.commands = []*Command{}
|
||||
}
|
||||
|
||||
ret := []*Command{}
|
||||
var ret []*Command
|
||||
for _, command := range c.commands {
|
||||
if !command.Hidden {
|
||||
ret = append(ret, command)
|
||||
|
2
cli.go
2
cli.go
@ -20,4 +20,4 @@
|
||||
// }
|
||||
package cli
|
||||
|
||||
//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
|
||||
//go:generate go run flag-gen/main.go flag-gen/assets_vfsdata.go
|
||||
|
52
command.go
52
command.go
@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
@ -23,8 +24,8 @@ type Command struct {
|
||||
ArgsUsage string
|
||||
// The category the command is part of
|
||||
Category string
|
||||
// The function to call when checking for shell command completions
|
||||
ShellComplete ShellCompleteFunc
|
||||
// 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
|
||||
@ -45,6 +46,10 @@ type Command struct {
|
||||
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 arguments into one
|
||||
// i.e. foobar -o -v -> foobar -ov
|
||||
UseShortOptionHandling bool
|
||||
|
||||
// Full name of command for help, defaults to full command name, including parent commands.
|
||||
HelpName string
|
||||
@ -63,7 +68,7 @@ func (c CommandsByName) Len() int {
|
||||
}
|
||||
|
||||
func (c CommandsByName) Less(i, j int) bool {
|
||||
return c[i].Name < c[j].Name
|
||||
return lexicographicLess(c[i].Name, c[j].Name)
|
||||
}
|
||||
|
||||
func (c CommandsByName) Swap(i, j int) {
|
||||
@ -90,10 +95,14 @@ func (c *Command) Run(ctx *Context) (err error) {
|
||||
c.appendFlag(HelpFlag)
|
||||
}
|
||||
|
||||
if ctx.App.EnableShellCompletion {
|
||||
c.appendFlag(GenerateCompletionFlag)
|
||||
if ctx.App.UseShortOptionHandling {
|
||||
c.UseShortOptionHandling = true
|
||||
}
|
||||
|
||||
//if ctx.App.EnableShellCompletion {
|
||||
// c.appendFlag(GenerateCompletionFlag)
|
||||
//}
|
||||
|
||||
set, err := flagSet(c.Name, c.Flags)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -108,9 +117,9 @@ func (c *Command) Run(ctx *Context) (err error) {
|
||||
|
||||
nerr := normalizeFlags(c.Flags, set)
|
||||
if nerr != nil {
|
||||
fmt.Fprintln(ctx.App.Writer, nerr)
|
||||
fmt.Fprintln(ctx.App.Writer)
|
||||
ShowCommandHelp(ctx, c.Name)
|
||||
_, _ = fmt.Fprintln(ctx.App.Writer, nerr)
|
||||
_, _ = fmt.Fprintln(ctx.App.Writer)
|
||||
_ = ShowCommandHelp(ctx, c.Name)
|
||||
return nerr
|
||||
}
|
||||
|
||||
@ -126,9 +135,9 @@ func (c *Command) Run(ctx *Context) (err error) {
|
||||
HandleExitCoder(err)
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
|
||||
fmt.Fprintln(context.App.Writer)
|
||||
ShowCommandHelp(context, c.Name)
|
||||
_, _ = fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
|
||||
_, _ = fmt.Fprintln(context.App.Writer)
|
||||
_ = ShowCommandHelp(context, c.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -136,6 +145,12 @@ func (c *Command) Run(ctx *Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
cerr := checkRequiredFlags(c.Flags, context)
|
||||
if cerr != nil {
|
||||
_ = ShowCommandHelp(context, c.Name)
|
||||
return cerr
|
||||
}
|
||||
|
||||
if c.After != nil {
|
||||
defer func() {
|
||||
afterErr := c.After(context)
|
||||
@ -172,6 +187,14 @@ func (c *Command) Run(ctx *Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Command) newFlagSet() (*flag.FlagSet, error) {
|
||||
return flagSet(c.Name, c.Flags)
|
||||
}
|
||||
|
||||
func (c *Command) useShortOptionHandling() bool {
|
||||
return c.UseShortOptionHandling
|
||||
}
|
||||
|
||||
// Names returns the names including short names and aliases.
|
||||
func (c *Command) Names() []string {
|
||||
return append([]string{c.Name}, c.Aliases...)
|
||||
@ -217,6 +240,7 @@ func (c *Command) startApp(ctx *Context) error {
|
||||
app.Compiled = ctx.App.Compiled
|
||||
app.Writer = ctx.App.Writer
|
||||
app.ErrWriter = ctx.App.ErrWriter
|
||||
app.UseShortOptionHandling = ctx.App.UseShortOptionHandling
|
||||
|
||||
app.Categories = newCommandCategories()
|
||||
for _, command := range c.Subcommands {
|
||||
@ -226,9 +250,9 @@ func (c *Command) startApp(ctx *Context) error {
|
||||
sort.Sort(app.Categories.(*commandCategories))
|
||||
|
||||
// bash completion
|
||||
app.EnableShellCompletion = ctx.App.EnableShellCompletion
|
||||
if c.ShellComplete != nil {
|
||||
app.ShellComplete = c.ShellComplete
|
||||
app.EnableBashCompletion = ctx.App.EnableBashCompletion
|
||||
if c.BashComplete != nil {
|
||||
app.BashComplete = c.BashComplete
|
||||
}
|
||||
|
||||
// set the actions
|
||||
|
155
command_test.go
155
command_test.go
@ -11,32 +11,40 @@ import (
|
||||
|
||||
func TestCommandFlagParsing(t *testing.T) {
|
||||
cases := []struct {
|
||||
testArgs []string
|
||||
skipFlagParsing bool
|
||||
expectedErr error
|
||||
testArgs []string
|
||||
skipFlagParsing bool
|
||||
skipArgReorder bool
|
||||
expectedErr error
|
||||
UseShortOptionHandling bool
|
||||
}{
|
||||
// Test normal "not ignoring flags" flow
|
||||
{[]string{"test-cmd", "-break", "blah", "blah"}, false, errors.New("flag provided but not defined: -break")},
|
||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break"), false},
|
||||
|
||||
{[]string{"test-cmd", "blah", "blah"}, true, nil}, // Test SkipFlagParsing without any args that look like flags
|
||||
{[]string{"test-cmd", "blah", "-break"}, true, nil}, // Test SkipFlagParsing with random flag arg
|
||||
{[]string{"test-cmd", "blah", "-help"}, true, nil}, // Test SkipFlagParsing with "special" help flag arg
|
||||
// Test no arg reorder
|
||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false},
|
||||
{[]string{"test-cmd", "blah", "blah", "-break", "ls", "-l"}, false, true, nil, true},
|
||||
|
||||
{[]string{"test-cmd", "blah", "blah"}, true, false, nil, false}, // Test SkipFlagParsing without any args that look like flags
|
||||
{[]string{"test-cmd", "blah", "-break"}, true, false, nil, false}, // Test SkipFlagParsing with random flag arg
|
||||
{[]string{"test-cmd", "blah", "-help"}, true, false, nil, false}, // Test SkipFlagParsing with "special" help flag arg
|
||||
{[]string{"test-cmd", "blah"}, false, false, nil, true}, // Test UseShortOptionHandling
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
app := &App{Writer: ioutil.Discard}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Parse(c.testArgs)
|
||||
_ = set.Parse(c.testArgs)
|
||||
|
||||
context := NewContext(app, set, nil)
|
||||
|
||||
command := Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *Context) error { return nil },
|
||||
SkipFlagParsing: c.skipFlagParsing,
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *Context) error { return nil },
|
||||
SkipFlagParsing: c.skipFlagParsing,
|
||||
UseShortOptionHandling: c.UseShortOptionHandling,
|
||||
}
|
||||
|
||||
err := command.Run(context)
|
||||
@ -46,6 +54,51 @@ func TestCommandFlagParsing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAndRunShortOpts(t *testing.T) {
|
||||
cases := []struct {
|
||||
testArgs []string
|
||||
expectedErr error
|
||||
expectedArgs []string
|
||||
}{
|
||||
{[]string{"foo", "test", "-a"}, nil, []string{}},
|
||||
{[]string{"foo", "test", "-c", "arg1", "arg2"}, nil, []string{"arg1", "arg2"}},
|
||||
{[]string{"foo", "test", "-f"}, nil, []string{}},
|
||||
{[]string{"foo", "test", "-ac", "--fgh"}, nil, []string{}},
|
||||
{[]string{"foo", "test", "-af"}, nil, []string{}},
|
||||
{[]string{"foo", "test", "-cf"}, nil, []string{}},
|
||||
{[]string{"foo", "test", "-acf"}, nil, []string{}},
|
||||
{[]string{"foo", "test", "-invalid"}, errors.New("flag provided but not defined: -invalid"), []string{}},
|
||||
{[]string{"foo", "test", "-acf", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}},
|
||||
}
|
||||
|
||||
var args Args
|
||||
cmd := Command{
|
||||
Name: "test",
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(c *Context) error {
|
||||
args = c.Args()
|
||||
return nil
|
||||
},
|
||||
UseShortOptionHandling: true,
|
||||
Flags: []Flag{
|
||||
&BoolFlag{Name: "abc", Aliases: []string{"a"}},
|
||||
&BoolFlag{Name: "cde", Aliases: []string{"c"}},
|
||||
&BoolFlag{Name: "fgh", Aliases: []string{"f"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
app := NewApp()
|
||||
app.Commands = []Command{cmd}
|
||||
|
||||
err := app.Run(c.testArgs)
|
||||
|
||||
expect(t, err, c.expectedErr)
|
||||
expect(t, args, c.expectedArgs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
|
||||
app := &App{
|
||||
Commands: []*Command{
|
||||
@ -238,3 +291,77 @@ func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandFlagReordering(t *testing.T) {
|
||||
cases := []struct {
|
||||
testArgs []string
|
||||
expectedValue string
|
||||
expectedArgs []string
|
||||
expectedErr error
|
||||
}{
|
||||
{[]string{"some-exec", "some-command", "some-arg", "--flag", "foo"}, "foo", []string{"some-arg"}, nil},
|
||||
{[]string{"some-exec", "some-command", "some-arg", "--flag=foo"}, "foo", []string{"some-arg"}, nil},
|
||||
{[]string{"some-exec", "some-command", "--flag=foo", "some-arg"}, "foo", []string{"some-arg"}, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
value := ""
|
||||
var args Args
|
||||
app := &App{
|
||||
Commands: []*Command{
|
||||
{
|
||||
Name: "some-command",
|
||||
Flags: []Flag{
|
||||
&StringFlag{Name: "flag"},
|
||||
},
|
||||
Action: func(c *Context) error {
|
||||
fmt.Printf("%+v\n", c.String("flag"))
|
||||
value = c.String("flag")
|
||||
args = c.Args()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(c.testArgs)
|
||||
expect(t, err, c.expectedErr)
|
||||
expect(t, value, c.expectedValue)
|
||||
expect(t, args, c.expectedArgs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSkipFlagParsing(t *testing.T) {
|
||||
cases := []struct {
|
||||
testArgs []string
|
||||
expectedArgs []string
|
||||
expectedErr error
|
||||
}{
|
||||
{[]string{"some-exec", "some-command", "some-arg", "--flag", "foo"}, []string{"some-arg", "--flag", "foo"}, nil},
|
||||
{[]string{"some-exec", "some-command", "some-arg", "--flag=foo"}, []string{"some-arg", "--flag=foo"}, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
var args Args
|
||||
app := &App{
|
||||
Commands: []*Command{
|
||||
{
|
||||
SkipFlagParsing: true,
|
||||
Name: "some-command",
|
||||
Flags: []Flag{
|
||||
&StringFlag{Name: "flag"},
|
||||
},
|
||||
Action: func(c *Context) error {
|
||||
fmt.Printf("%+v\n", c.String("flag"))
|
||||
args = c.Args()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(c.testArgs)
|
||||
expect(t, err, c.expectedErr)
|
||||
expect(t, args, c.expectedArgs)
|
||||
}
|
||||
}
|
||||
|
170
context.go
170
context.go
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
@ -20,7 +21,7 @@ type Context struct {
|
||||
App *App
|
||||
Command *Command
|
||||
shellComplete bool
|
||||
|
||||
setFlags map[string]bool
|
||||
flagSet *flag.FlagSet
|
||||
parentContext *Context
|
||||
}
|
||||
@ -65,41 +66,56 @@ func (c *Context) IsSet(name string) bool {
|
||||
isSet = true
|
||||
}
|
||||
})
|
||||
if isSet {
|
||||
return true
|
||||
|
||||
// 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 {
|
||||
for _, name := range f.Names() {
|
||||
if isSet, ok := c.setFlags[name]; isSet || !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// 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 avaliable.
|
||||
//
|
||||
// See https://github.com/urfave/cli/issues/294 for additional discussion
|
||||
f := lookupFlag(name, c)
|
||||
if f == nil {
|
||||
return false
|
||||
}
|
||||
val := reflect.ValueOf(f)
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
envVarValues := val.FieldByName("EnvVars")
|
||||
if !envVarValues.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, envVar := range envVarValues.Interface().([]string) {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal := os.Getenv(envVar); envVal != "" {
|
||||
return true
|
||||
envVarValues := val.FieldByName("EnvVars")
|
||||
if envVarValues.IsValid() {
|
||||
for _, envVar := range envVarValues.Interface().([]string) {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if _, ok := syscall.Getenv(envVar); ok {
|
||||
c.setFlags[name] = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +124,7 @@ func (c *Context) IsSet(name string) bool {
|
||||
|
||||
// LocalFlagNames returns a slice of flag names used in this context.
|
||||
func (c *Context) LocalFlagNames() []string {
|
||||
names := []string{}
|
||||
var names []string
|
||||
c.flagSet.Visit(makeFlagNameVisitor(&names))
|
||||
return names
|
||||
}
|
||||
@ -116,17 +132,42 @@ func (c *Context) LocalFlagNames() []string {
|
||||
// FlagNames returns a slice of flag names used by the this context and all of
|
||||
// its parent contexts.
|
||||
func (c *Context) FlagNames() []string {
|
||||
names := []string{}
|
||||
var names []string
|
||||
for _, ctx := range c.Lineage() {
|
||||
ctx.flagSet.Visit(makeFlagNameVisitor(&names))
|
||||
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// FlagNames returns a slice of flag names used in this context.
|
||||
//func (c *Context) FlagNames() (names []string) {
|
||||
// for _, f := range c.Command.Flags {
|
||||
// name := strings.Split(f.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 _, f := range c.App.Flags {
|
||||
// name := strings.Split(f.GetName(), ",")[0]
|
||||
// if name == "help" || name == "version" {
|
||||
// continue
|
||||
// }
|
||||
// names = append(names, name)
|
||||
// }
|
||||
// return names
|
||||
//}
|
||||
|
||||
// Lineage returns *this* context and all of its ancestor contexts in order from
|
||||
// child to parent
|
||||
func (c *Context) Lineage() []*Context {
|
||||
lineage := []*Context{}
|
||||
var lineage []*Context
|
||||
|
||||
for cur := c; cur != nil; cur = cur.parentContext {
|
||||
lineage = append(lineage, cur)
|
||||
@ -191,10 +232,10 @@ func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
|
||||
|
||||
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
||||
switch ff.Value.(type) {
|
||||
case Serializeder:
|
||||
set.Set(name, ff.Value.(Serializeder).Serialized())
|
||||
case Serializer:
|
||||
_ = set.Set(name, ff.Value.(Serializer).Serialize())
|
||||
default:
|
||||
set.Set(name, ff.Value.String())
|
||||
_ = set.Set(name, ff.Value.String())
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +285,58 @@ func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
(*names) = append(*names, name)
|
||||
*names = append(*names, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type requiredFlagsErr interface {
|
||||
error
|
||||
getMissingFlags() []string
|
||||
}
|
||||
|
||||
type errRequiredFlags struct {
|
||||
missingFlags []string
|
||||
}
|
||||
|
||||
func (e *errRequiredFlags) Error() string {
|
||||
numberOfMissingFlags := len(e.missingFlags)
|
||||
if numberOfMissingFlags == 1 {
|
||||
return fmt.Sprintf("Required flag %q not set", e.missingFlags[0])
|
||||
}
|
||||
joinedMissingFlags := strings.Join(e.missingFlags, ", ")
|
||||
return fmt.Sprintf("Required flags %q not set", joinedMissingFlags)
|
||||
}
|
||||
|
||||
func (e *errRequiredFlags) getMissingFlags() []string {
|
||||
return e.missingFlags
|
||||
}
|
||||
|
||||
func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr {
|
||||
var missingFlags []string
|
||||
for _, f := range flags {
|
||||
if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
|
||||
var flagPresent bool
|
||||
var flagName string
|
||||
for _, key := range f.Names() {
|
||||
if len(key) > 1 {
|
||||
flagName = key
|
||||
}
|
||||
|
||||
if context.IsSet(strings.TrimSpace(key)) {
|
||||
flagPresent = true
|
||||
}
|
||||
}
|
||||
|
||||
if !flagPresent && flagName != "" {
|
||||
missingFlags = append(missingFlags, flagName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingFlags) != 0 {
|
||||
return &errRequiredFlags{missingFlags: missingFlags}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
286
context_test.go
286
context_test.go
@ -3,7 +3,12 @@ package cli
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
|
||||
"sort"
|
||||
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -91,9 +96,11 @@ func TestContext_Float64(t *testing.T) {
|
||||
func TestContext_Duration(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Duration("myflag", 12*time.Second, "doc")
|
||||
|
||||
parentSet := flag.NewFlagSet("test", 0)
|
||||
parentSet.Duration("top-flag", 13*time.Second, "doc")
|
||||
parentCtx := NewContext(nil, parentSet, nil)
|
||||
|
||||
c := NewContext(nil, set, parentCtx)
|
||||
expect(t, c.Duration("myflag"), 12*time.Second)
|
||||
expect(t, c.Duration("top-flag"), 13*time.Second)
|
||||
@ -136,7 +143,7 @@ func TestContext_Args(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
_ = set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
expect(t, c.Args().Len(), 2)
|
||||
expect(t, c.Bool("myflag"), true)
|
||||
}
|
||||
@ -145,7 +152,7 @@ func TestContext_NArg(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
c := NewContext(nil, set, nil)
|
||||
set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
_ = set.Parse([]string{"--myflag", "bat", "baz"})
|
||||
expect(t, c.NArg(), 2)
|
||||
}
|
||||
|
||||
@ -159,8 +166,8 @@ func TestContext_IsSet(t *testing.T) {
|
||||
parentCtx := NewContext(nil, parentSet, nil)
|
||||
ctx := NewContext(nil, set, parentCtx)
|
||||
|
||||
set.Parse([]string{"--one-flag", "--two-flag", "--three-flag", "frob"})
|
||||
parentSet.Parse([]string{"--top-flag"})
|
||||
_ = set.Parse([]string{"--one-flag", "--two-flag", "--three-flag", "frob"})
|
||||
_ = parentSet.Parse([]string{"--top-flag"})
|
||||
|
||||
expect(t, ctx.IsSet("one-flag"), true)
|
||||
expect(t, ctx.IsSet("two-flag"), true)
|
||||
@ -169,6 +176,121 @@ func TestContext_IsSet(t *testing.T) {
|
||||
expect(t, ctx.IsSet("bogus"), false)
|
||||
}
|
||||
|
||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
||||
// Should be moved to `flag_test` in v2
|
||||
func TestContext_IsSet_fromEnv(t *testing.T) {
|
||||
var (
|
||||
timeoutIsSet, tIsSet bool
|
||||
noEnvVarIsSet, nIsSet bool
|
||||
passwordIsSet, pIsSet bool
|
||||
unparsableIsSet, uIsSet bool
|
||||
)
|
||||
|
||||
os.Clearenv()
|
||||
_ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
_ = os.Setenv("APP_PASSWORD", "")
|
||||
a := App{
|
||||
Flags: []Flag{
|
||||
&Float64Flag{Name: "timeout", Aliases: []string{"t"}, EnvVars: []string{"APP_TIMEOUT_SECONDS"}},
|
||||
&StringFlag{Name: "password", Aliases: []string{"p"}, EnvVars: []string{"APP_PASSWORD"}},
|
||||
&Float64Flag{Name: "unparsable", Aliases: []string{"u"}, EnvVars: []string{"APP_UNPARSABLE"}},
|
||||
&Float64Flag{Name: "no-env-var", Aliases: []string{"n"}},
|
||||
},
|
||||
Action: func(ctx *Context) error {
|
||||
timeoutIsSet = ctx.IsSet("timeout")
|
||||
tIsSet = ctx.IsSet("t")
|
||||
passwordIsSet = ctx.IsSet("password")
|
||||
pIsSet = ctx.IsSet("p")
|
||||
unparsableIsSet = ctx.IsSet("unparsable")
|
||||
uIsSet = ctx.IsSet("u")
|
||||
noEnvVarIsSet = ctx.IsSet("no-env-var")
|
||||
nIsSet = ctx.IsSet("n")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
_ = a.Run([]string{"run"})
|
||||
expect(t, timeoutIsSet, true)
|
||||
expect(t, tIsSet, true)
|
||||
expect(t, passwordIsSet, true)
|
||||
expect(t, pIsSet, true)
|
||||
expect(t, noEnvVarIsSet, false)
|
||||
expect(t, nIsSet, false)
|
||||
|
||||
_ = os.Setenv("APP_UNPARSABLE", "foobar")
|
||||
_ = a.Run([]string{"run"})
|
||||
expect(t, unparsableIsSet, false)
|
||||
expect(t, uIsSet, false)
|
||||
}
|
||||
|
||||
// XXX Corresponds to hack in context.IsSet for flags with EnvVar field
|
||||
// TODO: Should be moved to `flag_test` in v2
|
||||
//func TestContext_GlobalIsSet_fromEnv(t *testing.T) {
|
||||
// var (
|
||||
// timeoutIsSet, tIsSet bool
|
||||
// noEnvVarIsSet, nIsSet bool
|
||||
// passwordIsSet, pIsSet bool
|
||||
// passwordValue string
|
||||
// unparsableIsSet, uIsSet bool
|
||||
// overrideIsSet, oIsSet bool
|
||||
// overrideValue string
|
||||
// )
|
||||
//
|
||||
// os.Clearenv()
|
||||
// _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5")
|
||||
// _ = os.Setenv("APP_PASSWORD", "badpass")
|
||||
// _ = os.Setenv("APP_OVERRIDE", "overridden")
|
||||
// a := App{
|
||||
// Flags: []Flag{
|
||||
// Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"},
|
||||
// StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"},
|
||||
// Float64Flag{Name: "no-env-var, n"},
|
||||
// Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"},
|
||||
// StringFlag{Name: "overrides-default, o", Value: "default", EnvVar: "APP_OVERRIDE"},
|
||||
// },
|
||||
// Commands: []Command{
|
||||
// {
|
||||
// Name: "hello",
|
||||
// Action: func(ctx *Context) error {
|
||||
// timeoutIsSet = ctx.GlobalIsSet("timeout")
|
||||
// tIsSet = ctx.GlobalIsSet("t")
|
||||
// passwordIsSet = ctx.GlobalIsSet("password")
|
||||
// pIsSet = ctx.GlobalIsSet("p")
|
||||
// passwordValue = ctx.GlobalString("password")
|
||||
// unparsableIsSet = ctx.GlobalIsSet("unparsable")
|
||||
// uIsSet = ctx.GlobalIsSet("u")
|
||||
// noEnvVarIsSet = ctx.GlobalIsSet("no-env-var")
|
||||
// nIsSet = ctx.GlobalIsSet("n")
|
||||
// overrideIsSet = ctx.GlobalIsSet("overrides-default")
|
||||
// oIsSet = ctx.GlobalIsSet("o")
|
||||
// overrideValue = ctx.GlobalString("overrides-default")
|
||||
// return nil
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// if err := a.Run([]string{"run", "hello"}); err != nil {
|
||||
// t.Logf("error running Run(): %+v", err)
|
||||
// }
|
||||
// expect(t, timeoutIsSet, true)
|
||||
// expect(t, tIsSet, true)
|
||||
// expect(t, passwordIsSet, true)
|
||||
// expect(t, pIsSet, true)
|
||||
// expect(t, passwordValue, "badpass")
|
||||
// expect(t, unparsableIsSet, false)
|
||||
// expect(t, noEnvVarIsSet, false)
|
||||
// expect(t, nIsSet, false)
|
||||
// expect(t, overrideIsSet, true)
|
||||
// expect(t, oIsSet, true)
|
||||
// expect(t, overrideValue, "overridden")
|
||||
//
|
||||
// _ = os.Setenv("APP_UNPARSABLE", "foobar")
|
||||
// if err := a.Run([]string{"run"}); err != nil {
|
||||
// t.Logf("error running Run(): %+v", err)
|
||||
// }
|
||||
// expect(t, unparsableIsSet, false)
|
||||
// expect(t, uIsSet, false)
|
||||
//}
|
||||
|
||||
func TestContext_NumFlags(t *testing.T) {
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("myflag", false, "doc")
|
||||
@ -177,8 +299,8 @@ func TestContext_NumFlags(t *testing.T) {
|
||||
globalSet.Bool("myflagGlobal", true, "doc")
|
||||
globalCtx := NewContext(nil, globalSet, nil)
|
||||
c := NewContext(nil, set, globalCtx)
|
||||
set.Parse([]string{"--myflag", "--otherflag=foo"})
|
||||
globalSet.Parse([]string{"--myflagGlobal"})
|
||||
_ = set.Parse([]string{"--myflag", "--otherflag=foo"})
|
||||
_ = globalSet.Parse([]string{"--myflagGlobal"})
|
||||
expect(t, c.NumFlags(), 2)
|
||||
}
|
||||
|
||||
@ -188,7 +310,7 @@ func TestContext_Set(t *testing.T) {
|
||||
c := NewContext(nil, set, nil)
|
||||
|
||||
expect(t, c.IsSet("int"), false)
|
||||
c.Set("int", "1")
|
||||
_ = c.Set("int", "1")
|
||||
expect(t, c.Int("int"), 1)
|
||||
expect(t, c.IsSet("int"), true)
|
||||
}
|
||||
@ -293,3 +415,153 @@ func TestContextPropagation(t *testing.T) {
|
||||
t.Fatal("expected context to not be nil even if the parent's context is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckRequiredFlags(t *testing.T) {
|
||||
tdata := []struct {
|
||||
testCase string
|
||||
parseInput []string
|
||||
envVarInput [2]string
|
||||
flags []Flag
|
||||
expectedAnError bool
|
||||
expectedErrorContents []string
|
||||
}{
|
||||
{
|
||||
testCase: "empty",
|
||||
},
|
||||
{
|
||||
testCase: "optional",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "optionalFlag"},
|
||||
},
|
||||
},
|
||||
{
|
||||
testCase: "required",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
},
|
||||
expectedAnError: true,
|
||||
expectedErrorContents: []string{"requiredFlag"},
|
||||
},
|
||||
{
|
||||
testCase: "required_and_present",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
},
|
||||
parseInput: []string{"--requiredFlag", "myinput"},
|
||||
},
|
||||
{
|
||||
testCase: "required_and_present_via_env_var",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true, EnvVar: "REQUIRED_FLAG"},
|
||||
},
|
||||
envVarInput: [2]string{"REQUIRED_FLAG", "true"},
|
||||
},
|
||||
{
|
||||
testCase: "required_and_optional",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
&StringFlag{Name: "optionalFlag"},
|
||||
},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "required_and_optional_and_optional_present",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
&StringFlag{Name: "optionalFlag"},
|
||||
},
|
||||
parseInput: []string{"--optionalFlag", "myinput"},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "required_and_optional_and_optional_present_via_env_var",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
&StringFlag{Name: "optionalFlag", EnvVar: "OPTIONAL_FLAG"},
|
||||
},
|
||||
envVarInput: [2]string{"OPTIONAL_FLAG", "true"},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "required_and_optional_and_required_present",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
&StringFlag{Name: "optionalFlag"},
|
||||
},
|
||||
parseInput: []string{"--requiredFlag", "myinput"},
|
||||
},
|
||||
{
|
||||
testCase: "two_required",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlagOne", Required: true},
|
||||
&StringFlag{Name: "requiredFlagTwo", Required: true},
|
||||
},
|
||||
expectedAnError: true,
|
||||
expectedErrorContents: []string{"requiredFlagOne", "requiredFlagTwo"},
|
||||
},
|
||||
{
|
||||
testCase: "two_required_and_one_present",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
&StringFlag{Name: "requiredFlagTwo", Required: true},
|
||||
},
|
||||
parseInput: []string{"--requiredFlag", "myinput"},
|
||||
expectedAnError: true,
|
||||
},
|
||||
{
|
||||
testCase: "two_required_and_both_present",
|
||||
flags: []Flag{
|
||||
&StringFlag{Name: "requiredFlag", Required: true},
|
||||
&StringFlag{Name: "requiredFlagTwo", Required: true},
|
||||
},
|
||||
parseInput: []string{"--requiredFlag", "myinput", "--requiredFlagTwo", "myinput"},
|
||||
},
|
||||
{
|
||||
testCase: "required_flag_with_short_name",
|
||||
flags: []Flag{
|
||||
&StringSliceFlag{Name: "names, N", Required: true},
|
||||
},
|
||||
parseInput: []string{"-N", "asd", "-N", "qwe"},
|
||||
},
|
||||
{
|
||||
testCase: "required_flag_with_multiple_short_names",
|
||||
flags: []Flag{
|
||||
&StringSliceFlag{Name: "names, N, n", Required: true},
|
||||
},
|
||||
parseInput: []string{"-n", "asd", "-n", "qwe"},
|
||||
},
|
||||
}
|
||||
for _, test := range tdata {
|
||||
t.Run(test.testCase, func(t *testing.T) {
|
||||
// setup
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
for _, flags := range test.flags {
|
||||
flags.Apply(set)
|
||||
}
|
||||
_ = set.Parse(test.parseInput)
|
||||
if test.envVarInput[0] != "" {
|
||||
os.Clearenv()
|
||||
_ = os.Setenv(test.envVarInput[0], test.envVarInput[1])
|
||||
}
|
||||
ctx := &Context{}
|
||||
context := NewContext(ctx.App, set, ctx)
|
||||
context.Command.Flags = test.flags
|
||||
|
||||
// logic under test
|
||||
err := checkRequiredFlags(test.flags, context)
|
||||
|
||||
// assertions
|
||||
if test.expectedAnError && err == nil {
|
||||
t.Errorf("expected an error, but there was none")
|
||||
}
|
||||
if !test.expectedAnError && err != nil {
|
||||
t.Errorf("did not expected an error, but there was one: %s", err)
|
||||
}
|
||||
for _, errString := range test.expectedErrorContents {
|
||||
if !strings.Contains(err.Error(), errString) {
|
||||
t.Errorf("expected error %q to contain %q, but it didn't!", err.Error(), errString)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
148
docs.go
Normal file
148
docs.go
Normal file
@ -0,0 +1,148 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/cpuguy83/go-md2man/v2/md2man"
|
||||
)
|
||||
|
||||
// ToMarkdown creates a markdown string for the `*App`
|
||||
// The function errors if either parsing or writing of the string fails.
|
||||
func (a *App) ToMarkdown() (string, error) {
|
||||
var w bytes.Buffer
|
||||
if err := a.writeDocTemplate(&w); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return w.String(), nil
|
||||
}
|
||||
|
||||
// ToMan creates a man page string for the `*App`
|
||||
// The function errors if either parsing or writing of the string fails.
|
||||
func (a *App) ToMan() (string, error) {
|
||||
var w bytes.Buffer
|
||||
if err := a.writeDocTemplate(&w); err != nil {
|
||||
return "", err
|
||||
}
|
||||
man := md2man.Render(w.Bytes())
|
||||
return string(man), nil
|
||||
}
|
||||
|
||||
type cliTemplate struct {
|
||||
App *App
|
||||
Commands []string
|
||||
GlobalArgs []string
|
||||
SynopsisArgs []string
|
||||
}
|
||||
|
||||
func (a *App) writeDocTemplate(w io.Writer) error {
|
||||
const name = "cli"
|
||||
t, err := template.New(name).Parse(MarkdownDocTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.ExecuteTemplate(w, name, &cliTemplate{
|
||||
App: a,
|
||||
Commands: prepareCommands(a.Commands, 0),
|
||||
GlobalArgs: prepareArgsWithValues(a.Flags),
|
||||
SynopsisArgs: prepareArgsSynopsis(a.Flags),
|
||||
})
|
||||
}
|
||||
|
||||
func prepareCommands(commands []Command, level int) []string {
|
||||
coms := []string{}
|
||||
for i := range commands {
|
||||
command := &commands[i]
|
||||
if command.Hidden {
|
||||
continue
|
||||
}
|
||||
usage := ""
|
||||
if command.Usage != "" {
|
||||
usage = command.Usage
|
||||
}
|
||||
|
||||
prepared := fmt.Sprintf("%s %s\n\n%s\n",
|
||||
strings.Repeat("#", level+2),
|
||||
strings.Join(command.Names(), ", "),
|
||||
usage,
|
||||
)
|
||||
|
||||
flags := prepareArgsWithValues(command.Flags)
|
||||
if len(flags) > 0 {
|
||||
prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n"))
|
||||
}
|
||||
|
||||
coms = append(coms, prepared)
|
||||
|
||||
// recursevly iterate subcommands
|
||||
if len(command.Subcommands) > 0 {
|
||||
coms = append(
|
||||
coms,
|
||||
prepareCommands(command.Subcommands, level+1)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return coms
|
||||
}
|
||||
|
||||
func prepareArgsWithValues(flags []Flag) []string {
|
||||
return prepareFlags(flags, ", ", "**", "**", `""`, true)
|
||||
}
|
||||
|
||||
func prepareArgsSynopsis(flags []Flag) []string {
|
||||
return prepareFlags(flags, "|", "[", "]", "[value]", false)
|
||||
}
|
||||
|
||||
func prepareFlags(
|
||||
flags []Flag,
|
||||
sep, opener, closer, value string,
|
||||
addDetails bool,
|
||||
) []string {
|
||||
args := []string{}
|
||||
for _, f := range flags {
|
||||
flag, ok := f.(DocGenerationFlag)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
modifiedArg := opener
|
||||
for _, s := range strings.Split(flag.GetName(), ",") {
|
||||
trimmed := strings.TrimSpace(s)
|
||||
if len(modifiedArg) > len(opener) {
|
||||
modifiedArg += sep
|
||||
}
|
||||
if len(trimmed) > 1 {
|
||||
modifiedArg += fmt.Sprintf("--%s", trimmed)
|
||||
} else {
|
||||
modifiedArg += fmt.Sprintf("-%s", trimmed)
|
||||
}
|
||||
}
|
||||
modifiedArg += closer
|
||||
if flag.TakesValue() {
|
||||
modifiedArg += fmt.Sprintf("=%s", value)
|
||||
}
|
||||
|
||||
if addDetails {
|
||||
modifiedArg += flagDetails(flag)
|
||||
}
|
||||
|
||||
args = append(args, modifiedArg+"\n")
|
||||
|
||||
}
|
||||
sort.Strings(args)
|
||||
return args
|
||||
}
|
||||
|
||||
// flagDetails returns a string containing the flags metadata
|
||||
func flagDetails(flag DocGenerationFlag) string {
|
||||
description := flag.GetUsage()
|
||||
value := flag.GetValue()
|
||||
if value != "" {
|
||||
description += " (default: " + value + ")"
|
||||
}
|
||||
return ": " + description
|
||||
}
|
122
docs_test.go
Normal file
122
docs_test.go
Normal file
@ -0,0 +1,122 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testApp() *App {
|
||||
app := NewApp()
|
||||
app.Name = "greet"
|
||||
app.Flags = []Flag{
|
||||
StringFlag{
|
||||
Name: "socket, s",
|
||||
Usage: "some 'usage' text",
|
||||
Value: "value",
|
||||
TakesFile: true,
|
||||
},
|
||||
StringFlag{Name: "flag, fl, f"},
|
||||
BoolFlag{
|
||||
Name: "another-flag, b",
|
||||
Usage: "another usage text",
|
||||
},
|
||||
}
|
||||
app.Commands = []Command{{
|
||||
Aliases: []string{"c"},
|
||||
Flags: []Flag{
|
||||
StringFlag{
|
||||
Name: "flag, fl, f",
|
||||
TakesFile: true,
|
||||
},
|
||||
BoolFlag{
|
||||
Name: "another-flag, b",
|
||||
Usage: "another usage text",
|
||||
},
|
||||
},
|
||||
Name: "config",
|
||||
Usage: "another usage test",
|
||||
Subcommands: []Command{{
|
||||
Aliases: []string{"s", "ss"},
|
||||
Flags: []Flag{
|
||||
StringFlag{Name: "sub-flag, sub-fl, s"},
|
||||
BoolFlag{
|
||||
Name: "sub-command-flag, s",
|
||||
Usage: "some usage text",
|
||||
},
|
||||
},
|
||||
Name: "sub-config",
|
||||
Usage: "another usage test",
|
||||
}},
|
||||
}, {
|
||||
Aliases: []string{"i", "in"},
|
||||
Name: "info",
|
||||
Usage: "retrieve generic information",
|
||||
}, {
|
||||
Name: "some-command",
|
||||
}, {
|
||||
Name: "hidden-command",
|
||||
Hidden: true,
|
||||
}}
|
||||
app.UsageText = "app [first_arg] [second_arg]"
|
||||
app.Usage = "Some app"
|
||||
app.Author = "Harrison"
|
||||
app.Email = "harrison@lolwut.com"
|
||||
app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
|
||||
return app
|
||||
}
|
||||
|
||||
func expectFileContent(t *testing.T, file, expected string) {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
expect(t, err, nil)
|
||||
expect(t, string(data), expected)
|
||||
}
|
||||
|
||||
func TestToMarkdownFull(t *testing.T) {
|
||||
// Given
|
||||
app := testApp()
|
||||
|
||||
// When
|
||||
res, err := app.ToMarkdown()
|
||||
|
||||
// Then
|
||||
expect(t, err, nil)
|
||||
expectFileContent(t, "testdata/expected-doc-full.md", res)
|
||||
}
|
||||
|
||||
func TestToMarkdownNoFlags(t *testing.T) {
|
||||
// Given
|
||||
app := testApp()
|
||||
app.Flags = nil
|
||||
|
||||
// When
|
||||
res, err := app.ToMarkdown()
|
||||
|
||||
// Then
|
||||
expect(t, err, nil)
|
||||
expectFileContent(t, "testdata/expected-doc-no-flags.md", res)
|
||||
}
|
||||
|
||||
func TestToMarkdownNoCommands(t *testing.T) {
|
||||
// Given
|
||||
app := testApp()
|
||||
app.Commands = nil
|
||||
|
||||
// When
|
||||
res, err := app.ToMarkdown()
|
||||
|
||||
// Then
|
||||
expect(t, err, nil)
|
||||
expectFileContent(t, "testdata/expected-doc-no-commands.md", res)
|
||||
}
|
||||
|
||||
func TestToMan(t *testing.T) {
|
||||
// Given
|
||||
app := testApp()
|
||||
|
||||
// When
|
||||
res, err := app.ToMan()
|
||||
|
||||
// Then
|
||||
expect(t, err, nil)
|
||||
expectFileContent(t, "testdata/expected-doc-full.man", res)
|
||||
}
|
22
errors.go
22
errors.go
@ -59,25 +59,33 @@ type ExitCoder interface {
|
||||
ExitCode() int
|
||||
}
|
||||
|
||||
type exitError struct {
|
||||
type ExitError struct {
|
||||
exitCode int
|
||||
message interface{}
|
||||
}
|
||||
|
||||
// Exit wraps a message and exit code into an ExitCoder suitable for handling by
|
||||
// HandleExitCoder
|
||||
func Exit(message interface{}, exitCode int) ExitCoder {
|
||||
return &exitError{
|
||||
// NewExitError makes a new *ExitError
|
||||
func NewExitError(message interface{}, exitCode int) *ExitError {
|
||||
return &ExitError{
|
||||
exitCode: exitCode,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func (ee *exitError) Error() string {
|
||||
// Exit wraps a message and exit code into an ExitCoder suitable for handling by
|
||||
// HandleExitCoder
|
||||
func Exit(message interface{}, exitCode int) ExitCoder {
|
||||
return &ExitError{
|
||||
exitCode: exitCode,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func (ee *ExitError) Error() string {
|
||||
return fmt.Sprintf("%v", ee.message)
|
||||
}
|
||||
|
||||
func (ee *exitError) ExitCode() int {
|
||||
func (ee *ExitError) ExitCode() int {
|
||||
return ee.exitCode
|
||||
}
|
||||
|
||||
|
194
fish.go
Normal file
194
fish.go
Normal file
@ -0,0 +1,194 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// ToFishCompletion creates a fish completion string for the `*App`
|
||||
// The function errors if either parsing or writing of the string fails.
|
||||
func (a *App) ToFishCompletion() (string, error) {
|
||||
var w bytes.Buffer
|
||||
if err := a.writeFishCompletionTemplate(&w); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return w.String(), nil
|
||||
}
|
||||
|
||||
type fishCompletionTemplate struct {
|
||||
App *App
|
||||
Completions []string
|
||||
AllCommands []string
|
||||
}
|
||||
|
||||
func (a *App) writeFishCompletionTemplate(w io.Writer) error {
|
||||
const name = "cli"
|
||||
t, err := template.New(name).Parse(FishCompletionTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allCommands := []string{}
|
||||
|
||||
// Add global flags
|
||||
completions := a.prepareFishFlags(a.VisibleFlags(), allCommands)
|
||||
|
||||
// Add help flag
|
||||
if !a.HideHelp {
|
||||
completions = append(
|
||||
completions,
|
||||
a.prepareFishFlags([]Flag{HelpFlag}, allCommands)...,
|
||||
)
|
||||
}
|
||||
|
||||
// Add version flag
|
||||
if !a.HideVersion {
|
||||
completions = append(
|
||||
completions,
|
||||
a.prepareFishFlags([]Flag{VersionFlag}, allCommands)...,
|
||||
)
|
||||
}
|
||||
|
||||
// Add commands and their flags
|
||||
completions = append(
|
||||
completions,
|
||||
a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})...,
|
||||
)
|
||||
|
||||
return t.ExecuteTemplate(w, name, &fishCompletionTemplate{
|
||||
App: a,
|
||||
Completions: completions,
|
||||
AllCommands: allCommands,
|
||||
})
|
||||
}
|
||||
|
||||
func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, previousCommands []string) []string {
|
||||
completions := []string{}
|
||||
for i := range commands {
|
||||
command := &commands[i]
|
||||
|
||||
if command.Hidden {
|
||||
continue
|
||||
}
|
||||
|
||||
var completion strings.Builder
|
||||
completion.WriteString(fmt.Sprintf(
|
||||
"complete -r -c %s -n '%s' -a '%s'",
|
||||
a.Name,
|
||||
a.fishSubcommandHelper(previousCommands),
|
||||
strings.Join(command.Names(), " "),
|
||||
))
|
||||
|
||||
if command.Usage != "" {
|
||||
completion.WriteString(fmt.Sprintf(" -d '%s'",
|
||||
escapeSingleQuotes(command.Usage)))
|
||||
}
|
||||
|
||||
if !command.HideHelp {
|
||||
completions = append(
|
||||
completions,
|
||||
a.prepareFishFlags([]Flag{HelpFlag}, command.Names())...,
|
||||
)
|
||||
}
|
||||
|
||||
*allCommands = append(*allCommands, command.Names()...)
|
||||
completions = append(completions, completion.String())
|
||||
completions = append(
|
||||
completions,
|
||||
a.prepareFishFlags(command.Flags, command.Names())...,
|
||||
)
|
||||
|
||||
// recursevly iterate subcommands
|
||||
if len(command.Subcommands) > 0 {
|
||||
completions = append(
|
||||
completions,
|
||||
a.prepareFishCommands(
|
||||
command.Subcommands, allCommands, command.Names(),
|
||||
)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return completions
|
||||
}
|
||||
|
||||
func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string {
|
||||
completions := []string{}
|
||||
for _, f := range flags {
|
||||
flag, ok := f.(DocGenerationFlag)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
completion := &strings.Builder{}
|
||||
completion.WriteString(fmt.Sprintf(
|
||||
"complete -c %s -n '%s'",
|
||||
a.Name,
|
||||
a.fishSubcommandHelper(previousCommands),
|
||||
))
|
||||
|
||||
fishAddFileFlag(f, completion)
|
||||
|
||||
for idx, opt := range strings.Split(flag.GetName(), ",") {
|
||||
if idx == 0 {
|
||||
completion.WriteString(fmt.Sprintf(
|
||||
" -l %s", strings.TrimSpace(opt),
|
||||
))
|
||||
} else {
|
||||
completion.WriteString(fmt.Sprintf(
|
||||
" -s %s", strings.TrimSpace(opt),
|
||||
))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if flag.TakesValue() {
|
||||
completion.WriteString(" -r")
|
||||
}
|
||||
|
||||
if flag.GetUsage() != "" {
|
||||
completion.WriteString(fmt.Sprintf(" -d '%s'",
|
||||
escapeSingleQuotes(flag.GetUsage())))
|
||||
}
|
||||
|
||||
completions = append(completions, completion.String())
|
||||
}
|
||||
|
||||
return completions
|
||||
}
|
||||
|
||||
func fishAddFileFlag(flag Flag, completion *strings.Builder) {
|
||||
switch f := flag.(type) {
|
||||
case GenericFlag:
|
||||
if f.TakesFile {
|
||||
return
|
||||
}
|
||||
case StringFlag:
|
||||
if f.TakesFile {
|
||||
return
|
||||
}
|
||||
case StringSliceFlag:
|
||||
if f.TakesFile {
|
||||
return
|
||||
}
|
||||
}
|
||||
completion.WriteString(" -f")
|
||||
}
|
||||
|
||||
func (a *App) fishSubcommandHelper(allCommands []string) string {
|
||||
fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name)
|
||||
if len(allCommands) > 0 {
|
||||
fishHelper = fmt.Sprintf(
|
||||
"__fish_seen_subcommand_from %s",
|
||||
strings.Join(allCommands, " "),
|
||||
)
|
||||
}
|
||||
return fishHelper
|
||||
|
||||
}
|
||||
|
||||
func escapeSingleQuotes(input string) string {
|
||||
return strings.Replace(input, `'`, `\'`, -1)
|
||||
}
|
17
fish_test.go
Normal file
17
fish_test.go
Normal file
@ -0,0 +1,17 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFishCompletion(t *testing.T) {
|
||||
// Given
|
||||
app := testApp()
|
||||
|
||||
// When
|
||||
res, err := app.ToFishCompletion()
|
||||
|
||||
// Then
|
||||
expect(t, err, nil)
|
||||
expectFileContent(t, "testdata/expected-fish-full.fish", res)
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Bool",
|
||||
"type": "bool",
|
||||
"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": "Float64Slice",
|
||||
"type": "*Float64Slice",
|
||||
"dest": false,
|
||||
"context_default": "nil",
|
||||
"context_type": "[]float64",
|
||||
"parser": "(f.Value.(*Float64Slice)).Value(), error(nil)"
|
||||
},
|
||||
{
|
||||
"name": "String",
|
||||
"type": "string",
|
||||
"context_default": "\"\"",
|
||||
"parser": "f.Value.String(), error(nil)"
|
||||
},
|
||||
{
|
||||
"name": "Path",
|
||||
"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)"
|
||||
}
|
||||
]
|
828
flag.go
828
flag.go
@ -1,9 +1,9 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@ -22,50 +22,66 @@ var (
|
||||
)
|
||||
|
||||
// GenerateCompletionFlag enables completion for all commands and subcommands
|
||||
var GenerateCompletionFlag Flag = &BoolFlag{
|
||||
Name: "generate-completion",
|
||||
//var GenerateCompletionFlag Flag = &BoolFlag{
|
||||
// Name: "generate-completion",
|
||||
// Hidden: true,
|
||||
//}
|
||||
//
|
||||
//func genCompName() string {
|
||||
// names := GenerateCompletionFlag.Names()
|
||||
// if len(names) == 0 {
|
||||
// return "generate-completion"
|
||||
// }
|
||||
// return names[0]
|
||||
//}
|
||||
//
|
||||
//// InitCompletionFlag generates completion code
|
||||
//var InitCompletionFlag = &StringFlag{
|
||||
// Name: "init-completion",
|
||||
// Usage: "generate completion code. Value must be 'bash' or 'zsh'",
|
||||
//}
|
||||
|
||||
// BashCompletionFlag enables bash-completion for all commands and subcommands
|
||||
var BashCompletionFlag Flag = &BoolFlag{
|
||||
Name: "generate-bash-completion",
|
||||
Hidden: true,
|
||||
}
|
||||
|
||||
func genCompName() string {
|
||||
names := GenerateCompletionFlag.Names()
|
||||
if len(names) == 0 {
|
||||
return "generate-completion"
|
||||
}
|
||||
return names[0]
|
||||
}
|
||||
|
||||
// InitCompletionFlag generates completion code
|
||||
var InitCompletionFlag = &StringFlag{
|
||||
Name: "init-completion",
|
||||
Usage: "generate completion code. Value must be 'bash' or 'zsh'",
|
||||
}
|
||||
|
||||
// VersionFlag prints the version for the application
|
||||
var VersionFlag Flag = &BoolFlag{
|
||||
Name: "version",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "print the version",
|
||||
Name: "version, v",
|
||||
Usage: "print the version",
|
||||
}
|
||||
|
||||
// HelpFlag prints the help for all commands and subcommands.
|
||||
// Set to nil to disable the flag. The subcommand
|
||||
// will still be added unless HideHelp is set to true.
|
||||
var HelpFlag Flag = &BoolFlag{
|
||||
Name: "help",
|
||||
Aliases: []string{"h"},
|
||||
Usage: "show help",
|
||||
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
|
||||
|
||||
// Serializeder is used to circumvent the limitations of flag.FlagSet.Set
|
||||
type Serializeder interface {
|
||||
Serialized() string
|
||||
// Serializer is used to circumvent the limitations of flag.FlagSet.Set
|
||||
type Serializer interface {
|
||||
Serialize() string
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@ -74,12 +90,16 @@ func (f FlagsByName) Len() int {
|
||||
}
|
||||
|
||||
func (f FlagsByName) Less(i, j int) bool {
|
||||
//<<<<<<< HEAD
|
||||
if len(f[j].Names()) == 0 {
|
||||
return false
|
||||
} else if len(f[i].Names()) == 0 {
|
||||
return true
|
||||
}
|
||||
return f[i].Names()[0] < f[j].Names()[0]
|
||||
//=======
|
||||
// return lexicographicLess(f[i].GetName(), f[j].GetName())
|
||||
//>>>>>>> master
|
||||
}
|
||||
|
||||
func (f FlagsByName) Swap(i, j int) {
|
||||
@ -92,711 +112,59 @@ func (f FlagsByName) Swap(i, j int) {
|
||||
type Flag interface {
|
||||
fmt.Stringer
|
||||
// Apply Flag settings to the given flag set
|
||||
Apply(*flag.FlagSet)
|
||||
Apply(*flag.FlagSet) error
|
||||
Names() []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 {
|
||||
// RequiredFlag is an interface that allows us to mark flags as required
|
||||
// it allows flags required flags to be backwards compatible with the Flag interface
|
||||
type RequiredFlag interface {
|
||||
Flag
|
||||
|
||||
ApplyWithError(*flag.FlagSet) error
|
||||
IsRequired() bool
|
||||
}
|
||||
|
||||
// DocGenerationFlag is an interface that allows documentation generation for the flag
|
||||
type DocGenerationFlag interface {
|
||||
Flag
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
TakesValue() bool
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
GetUsage() string
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
GetValue() string
|
||||
}
|
||||
|
||||
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)
|
||||
if err := f.Apply(set); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
set.SetOutput(ioutil.Discard)
|
||||
return set, nil
|
||||
}
|
||||
|
||||
// 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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
val.Set(envVal)
|
||||
break
|
||||
}
|
||||
}
|
||||
func eachName(longName string, fn func(string)) {
|
||||
parts := strings.Split(longName, ",")
|
||||
for _, name := range parts {
|
||||
name = strings.Trim(name, " ")
|
||||
fn(name)
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
set.Var(val, name, f.Usage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringSlice wraps a []string to satisfy flag.Value
|
||||
type StringSlice struct {
|
||||
slice []string
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// NewStringSlice creates a *StringSlice with default values
|
||||
func NewStringSlice(defaults ...string) *StringSlice {
|
||||
return &StringSlice{slice: append([]string{}, defaults...)}
|
||||
}
|
||||
|
||||
// Set appends the string value to the list of values
|
||||
func (f *StringSlice) Set(value string) error {
|
||||
if !f.hasBeenSet {
|
||||
f.slice = []string{}
|
||||
f.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice)
|
||||
f.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
f.slice = append(f.slice, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f *StringSlice) String() string {
|
||||
return fmt.Sprintf("%s", f.slice)
|
||||
}
|
||||
|
||||
// Serialized allows StringSlice to fulfill Serializeder
|
||||
func (f *StringSlice) Serialized() string {
|
||||
jsonBytes, _ := json.Marshal(f.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of strings set by this flag
|
||||
func (f *StringSlice) Value() []string {
|
||||
return f.slice
|
||||
}
|
||||
|
||||
// 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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
newVal := NewStringSlice()
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if err := newVal.Set(s); err != nil {
|
||||
return fmt.Errorf("could not parse %q as string value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.Value == nil {
|
||||
f.Value = NewStringSlice()
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IntSlice wraps an []int to satisfy flag.Value
|
||||
type IntSlice struct {
|
||||
slice []int
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// NewIntSlice makes an *IntSlice with default values
|
||||
func NewIntSlice(defaults ...int) *IntSlice {
|
||||
return &IntSlice{slice: append([]int{}, defaults...)}
|
||||
}
|
||||
|
||||
// NewInt64Slice makes an *Int64Slice with default values
|
||||
func NewInt64Slice(defaults ...int64) *Int64Slice {
|
||||
return &Int64Slice{slice: append([]int64{}, defaults...)}
|
||||
}
|
||||
|
||||
// SetInt directly adds an integer to the list of values
|
||||
func (i *IntSlice) SetInt(value int) {
|
||||
if !i.hasBeenSet {
|
||||
i.slice = []int{}
|
||||
i.hasBeenSet = true
|
||||
}
|
||||
|
||||
i.slice = append(i.slice, value)
|
||||
}
|
||||
|
||||
// Set parses the value into an integer and appends it to the list of values
|
||||
func (i *IntSlice) Set(value string) error {
|
||||
if !i.hasBeenSet {
|
||||
i.slice = []int{}
|
||||
i.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
|
||||
i.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp, err := strconv.ParseInt(value, 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.slice = append(i.slice, int(tmp))
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f *IntSlice) String() string {
|
||||
return fmt.Sprintf("%#v", f.slice)
|
||||
}
|
||||
|
||||
// Serialized allows IntSlice to fulfill Serializeder
|
||||
func (i *IntSlice) Serialized() string {
|
||||
jsonBytes, _ := json.Marshal(i.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of ints set by this flag
|
||||
func (i *IntSlice) Value() []int {
|
||||
return i.slice
|
||||
}
|
||||
|
||||
// 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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
newVal := NewIntSlice()
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if err := newVal.Set(s); err != nil {
|
||||
return fmt.Errorf("could not parse %q as int slice value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.Value == nil {
|
||||
f.Value = NewIntSlice()
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int64Slice is an opaque type for []int to satisfy flag.Value
|
||||
type Int64Slice struct {
|
||||
slice []int64
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// Set parses the value into an integer and appends it to the list of values
|
||||
func (f *Int64Slice) Set(value string) error {
|
||||
if !f.hasBeenSet {
|
||||
f.slice = []int64{}
|
||||
f.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice)
|
||||
f.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp, err := strconv.ParseInt(value, 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.slice = append(f.slice, tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f *Int64Slice) String() string {
|
||||
return fmt.Sprintf("%#v", f.slice)
|
||||
}
|
||||
|
||||
// Serialized allows Int64Slice to fulfill Serializeder
|
||||
func (f *Int64Slice) Serialized() string {
|
||||
jsonBytes, _ := json.Marshal(f.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of ints set by this flag
|
||||
func (f *Int64Slice) Value() []int64 {
|
||||
return f.slice
|
||||
}
|
||||
|
||||
// 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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
newVal := NewInt64Slice()
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if err := newVal.Set(s); err != nil {
|
||||
return fmt.Errorf("could not parse %q as int64 slice value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.Value == nil {
|
||||
f.Value = NewInt64Slice()
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
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 {
|
||||
if f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
if envVal == "" {
|
||||
f.Value = false
|
||||
break
|
||||
}
|
||||
|
||||
envValBool, err := strconv.ParseBool(envVal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q as bool value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
f.Value = envValBool
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.BoolVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Bool(name, f.Value, 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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
f.Value = envVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.StringVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.String(name, f.Value, f.Usage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
// Ignores errors
|
||||
func (f *PathFlag) Apply(set *flag.FlagSet) {
|
||||
f.ApplyWithError(set)
|
||||
}
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f *PathFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
f.Value = envVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.StringVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q as int value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
f.Value = int(envValInt)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.IntVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q as int value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
|
||||
f.Value = envValInt
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.Int64Var(f.Destination, name, f.Value, f.Usage)
|
||||
return nil
|
||||
}
|
||||
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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q as uint value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
|
||||
f.Value = uint(envValInt)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.UintVar(f.Destination, name, f.Value, f.Usage)
|
||||
return nil
|
||||
}
|
||||
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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q as uint64 value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
|
||||
f.Value = uint64(envValInt)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
|
||||
return nil
|
||||
}
|
||||
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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValDuration, err := time.ParseDuration(envVal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q as duration for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
|
||||
f.Value = envValDuration
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.DurationVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
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 f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q as float64 value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
|
||||
f.Value = float64(envValFloat)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.Float64Var(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Float64(name, f.Value, f.Usage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFloat64Slice makes a *Float64Slice with default values
|
||||
func NewFloat64Slice(defaults ...float64) *Float64Slice {
|
||||
return &Float64Slice{slice: append([]float64{}, defaults...)}
|
||||
}
|
||||
|
||||
// Float64Slice is an opaque type for []float64 to satisfy flag.Value
|
||||
type Float64Slice struct {
|
||||
slice []float64
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// Set parses the value into a float64 and appends it to the list of values
|
||||
func (f *Float64Slice) Set(value string) error {
|
||||
if !f.hasBeenSet {
|
||||
f.slice = []float64{}
|
||||
f.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice)
|
||||
f.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.slice = append(f.slice, tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f *Float64Slice) String() string {
|
||||
return fmt.Sprintf("%#v", f.slice)
|
||||
}
|
||||
|
||||
// Serialized allows Float64Slice to fulfill Serializeder
|
||||
func (f *Float64Slice) Serialized() string {
|
||||
jsonBytes, _ := json.Marshal(f.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of float64s set by this flag
|
||||
func (f *Float64Slice) Value() []float64 {
|
||||
return f.slice
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
// Ignores errors
|
||||
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) {
|
||||
f.ApplyWithError(set)
|
||||
}
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f *Float64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVars != nil {
|
||||
for _, envVar := range f.EnvVars {
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
newVal := NewFloat64Slice()
|
||||
for _, s := range strings.Split(envVal, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
err := newVal.Set(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(ErrWriter, err.Error())
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f.Value == nil {
|
||||
f.Value = NewFloat64Slice()
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func visibleFlags(fl []Flag) []Flag {
|
||||
visible := []Flag{}
|
||||
for _, flag := range fl {
|
||||
field := flagValue(flag).FieldByName("Hidden")
|
||||
var visible []Flag
|
||||
for _, f := range fl {
|
||||
field := flagValue(f).FieldByName("Hidden")
|
||||
if !field.IsValid() || !field.Bool() {
|
||||
visible = append(visible, flag)
|
||||
visible = append(visible, f)
|
||||
}
|
||||
}
|
||||
return visible
|
||||
@ -858,6 +226,7 @@ func withEnvHint(envVars []string, str string) string {
|
||||
suffix = "%"
|
||||
sep = "%, %"
|
||||
}
|
||||
|
||||
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(envVars, sep), suffix)
|
||||
}
|
||||
return str + envText
|
||||
@ -902,6 +271,14 @@ func flagStringField(f Flag, name string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -956,14 +333,14 @@ func stringifyFlag(f Flag) string {
|
||||
placeholder = defaultPlaceholder
|
||||
}
|
||||
|
||||
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
|
||||
usageWithDefault := strings.TrimSpace(usage + defaultValueString)
|
||||
|
||||
return withEnvHint(flagStringSliceField(f, "EnvVars"),
|
||||
fmt.Sprintf("%s\t%s", prefixedNames(f.Names(), placeholder), usageWithDefault))
|
||||
}
|
||||
|
||||
func stringifyIntSliceFlag(f *IntSliceFlag) string {
|
||||
defaultVals := []string{}
|
||||
var defaultVals []string
|
||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
||||
for _, i := range f.Value.Value() {
|
||||
defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
|
||||
@ -974,10 +351,10 @@ func stringifyIntSliceFlag(f *IntSliceFlag) string {
|
||||
}
|
||||
|
||||
func stringifyInt64SliceFlag(f *Int64SliceFlag) string {
|
||||
defaultVals := []string{}
|
||||
var defaultVals []string
|
||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
||||
for _, i := range f.Value.Value() {
|
||||
defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
|
||||
defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
|
||||
}
|
||||
}
|
||||
|
||||
@ -985,7 +362,8 @@ func stringifyInt64SliceFlag(f *Int64SliceFlag) string {
|
||||
}
|
||||
|
||||
func stringifyFloat64SliceFlag(f *Float64SliceFlag) string {
|
||||
defaultVals := []string{}
|
||||
var defaultVals []string
|
||||
|
||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
||||
for _, i := range f.Value.Value() {
|
||||
defaultVals = append(defaultVals, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%f", i), "0"), "."))
|
||||
@ -996,11 +374,12 @@ func stringifyFloat64SliceFlag(f *Float64SliceFlag) string {
|
||||
}
|
||||
|
||||
func stringifyStringSliceFlag(f *StringSliceFlag) string {
|
||||
defaultVals := []string{}
|
||||
var defaultVals []string
|
||||
|
||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
||||
for _, s := range f.Value.Value() {
|
||||
if len(s) > 0 {
|
||||
defaultVals = append(defaultVals, fmt.Sprintf("%q", s))
|
||||
defaultVals = append(defaultVals, strconv.Quote(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1032,3 +411,18 @@ func hasFlag(flags []Flag, fl Flag) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool) {
|
||||
for _, envVar := range envVars {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if val, ok := syscall.Getenv(envVar); ok {
|
||||
return val, true
|
||||
}
|
||||
}
|
||||
for _, fileVar := range strings.Split(filePath, ",") {
|
||||
if data, err := ioutil.ReadFile(fileVar); err == nil {
|
||||
return string(data), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
107
flag_bool.go
Normal file
107
flag_bool.go
Normal file
@ -0,0 +1,107 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// BoolFlag is a flag with type bool
|
||||
type BoolFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value bool
|
||||
DefaultText string
|
||||
Destination *bool
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *BoolFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *BoolFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *BoolFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *BoolFlag) TakesValue() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *BoolFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *BoolFlag) GetValue() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if val != "" {
|
||||
valBool, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
f.Value = valBool
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.BoolVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Bool(name, f.Value, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bool looks up the value of a local BoolFlag, returns
|
||||
// false if not found
|
||||
func (c *Context) Bool(name string) bool {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupBool(name, fs)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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
|
||||
//}
|
||||
|
||||
// TODO: Fix Duplicate
|
||||
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
|
||||
}
|
104
flag_duration.go
Normal file
104
flag_duration.go
Normal file
@ -0,0 +1,104 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
|
||||
type DurationFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value time.Duration
|
||||
DefaultText string
|
||||
Destination *time.Duration
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *DurationFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *DurationFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *DurationFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *DurationFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *DurationFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *DurationFlag) GetValue() string {
|
||||
return f.Value.String()
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if val != ""{
|
||||
valDuration, err := time.ParseDuration(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s as duration value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
f.Value = valDuration
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.DurationVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Duration(name, f.Value, f.Usage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Duration looks up the value of a local DurationFlag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Duration(name string) time.Duration {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupDuration(name, fs)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
// 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
|
||||
}
|
106
flag_float64.go
Normal file
106
flag_float64.go
Normal file
@ -0,0 +1,106 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Float64Flag is a flag with type float64
|
||||
type Float64Flag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value float64
|
||||
DefaultText string
|
||||
Destination *float64
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Float64Flag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Float64Flag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *Float64Flag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *Float64Flag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *Float64Flag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *Float64Flag) GetValue() string {
|
||||
return fmt.Sprintf("%f", f.Value)
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if val != "" {
|
||||
valFloat, err := strconv.ParseFloat(val, 10)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
f.Value = valFloat
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.Float64Var(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Float64(name, f.Value, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float64 looks up the value of a local Float64Flag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Float64(name string) float64 {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupFloat64(name, fs)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
164
flag_float64_slice.go
Normal file
164
flag_float64_slice.go
Normal file
@ -0,0 +1,164 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Float64Slice wraps []float64 to satisfy flag.Value
|
||||
type Float64Slice struct {
|
||||
slice []float64
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// NewFloat64Slice makes a *Float64Slice with default values
|
||||
func NewFloat64Slice(defaults ...float64) *Float64Slice {
|
||||
return &Float64Slice{slice: append([]float64{}, defaults...)}
|
||||
}
|
||||
|
||||
// Set parses the value into a float64 and appends it to the list of values
|
||||
func (f *Float64Slice) Set(value string) error {
|
||||
if !f.hasBeenSet {
|
||||
f.slice = []float64{}
|
||||
f.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &f.slice)
|
||||
f.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.slice = append(f.slice, tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (f *Float64Slice) String() string {
|
||||
return fmt.Sprintf("%#v", f.slice)
|
||||
}
|
||||
|
||||
// Serialize allows Float64Slice to fulfill Serializer
|
||||
func (f *Float64Slice) Serialize() string {
|
||||
jsonBytes, _ := json.Marshal(f.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of float64s set by this flag
|
||||
func (f *Float64Slice) Value() []float64 {
|
||||
return f.slice
|
||||
}
|
||||
|
||||
// Get returns the slice of float64s set by this flag
|
||||
func (f *Float64Slice) Get() interface{} {
|
||||
return *f
|
||||
}
|
||||
|
||||
// Float64SliceFlag is a flag with type *Float64Slice
|
||||
type Float64SliceFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value *Float64Slice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Float64SliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Float64SliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *Float64SliceFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true if the flag takes a value, otherwise false
|
||||
func (f *Float64SliceFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *Float64SliceFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *Float64SliceFlag) GetValue() string {
|
||||
if f.Value != nil {
|
||||
return f.Value.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
f.Value = &Float64Slice{}
|
||||
|
||||
for _, s := range strings.Split(val, ",") {
|
||||
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
|
||||
return fmt.Errorf("could not parse %s as float64 slice value for flag %s: %s", f.Value, f.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Value == nil {
|
||||
f.Value = &Float64Slice{}
|
||||
}
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Float64Slice looks up the value of a local Float64SliceFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) Float64Slice(name string) []float64 {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupFloat64Slice(name, fs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GlobalFloat64Slice looks up the value of a global Float64SliceFlag, returns
|
||||
// nil if not found
|
||||
//func (c *Context) GlobalFloat64Slice(name string) []int {
|
||||
// if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
||||
// return lookupFloat64Slice(name, fs)
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
|
||||
func lookupFloat64Slice(name string, set *flag.FlagSet) []float64 {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
parsed, err := (f.Value.(*Float64Slice)).Value(), error(nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,629 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WARNING: This file is generated!
|
||||
|
||||
// BoolFlag is a flag with type bool
|
||||
type BoolFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value bool
|
||||
DefaultText string
|
||||
|
||||
Destination *bool
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *BoolFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *BoolFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Bool looks up the value of a local BoolFlag, returns
|
||||
// false if not found
|
||||
func (c *Context) Bool(name string) bool {
|
||||
if fs := lookupFlagSet(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
|
||||
}
|
||||
|
||||
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
|
||||
type DurationFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value time.Duration
|
||||
DefaultText string
|
||||
|
||||
Destination *time.Duration
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *DurationFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *DurationFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Duration looks up the value of a local DurationFlag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Duration(name string) time.Duration {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value float64
|
||||
DefaultText string
|
||||
|
||||
Destination *float64
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Float64Flag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Float64Flag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Float64 looks up the value of a local Float64Flag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Float64(name string) float64 {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value Generic
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *GenericFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *GenericFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Generic looks up the value of a local GenericFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) Generic(name string) interface{} {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value int64
|
||||
DefaultText string
|
||||
|
||||
Destination *int64
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Int64Flag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Int64Flag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Int64 looks up the value of a local Int64Flag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Int64(name string) int64 {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value int
|
||||
DefaultText string
|
||||
|
||||
Destination *int
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *IntFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *IntFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Int looks up the value of a local IntFlag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Int(name string) int {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value *IntSlice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *IntSliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *IntSliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IntSlice looks up the value of a local IntSliceFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) IntSlice(name string) []int {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value *Int64Slice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Int64SliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Int64SliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Int64Slice looks up the value of a local Int64SliceFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) Int64Slice(name string) []int64 {
|
||||
if fs := lookupFlagSet(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
|
||||
}
|
||||
|
||||
// Float64SliceFlag is a flag with type *Float64Slice
|
||||
type Float64SliceFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value *Float64Slice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Float64SliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Float64SliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Float64Slice looks up the value of a local Float64SliceFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) Float64Slice(name string) []float64 {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupFloat64Slice(name, fs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupFloat64Slice(name string, set *flag.FlagSet) []float64 {
|
||||
f := set.Lookup(name)
|
||||
if f != nil {
|
||||
parsed, err := (f.Value.(*Float64Slice)).Value(), error(nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringFlag is a flag with type string
|
||||
type StringFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value string
|
||||
DefaultText string
|
||||
|
||||
Destination *string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *StringFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *StringFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// String looks up the value of a local StringFlag, returns
|
||||
// "" if not found
|
||||
func (c *Context) String(name string) string {
|
||||
if fs := lookupFlagSet(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 ""
|
||||
}
|
||||
|
||||
// PathFlag is a flag with type string
|
||||
type PathFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value string
|
||||
DefaultText string
|
||||
|
||||
Destination *string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *PathFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *PathFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Path looks up the value of a local PathFlag, returns
|
||||
// "" if not found
|
||||
func (c *Context) Path(name string) string {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupPath(name, fs)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func lookupPath(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value *StringSlice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *StringSliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *StringSliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// StringSlice looks up the value of a local StringSliceFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) StringSlice(name string) []string {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value uint64
|
||||
DefaultText string
|
||||
|
||||
Destination *uint64
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Uint64Flag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Uint64Flag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Uint64 looks up the value of a local Uint64Flag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Uint64(name string) uint64 {
|
||||
if fs := lookupFlagSet(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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value uint
|
||||
DefaultText string
|
||||
|
||||
Destination *uint
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *UintFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *UintFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// Uint looks up the value of a local UintFlag, returns
|
||||
// 0 if not found
|
||||
func (c *Context) Uint(name string) uint {
|
||||
if fs := lookupFlagSet(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
|
||||
}
|
104
flag_generic.go
Normal file
104
flag_generic.go
Normal file
@ -0,0 +1,104 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Generic is a generic parseable type identified by a specific flag
|
||||
type Generic interface {
|
||||
Set(value string) error
|
||||
String() string
|
||||
}
|
||||
|
||||
// GenericFlag is a flag with type Generic
|
||||
type GenericFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
TakesFile bool
|
||||
Value Generic
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *GenericFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *GenericFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *GenericFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *GenericFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *GenericFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *GenericFlag) GetValue() string {
|
||||
if f.Value != nil {
|
||||
return f.Value.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||
// provided by the user for parsing by the flag
|
||||
func (f GenericFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if err := f.Value.Set(val); err != nil {
|
||||
return fmt.Errorf("could not parse %s as value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
103
flag_int.go
Normal file
103
flag_int.go
Normal file
@ -0,0 +1,103 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// IntFlag is a flag with type int
|
||||
type IntFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value int
|
||||
DefaultText string
|
||||
Destination *int
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *IntFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *IntFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *IntFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *IntFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *IntFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *IntFlag) GetValue() string {
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *IntFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if val != "" {
|
||||
valInt, err := strconv.ParseInt(val, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s as int value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
f.Value = int(valInt)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.IntVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Int(name, f.Value, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
103
flag_int64.go
Normal file
103
flag_int64.go
Normal file
@ -0,0 +1,103 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Int64Flag is a flag with type int64
|
||||
type Int64Flag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value int64
|
||||
DefaultText string
|
||||
Destination *int64
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Int64Flag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Int64Flag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *Int64Flag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *Int64Flag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *Int64Flag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *Int64Flag) GetValue() string {
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *Int64Flag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if val != "" {
|
||||
valInt, err := strconv.ParseInt(val, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s as int value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
f.Value = valInt
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.Int64Var(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Int64(name, f.Value, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
164
flag_int64_slice.go
Normal file
164
flag_int64_slice.go
Normal file
@ -0,0 +1,164 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Int64Slice wraps []int64 to satisfy flag.Value
|
||||
type Int64Slice struct {
|
||||
slice []int64
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// NewInt64Slice makes an *Int64Slice with default values
|
||||
func NewInt64Slice(defaults ...int64) *Int64Slice {
|
||||
return &Int64Slice{slice: append([]int64{}, defaults...)}
|
||||
}
|
||||
|
||||
// Set parses the value into an integer and appends it to the list of values
|
||||
func (i *Int64Slice) Set(value string) error {
|
||||
if !i.hasBeenSet {
|
||||
i.slice = []int64{}
|
||||
i.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
|
||||
i.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp, err := strconv.ParseInt(value, 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.slice = append(i.slice, tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (i *Int64Slice) String() string {
|
||||
return fmt.Sprintf("%#v", i.slice)
|
||||
}
|
||||
|
||||
// Serialize allows Int64Slice to fulfill Serializer
|
||||
func (i *Int64Slice) Serialize() string {
|
||||
jsonBytes, _ := json.Marshal(i.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of ints set by this flag
|
||||
func (i *Int64Slice) Value() []int64 {
|
||||
return i.slice
|
||||
}
|
||||
|
||||
// Get returns the slice of ints set by this flag
|
||||
func (i *Int64Slice) Get() interface{} {
|
||||
return *i
|
||||
}
|
||||
|
||||
// Int64SliceFlag is a flag with type *Int64Slice
|
||||
type Int64SliceFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value *Int64Slice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Int64SliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Int64SliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *Int64SliceFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *Int64SliceFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f Int64SliceFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *Int64SliceFlag) GetValue() string {
|
||||
if f.Value != nil {
|
||||
return f.Value.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
f.Value = &Int64Slice{}
|
||||
|
||||
for _, s := range strings.Split(val, ",") {
|
||||
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
|
||||
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", f.Value, f.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Value == nil {
|
||||
f.Value = &Int64Slice{}
|
||||
}
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Int64Slice looks up the value of a local Int64SliceFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) Int64Slice(name string) []int64 {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupInt64Slice(name, fs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns
|
||||
// nil if not found
|
||||
//func (c *Context) GlobalInt64Slice(name string) []int {
|
||||
// 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
|
||||
}
|
175
flag_int_slice.go
Normal file
175
flag_int_slice.go
Normal file
@ -0,0 +1,175 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IntSlice wraps []int to satisfy flag.Value
|
||||
type IntSlice struct {
|
||||
slice []int
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// NewIntSlice makes an *IntSlice with default values
|
||||
func NewIntSlice(defaults ...int) *IntSlice {
|
||||
return &IntSlice{slice: append([]int{}, defaults...)}
|
||||
}
|
||||
|
||||
// TODO: Consistently have specific Set function for Int64 and Float64 ?
|
||||
// SetInt directly adds an integer to the list of values
|
||||
func (i *IntSlice) SetInt(value int) {
|
||||
if !i.hasBeenSet {
|
||||
i.slice = []int{}
|
||||
i.hasBeenSet = true
|
||||
}
|
||||
|
||||
i.slice = append(i.slice, value)
|
||||
}
|
||||
|
||||
// Set parses the value into an integer and appends it to the list of values
|
||||
func (i *IntSlice) Set(value string) error {
|
||||
if !i.hasBeenSet {
|
||||
i.slice = []int{}
|
||||
i.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &i.slice)
|
||||
i.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp, err := strconv.ParseInt(value, 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.slice = append(i.slice, int(tmp))
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (i *IntSlice) String() string {
|
||||
return fmt.Sprintf("%#v", i.slice)
|
||||
}
|
||||
|
||||
// Serialize allows IntSlice to fulfill Serializer
|
||||
func (i *IntSlice) Serialize() string {
|
||||
jsonBytes, _ := json.Marshal(i.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of ints set by this flag
|
||||
func (i *IntSlice) Value() []int {
|
||||
return i.slice
|
||||
}
|
||||
|
||||
// Get returns the slice of ints set by this flag
|
||||
func (i *IntSlice) Get() interface{} {
|
||||
return *i
|
||||
}
|
||||
|
||||
// IntSliceFlag is a flag with type *IntSlice
|
||||
type IntSliceFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value *IntSlice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *IntSliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *IntSliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *IntSliceFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *IntSliceFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f IntSliceFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *IntSliceFlag) GetValue() string {
|
||||
if f.Value != nil {
|
||||
return f.Value.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
f.Value = &IntSlice{}
|
||||
|
||||
for _, s := range strings.Split(val, ",") {
|
||||
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
|
||||
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Value == nil {
|
||||
f.Value = &IntSlice{}
|
||||
}
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IntSlice looks up the value of a local IntSliceFlag, returns
|
||||
// nil if not found
|
||||
func (c *Context) IntSlice(name string) []int {
|
||||
if fs := lookupFlagSet(name, c); fs != nil {
|
||||
return lookupIntSlice(name, c.flagSet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
85
flag_path.go
Normal file
85
flag_path.go
Normal file
@ -0,0 +1,85 @@
|
||||
package cli
|
||||
|
||||
import "flag"
|
||||
|
||||
type PathFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
TakesFile bool
|
||||
Value string
|
||||
DefaultText string
|
||||
Destination *string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *PathFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *PathFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *PathFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *PathFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *PathFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *PathFlag) GetValue() string {
|
||||
return f.Value
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *PathFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
f.Value = val
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.StringVar(f.Destination, name, f.Value, f.Usage)
|
||||
return
|
||||
}
|
||||
set.String(name, f.Value, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String looks up the value of a local PathFlag, returns
|
||||
// "" if not found
|
||||
func (c *Context) Path(name string) string {
|
||||
return lookupPath(name, c.flagSet)
|
||||
}
|
||||
|
||||
func lookupPath(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 ""
|
||||
}
|
95
flag_string.go
Normal file
95
flag_string.go
Normal file
@ -0,0 +1,95 @@
|
||||
package cli
|
||||
|
||||
import "flag"
|
||||
|
||||
// StringFlag is a flag with type string
|
||||
type StringFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
TakesFile bool
|
||||
Value string
|
||||
DefaultText string
|
||||
Destination *string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (s *StringFlag) String() string {
|
||||
return FlagStringer(s)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (s *StringFlag) Names() []string {
|
||||
return flagNames(s)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (s *StringFlag) IsRequired() bool {
|
||||
return s.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (s *StringFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (s *StringFlag) GetUsage() string {
|
||||
return s.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (s *StringFlag) GetValue() string {
|
||||
return s.Value
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (s *StringFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(s.EnvVars, s.FilePath); ok {
|
||||
s.Value = val
|
||||
}
|
||||
|
||||
for _, name := range s.Names() {
|
||||
if s.Destination != nil {
|
||||
set.StringVar(s.Destination, name, s.Value, s.Usage)
|
||||
return
|
||||
}
|
||||
set.String(name, s.Value, s.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 lookupPath(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 ""
|
||||
}
|
155
flag_string_slice.go
Normal file
155
flag_string_slice.go
Normal file
@ -0,0 +1,155 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StringSlice wraps a []string to satisfy flag.Value
|
||||
type StringSlice struct {
|
||||
slice []string
|
||||
hasBeenSet bool
|
||||
}
|
||||
|
||||
// NewStringSlice creates a *StringSlice with default values
|
||||
func NewStringSlice(defaults ...string) *StringSlice {
|
||||
return &StringSlice{slice: append([]string{}, defaults...)}
|
||||
}
|
||||
|
||||
// Set appends the string value to the list of values
|
||||
func (s *StringSlice) Set(value string) error {
|
||||
if !s.hasBeenSet {
|
||||
s.slice = []string{}
|
||||
s.hasBeenSet = true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(value, slPfx) {
|
||||
// Deserializing assumes overwrite
|
||||
_ = json.Unmarshal([]byte(strings.Replace(value, slPfx, "", 1)), &s.slice)
|
||||
s.hasBeenSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
s.slice = append(s.slice, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value (for usage defaults)
|
||||
func (s *StringSlice) String() string {
|
||||
return fmt.Sprintf("%s", s.slice)
|
||||
}
|
||||
|
||||
// Serialize allows StringSlice to fulfill Serializer
|
||||
func (s *StringSlice) Serialize() string {
|
||||
jsonBytes, _ := json.Marshal(s.slice)
|
||||
return fmt.Sprintf("%s%s", slPfx, string(jsonBytes))
|
||||
}
|
||||
|
||||
// Value returns the slice of strings set by this flag
|
||||
func (s *StringSlice) Value() []string {
|
||||
return s.slice
|
||||
}
|
||||
|
||||
// Get returns the slice of strings set by this flag
|
||||
func (s *StringSlice) Get() interface{} {
|
||||
return *s
|
||||
}
|
||||
|
||||
// StringSliceFlag is a flag with type *StringSlice
|
||||
type StringSliceFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
TakesFile bool
|
||||
Value *StringSlice
|
||||
DefaultText string
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *StringSliceFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *StringSliceFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *StringSliceFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *StringSliceFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *StringSliceFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *StringSliceFlag) GetValue() string {
|
||||
if f.Value != nil {
|
||||
return f.Value.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
f.Value = &StringSlice{}
|
||||
for _, s := range strings.Split(val, ",") {
|
||||
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
|
||||
return fmt.Errorf("could not parse %s as string value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Value == nil {
|
||||
f.Value = &StringSlice{}
|
||||
}
|
||||
set.Var(f.Value, name, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
546
flag_test.go
546
flag_test.go
File diff suppressed because it is too large
Load Diff
104
flag_uint.go
Normal file
104
flag_uint.go
Normal file
@ -0,0 +1,104 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// UintFlag is a flag with type uint
|
||||
type UintFlag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value uint
|
||||
DefaultText string
|
||||
Destination *uint
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *UintFlag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *UintFlag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *UintFlag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *UintFlag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *UintFlag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *UintFlag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if val != "" {
|
||||
valInt, err := strconv.ParseUint(val, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
|
||||
f.Value = uint(valInt)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.UintVar(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Uint(name, f.Value, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *UintFlag) GetValue() string {
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
104
flag_uint64.go
Normal file
104
flag_uint64.go
Normal file
@ -0,0 +1,104 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Uint64Flag is a flag with type uint64
|
||||
type Uint64Flag struct {
|
||||
Name string
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
FilePath string
|
||||
Required bool
|
||||
Hidden bool
|
||||
Value uint64
|
||||
DefaultText string
|
||||
Destination *uint64
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
// (for usage defaults)
|
||||
func (f *Uint64Flag) String() string {
|
||||
return FlagStringer(f)
|
||||
}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *Uint64Flag) Names() []string {
|
||||
return flagNames(f)
|
||||
}
|
||||
|
||||
// IsRequired returns whether or not the flag is required
|
||||
func (f *Uint64Flag) IsRequired() bool {
|
||||
return f.Required
|
||||
}
|
||||
|
||||
// TakesValue returns true of the flag takes a value, otherwise false
|
||||
func (f *Uint64Flag) TakesValue() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetUsage returns the usage string for the flag
|
||||
func (f *Uint64Flag) GetUsage() string {
|
||||
return f.Usage
|
||||
}
|
||||
|
||||
// Apply populates the flag given the flag set and environment
|
||||
func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
|
||||
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
|
||||
if val != "" {
|
||||
valInt, err := strconv.ParseUint(val, 0, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", val, f.Name, err)
|
||||
}
|
||||
|
||||
f.Value = valInt
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names() {
|
||||
if f.Destination != nil {
|
||||
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
|
||||
continue
|
||||
}
|
||||
set.Uint64(name, f.Value, f.Usage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValue returns the flags value as string representation and an empty
|
||||
// string if the flag takes no value at all.
|
||||
func (f *Uint64Flag) GetValue() string {
|
||||
return fmt.Sprintf("%d", f.Value)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
20
funcs.go
20
funcs.go
@ -1,7 +1,7 @@
|
||||
package cli
|
||||
|
||||
// ShellCompleteFunc is an action to execute when the shell completion flag is set
|
||||
type ShellCompleteFunc func(*Context)
|
||||
// BashCompleteFunc is an action to execute when the shell 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
|
||||
@ -23,6 +23,22 @@ type CommandNotFoundFunc func(*Context, string)
|
||||
// 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 []string, placeholder string) string
|
||||
|
||||
// FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help
|
||||
// with the environment variable details.
|
||||
type FlagEnvHintFunc func(envVars []string, 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
|
||||
|
@ -1,255 +0,0 @@
|
||||
#!/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
|
||||
|
||||
|
||||
_PY3 = sys.version_info.major == 3
|
||||
|
||||
|
||||
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 = _get_named_tmp_go()
|
||||
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 _get_named_tmp_go():
|
||||
tmp_args = dict(suffix='.go', mode='w', delete=False)
|
||||
if _PY3:
|
||||
tmp_args['encoding'] = 'utf-8'
|
||||
return tempfile.NamedTemporaryFile(**tmp_args)
|
||||
|
||||
|
||||
def _set_typedef_defaults(typedef):
|
||||
typedef.setdefault('doctail', '')
|
||||
typedef.setdefault('context_type', typedef['type'])
|
||||
typedef.setdefault('dest', 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
|
||||
Aliases []string
|
||||
Usage string
|
||||
EnvVars []string
|
||||
Hidden bool
|
||||
Value {type}
|
||||
DefaultText string
|
||||
""".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)
|
||||
}}
|
||||
|
||||
// Names returns the names of the flag
|
||||
func (f *{name}Flag) Names() []string {{
|
||||
return flagNames(f)
|
||||
}}
|
||||
|
||||
// {name} looks up the value of a local {name}Flag, returns
|
||||
// {context_default} if not found
|
||||
func (c *Context) {name}(name string) {context_type} {{
|
||||
if fs := lookupFlagSet(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.v2"
|
||||
|
||||
// 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=None, file=outfile)
|
||||
|
||||
|
||||
_WRITEFUNCS = {
|
||||
'cli': _write_cli_flag_types,
|
||||
'altsrc': _write_altsrc_flag_types
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
||||
module github.com/urfave/cli/v2
|
||||
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
14
go.sum
Normal file
14
go.sum
Normal file
@ -0,0 +1,14 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
126
help.go
126
help.go
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// AppHelpTemplate is the text template for the Default help topic.
|
||||
@ -89,7 +90,7 @@ var helpCommand = &Command{
|
||||
return ShowCommandHelp(c, args.First())
|
||||
}
|
||||
|
||||
ShowAppHelp(c)
|
||||
_ = ShowAppHelp(c)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@ -129,7 +130,7 @@ 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)
|
||||
_ = ShowAppHelp(c)
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
@ -153,19 +154,94 @@ func ShowAppHelp(c *Context) (err error) {
|
||||
|
||||
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
||||
func DefaultAppComplete(c *Context) {
|
||||
for _, command := range c.App.Commands {
|
||||
DefaultCompleteWithFlags(nil)(c)
|
||||
}
|
||||
|
||||
func printCommandSuggestions(commands []Command, writer io.Writer) {
|
||||
for _, command := range commands {
|
||||
if command.Hidden {
|
||||
continue
|
||||
}
|
||||
for _, name := range command.Names() {
|
||||
fmt.Fprintln(c.App.Writer, name)
|
||||
if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" {
|
||||
for _, name := range command.Names() {
|
||||
_, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage)
|
||||
}
|
||||
} else {
|
||||
for _, name := range command.Names() {
|
||||
_, _ = fmt.Fprintf(writer, "%s\n", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cliArgContains(flagName string) bool {
|
||||
for _, name := range strings.Split(flagName, ",") {
|
||||
name = strings.TrimSpace(name)
|
||||
count := utf8.RuneCountInString(name)
|
||||
if count > 2 {
|
||||
count = 2
|
||||
}
|
||||
flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name)
|
||||
for _, a := range os.Args {
|
||||
if a == flag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) {
|
||||
cur := strings.TrimPrefix(lastArg, "-")
|
||||
cur = strings.TrimPrefix(cur, "-")
|
||||
for _, flag := range flags {
|
||||
if bflag, ok := flag.(BoolFlag); ok && bflag.Hidden {
|
||||
continue
|
||||
}
|
||||
for _, name := range strings.Split(flag.GetName(), ",") {
|
||||
name = strings.TrimSpace(name)
|
||||
// this will get total count utf8 letters in flag name
|
||||
count := utf8.RuneCountInString(name)
|
||||
if count > 2 {
|
||||
count = 2 // resuse this count to generate single - or -- in flag completion
|
||||
}
|
||||
// if flag name has more than one utf8 letter and last argument in cli has -- prefix then
|
||||
// skip flag completion for short flags example -v or -x
|
||||
if strings.HasPrefix(lastArg, "--") && count == 1 {
|
||||
continue
|
||||
}
|
||||
// match if last argument matches this flag and it is not repeated
|
||||
if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(flag.GetName()) {
|
||||
flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name)
|
||||
_, _ = fmt.Fprintln(writer, flagCompletion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultCompleteWithFlags(cmd *Command) func(c *Context) {
|
||||
return func(c *Context) {
|
||||
if len(os.Args) > 2 {
|
||||
lastArg := os.Args[len(os.Args)-2]
|
||||
if strings.HasPrefix(lastArg, "-") {
|
||||
printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer)
|
||||
if cmd != nil {
|
||||
printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if cmd != nil {
|
||||
printCommandSuggestions(cmd.Subcommands, c.App.Writer)
|
||||
} else {
|
||||
printCommandSuggestions(c.App.Commands, c.App.Writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ShowCommandHelpAndExit - exits with code after showing help
|
||||
func ShowCommandHelpAndExit(c *Context, command string, code int) {
|
||||
ShowCommandHelp(c, command)
|
||||
_ = ShowCommandHelp(c, command)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
@ -215,49 +291,57 @@ func ShowVersion(c *Context) {
|
||||
}
|
||||
|
||||
func printVersion(c *Context) {
|
||||
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
|
||||
_, _ = 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.ShellComplete != nil {
|
||||
a.ShellComplete(c)
|
||||
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)
|
||||
//TODO: Resolve
|
||||
//<<<<<<< HEAD
|
||||
if c != nil && c.ShellComplete != nil {
|
||||
c.ShellComplete(ctx)
|
||||
//=======
|
||||
// if c != nil {
|
||||
// if c.BashComplete != nil {
|
||||
// c.BashComplete(ctx)
|
||||
// } else {
|
||||
// DefaultCompleteWithFlags(c)(ctx)
|
||||
// }
|
||||
//>>>>>>> master
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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))
|
||||
|
||||
errDebug := os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != ""
|
||||
|
||||
err := t.Execute(w, data)
|
||||
if err != nil {
|
||||
if errDebug {
|
||||
fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
|
||||
// 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()
|
||||
_ = w.Flush()
|
||||
}
|
||||
|
||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||
@ -286,7 +370,7 @@ func checkHelp(c *Context) bool {
|
||||
|
||||
func checkCommandHelp(c *Context, name string) bool {
|
||||
if c.Bool("h") || c.Bool("help") {
|
||||
ShowCommandHelp(c, name)
|
||||
_ = ShowCommandHelp(c, name)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -295,7 +379,7 @@ func checkCommandHelp(c *Context, name string) bool {
|
||||
|
||||
func checkSubcommandHelp(c *Context) bool {
|
||||
if c.Bool("h") || c.Bool("help") {
|
||||
ShowSubcommandHelp(c)
|
||||
_ = ShowSubcommandHelp(c)
|
||||
return true
|
||||
}
|
||||
|
||||
|
46
help_test.go
46
help_test.go
@ -15,9 +15,9 @@ func Test_ShowAppHelp_NoAuthor(t *testing.T) {
|
||||
|
||||
c := NewContext(app, nil, nil)
|
||||
|
||||
ShowAppHelp(c)
|
||||
_ = ShowAppHelp(c)
|
||||
|
||||
if bytes.Index(output.Bytes(), []byte("AUTHOR(S):")) != -1 {
|
||||
if bytes.Contains(output.Bytes(), []byte("AUTHOR(S):")) {
|
||||
t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):")
|
||||
}
|
||||
}
|
||||
@ -30,9 +30,9 @@ func Test_ShowAppHelp_NoVersion(t *testing.T) {
|
||||
|
||||
c := NewContext(app, nil, nil)
|
||||
|
||||
ShowAppHelp(c)
|
||||
_ = ShowAppHelp(c)
|
||||
|
||||
if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 {
|
||||
if bytes.Contains(output.Bytes(), []byte("VERSION:")) {
|
||||
t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:")
|
||||
}
|
||||
}
|
||||
@ -45,9 +45,9 @@ func Test_ShowAppHelp_HideVersion(t *testing.T) {
|
||||
|
||||
c := NewContext(app, nil, nil)
|
||||
|
||||
ShowAppHelp(c)
|
||||
_ = ShowAppHelp(c)
|
||||
|
||||
if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 {
|
||||
if bytes.Contains(output.Bytes(), []byte("VERSION:")) {
|
||||
t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:")
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@ func Test_Help_Custom_Flags(t *testing.T) {
|
||||
}
|
||||
output := new(bytes.Buffer)
|
||||
app.Writer = output
|
||||
app.Run([]string{"test", "-h"})
|
||||
_ = app.Run([]string{"test", "-h"})
|
||||
if output.Len() > 0 {
|
||||
t.Errorf("unexpected output: %s", output.String())
|
||||
}
|
||||
@ -108,7 +108,7 @@ func Test_Version_Custom_Flags(t *testing.T) {
|
||||
}
|
||||
output := new(bytes.Buffer)
|
||||
app.Writer = output
|
||||
app.Run([]string{"test", "-v"})
|
||||
_ = app.Run([]string{"test", "-v"})
|
||||
if output.Len() > 0 {
|
||||
t.Errorf("unexpected output: %s", output.String())
|
||||
}
|
||||
@ -118,7 +118,7 @@ func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) {
|
||||
app := &App{}
|
||||
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Parse([]string{"foo"})
|
||||
_ = set.Parse([]string{"foo"})
|
||||
|
||||
c := NewContext(app, set, nil)
|
||||
|
||||
@ -128,9 +128,9 @@ func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) {
|
||||
t.Fatalf("expected error from helpCommand.Action(), but got nil")
|
||||
}
|
||||
|
||||
exitErr, ok := err.(*exitError)
|
||||
exitErr, ok := err.(*ExitError)
|
||||
if !ok {
|
||||
t.Fatalf("expected *exitError from helpCommand.Action(), but instead got: %v", err.Error())
|
||||
t.Fatalf("expected *ExitError from helpCommand.Action(), but instead got: %v", err.Error())
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
|
||||
@ -146,7 +146,7 @@ func Test_helpCommand_InHelpOutput(t *testing.T) {
|
||||
app := &App{}
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"test", "--help"})
|
||||
_ = app.Run([]string{"test", "--help"})
|
||||
|
||||
s := output.String()
|
||||
|
||||
@ -163,7 +163,7 @@ func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) {
|
||||
app := &App{}
|
||||
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Parse([]string{"foo"})
|
||||
_ = set.Parse([]string{"foo"})
|
||||
|
||||
c := NewContext(app, set, nil)
|
||||
|
||||
@ -173,9 +173,9 @@ func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) {
|
||||
t.Fatalf("expected error from helpCommand.Action(), but got nil")
|
||||
}
|
||||
|
||||
exitErr, ok := err.(*exitError)
|
||||
exitErr, ok := err.(*ExitError)
|
||||
if !ok {
|
||||
t.Fatalf("expected *exitError from helpCommand.Action(), but instead got: %v", err.Error())
|
||||
t.Fatalf("expected *ExitError from helpCommand.Action(), but instead got: %v", err.Error())
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
|
||||
@ -202,7 +202,7 @@ func TestShowAppHelp_CommandAliases(t *testing.T) {
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"foo", "--help"})
|
||||
_ = app.Run([]string{"foo", "--help"})
|
||||
|
||||
if !strings.Contains(output.String(), "frobbly, fr, frob") {
|
||||
t.Errorf("expected output to include all command aliases; got: %q", output.String())
|
||||
@ -224,7 +224,7 @@ func TestShowCommandHelp_CommandAliases(t *testing.T) {
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"foo", "help", "fr"})
|
||||
_ = app.Run([]string{"foo", "help", "fr"})
|
||||
|
||||
if !strings.Contains(output.String(), "frobbly") {
|
||||
t.Errorf("expected output to include command name; got: %q", output.String())
|
||||
@ -250,7 +250,7 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"foo", "help"})
|
||||
_ = app.Run([]string{"foo", "help"})
|
||||
|
||||
if !strings.Contains(output.String(), "frobbly, fr, frob, bork") {
|
||||
t.Errorf("expected output to include all command aliases; got: %q", output.String())
|
||||
@ -284,7 +284,7 @@ EXAMPLES:
|
||||
}
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"foo", "help", "frobbly"})
|
||||
_ = app.Run([]string{"foo", "help", "frobbly"})
|
||||
|
||||
if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") {
|
||||
t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String())
|
||||
@ -312,7 +312,7 @@ func TestShowSubcommandHelp_CommandUsageText(t *testing.T) {
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
|
||||
app.Run([]string{"foo", "frobbly", "--help"})
|
||||
_ = app.Run([]string{"foo", "frobbly", "--help"})
|
||||
|
||||
if !strings.Contains(output.String(), "this is usage text") {
|
||||
t.Errorf("expected output to include usage text; got: %q", output.String())
|
||||
@ -336,7 +336,7 @@ func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) {
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"foo", "frobbly", "bobbly", "--help"})
|
||||
_ = app.Run([]string{"foo", "frobbly", "bobbly", "--help"})
|
||||
|
||||
if !strings.Contains(output.String(), "this is usage text") {
|
||||
t.Errorf("expected output to include usage text; got: %q", output.String())
|
||||
@ -364,7 +364,7 @@ func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"app", "--help"})
|
||||
_ = app.Run([]string{"app", "--help"})
|
||||
|
||||
if strings.Contains(output.String(), "secretfrob") {
|
||||
t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
|
||||
@ -422,7 +422,7 @@ VERSION:
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
app.Writer = output
|
||||
app.Run([]string{"app", "--help"})
|
||||
_ = app.Run([]string{"app", "--help"})
|
||||
|
||||
if strings.Contains(output.String(), "secretfrob") {
|
||||
t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
|
||||
|
@ -1,9 +0,0 @@
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package cli
|
||||
|
||||
import "os"
|
||||
|
||||
func clearenv() {
|
||||
os.Clearenv()
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// os.Clearenv() doesn't actually unset variables on Windows
|
||||
// See: https://github.com/golang/go/issues/17902
|
||||
func clearenv() {
|
||||
for _, s := range os.Environ() {
|
||||
for j := 1; j < len(s); j++ {
|
||||
if s[j] == '=' {
|
||||
keyp, _ := syscall.UTF16PtrFromString(s[0:j])
|
||||
syscall.SetEnvironmentVariable(keyp, nil)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
80
parse.go
Normal file
80
parse.go
Normal file
@ -0,0 +1,80 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type iterativeParser interface {
|
||||
newFlagSet() (*flag.FlagSet, error)
|
||||
useShortOptionHandling() bool
|
||||
}
|
||||
|
||||
// To enable short-option handling (e.g., "-it" vs "-i -t") we have to
|
||||
// iteratively catch parsing errors. This way we achieve LR parsing without
|
||||
// transforming any arguments. Otherwise, there is no way we can discriminate
|
||||
// combined short options from common arguments that should be left untouched.
|
||||
func parseIter(ip iterativeParser, args []string) (*flag.FlagSet, error) {
|
||||
for {
|
||||
set, err := ip.newFlagSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = set.Parse(args)
|
||||
if !ip.useShortOptionHandling() || err == nil {
|
||||
return set, err
|
||||
}
|
||||
|
||||
errStr := err.Error()
|
||||
trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ")
|
||||
if errStr == trimmed {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// regenerate the initial args with the split short opts
|
||||
newArgs := []string{}
|
||||
for i, arg := range args {
|
||||
if arg != trimmed {
|
||||
newArgs = append(newArgs, arg)
|
||||
continue
|
||||
}
|
||||
|
||||
shortOpts := splitShortOptions(set, trimmed)
|
||||
if len(shortOpts) == 1 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add each short option and all remaining arguments
|
||||
newArgs = append(newArgs, shortOpts...)
|
||||
newArgs = append(newArgs, args[i+1:]...)
|
||||
args = newArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func splitShortOptions(set *flag.FlagSet, arg string) []string {
|
||||
shortFlagsExist := func(s string) bool {
|
||||
for _, c := range s[1:] {
|
||||
if f := set.Lookup(string(c)); f == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if !isSplittable(arg) || !shortFlagsExist(arg) {
|
||||
return []string{arg}
|
||||
}
|
||||
|
||||
separated := make([]string, 0, len(arg)-1)
|
||||
for _, flagChar := range arg[1:] {
|
||||
separated = append(separated, "-"+string(flagChar))
|
||||
}
|
||||
|
||||
return separated
|
||||
}
|
||||
|
||||
func isSplittable(flagArg string) bool {
|
||||
return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2
|
||||
}
|
172
runtests
172
runtests
@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import codecs
|
||||
import glob
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from subprocess import check_call, check_output
|
||||
|
||||
|
||||
_PY3 = sys.version_info.major == 3
|
||||
_WINDOWS = platform.system().lower() == 'windows'
|
||||
_PACKAGE_NAME = os.environ.get(
|
||||
'CLI_PACKAGE_NAME', 'github.com/urfave/cli'
|
||||
)
|
||||
_TARGETS = {}
|
||||
|
||||
|
||||
def main(sysargs=sys.argv[:]):
|
||||
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 _target(func):
|
||||
_TARGETS[func.__name__.strip('_')] = func
|
||||
return func
|
||||
|
||||
|
||||
@_target
|
||||
def _test():
|
||||
if _go_version() < '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)
|
||||
|
||||
|
||||
@_target
|
||||
def _gfmrun():
|
||||
go_version = _go_version()
|
||||
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'])
|
||||
|
||||
|
||||
@_target
|
||||
def _vet():
|
||||
_run('go vet ./...')
|
||||
|
||||
|
||||
@_target
|
||||
def _migrations():
|
||||
go_version = _go_version()
|
||||
if go_version < 'go1.3':
|
||||
print('runtests: skip on {}'.format(go_version), file=sys.stderr)
|
||||
return
|
||||
|
||||
migration_script = os.path.abspath(
|
||||
os.environ.get('V1TOV2', './cli-v1-to-v2')
|
||||
)
|
||||
v1_readme_url = os.environ.get(
|
||||
'V1README',
|
||||
'https://raw.githubusercontent.com/urfave/cli/v1/README.md'
|
||||
)
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
_run('curl -sSL -o README.md {}'.format(v1_readme_url).split())
|
||||
_run('gfmrun extract -o .'.split())
|
||||
|
||||
for gofile in glob.glob('*.go'):
|
||||
for i in (0, 1):
|
||||
_run(['python', migration_script, '-w', gofile])
|
||||
_run('go build -o tmp.out {}'.format(gofile).split())
|
||||
finally:
|
||||
if os.environ.get('NOCLEAN', '') == '':
|
||||
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||
|
||||
|
||||
@_target
|
||||
def _toc():
|
||||
exe = ['bash'] if _WINDOWS else []
|
||||
_run(exe + [
|
||||
os.path.join('node_modules', '.bin', 'markdown-toc'),
|
||||
'-i', 'README.md'
|
||||
])
|
||||
_run('git diff --exit-code')
|
||||
|
||||
|
||||
@_target
|
||||
def _gen():
|
||||
go_version = _go_version()
|
||||
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)
|
||||
sys.stderr.flush()
|
||||
check_call(command)
|
||||
|
||||
|
||||
def _gfmrun_count():
|
||||
with codecs.open('README.md', 'r', 'utf-8') as infile:
|
||||
lines = infile.read().splitlines()
|
||||
return len(list(filter(_is_go_runnable, lines)))
|
||||
|
||||
|
||||
def _is_go_runnable(line):
|
||||
return line.startswith('package main')
|
||||
|
||||
|
||||
def _go_version():
|
||||
return check_output('go version'.split()).decode('utf-8').split()[2]
|
||||
|
||||
|
||||
def _combine_coverprofiles(coverprofiles):
|
||||
tmp_args = dict(suffix='.coverprofile', mode='w', delete=False)
|
||||
if _PY3:
|
||||
tmp_args['encoding'] = 'utf-8'
|
||||
|
||||
combined = tempfile.NamedTemporaryFile(**tmp_args)
|
||||
combined.write('mode: set\n')
|
||||
|
||||
for coverprofile in coverprofiles:
|
||||
with codecs.open(coverprofile, 'r', 'utf-8') 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())
|
29
sort.go
Normal file
29
sort.go
Normal file
@ -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
|
||||
}
|
30
sort_test.go
Normal file
30
sort_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
package cli
|
||||
|
||||
import "testing"
|
||||
|
||||
var lexicographicLessTests = []struct {
|
||||
i string
|
||||
j string
|
||||
expected bool
|
||||
}{
|
||||
{"", "a", true},
|
||||
{"a", "", false},
|
||||
{"a", "a", false},
|
||||
{"a", "A", false},
|
||||
{"A", "a", true},
|
||||
{"aa", "a", false},
|
||||
{"a", "aa", true},
|
||||
{"a", "b", true},
|
||||
{"a", "B", true},
|
||||
{"A", "b", true},
|
||||
{"A", "B", true},
|
||||
}
|
||||
|
||||
func TestLexicographicLess(t *testing.T) {
|
||||
for _, test := range lexicographicLessTests {
|
||||
actual := lexicographicLess(test.i, test.j)
|
||||
if test.expected != actual {
|
||||
t.Errorf(`expected string "%s" to come before "%s"`, test.i, test.j)
|
||||
}
|
||||
}
|
||||
}
|
121
template.go
Normal file
121
template.go
Normal file
@ -0,0 +1,121 @@
|
||||
package cli
|
||||
|
||||
// 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}}:{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
||||
{{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}
|
||||
|
||||
COPYRIGHT:
|
||||
{{.Copyright}}{{end}}
|
||||
`
|
||||
|
||||
// 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}}:{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
|
||||
OPTIONS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
`
|
||||
|
||||
var MarkdownDocTemplate = `% {{ .App.Name }}(8) {{ .App.Description }}
|
||||
|
||||
% {{ .App.Author }}
|
||||
|
||||
# NAME
|
||||
|
||||
{{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }}
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
{{ .App.Name }}
|
||||
{{ if .SynopsisArgs }}
|
||||
` + "```" + `
|
||||
{{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + `
|
||||
{{ end }}{{ if .App.UsageText }}
|
||||
# DESCRIPTION
|
||||
|
||||
{{ .App.UsageText }}
|
||||
{{ end }}
|
||||
**Usage**:
|
||||
|
||||
` + "```" + `
|
||||
{{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
|
||||
` + "```" + `
|
||||
{{ if .GlobalArgs }}
|
||||
# GLOBAL OPTIONS
|
||||
{{ range $v := .GlobalArgs }}
|
||||
{{ $v }}{{ end }}
|
||||
{{ end }}{{ if .Commands }}
|
||||
# COMMANDS
|
||||
{{ range $v := .Commands }}
|
||||
{{ $v }}{{ end }}{{ end }}`
|
||||
|
||||
var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion
|
||||
|
||||
function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }}
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
{{ range $v := .Completions }}{{ $v }}
|
||||
{{ end }}`
|
80
testdata/expected-doc-full.man
vendored
Normal file
80
testdata/expected-doc-full.man
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
.nh
|
||||
.TH greet(8)
|
||||
|
||||
.SH Harrison
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
greet \- Some app
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
greet
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
[\-\-another\-flag|\-b]
|
||||
[\-\-flag|\-\-fl|\-f]=[value]
|
||||
[\-\-socket|\-s]=[value]
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
app [first\_arg] [second\_arg]
|
||||
|
||||
.PP
|
||||
\fBUsage\fP:
|
||||
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
||||
.SH GLOBAL OPTIONS
|
||||
.PP
|
||||
\fB\-\-another\-flag, \-b\fP: another usage text
|
||||
|
||||
.PP
|
||||
\fB\-\-flag, \-\-fl, \-f\fP="":
|
||||
|
||||
.PP
|
||||
\fB\-\-socket, \-s\fP="": some 'usage' text (default: value)
|
||||
|
||||
|
||||
.SH COMMANDS
|
||||
.SH config, c
|
||||
.PP
|
||||
another usage test
|
||||
|
||||
.PP
|
||||
\fB\-\-another\-flag, \-b\fP: another usage text
|
||||
|
||||
.PP
|
||||
\fB\-\-flag, \-\-fl, \-f\fP="":
|
||||
|
||||
.SS sub\-config, s, ss
|
||||
.PP
|
||||
another usage test
|
||||
|
||||
.PP
|
||||
\fB\-\-sub\-command\-flag, \-s\fP: some usage text
|
||||
|
||||
.PP
|
||||
\fB\-\-sub\-flag, \-\-sub\-fl, \-s\fP="":
|
||||
|
||||
.SH info, i, in
|
||||
.PP
|
||||
retrieve generic information
|
||||
|
||||
.SH some\-command
|
62
testdata/expected-doc-full.md
vendored
Normal file
62
testdata/expected-doc-full.md
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
% greet(8)
|
||||
|
||||
% Harrison
|
||||
|
||||
# NAME
|
||||
|
||||
greet - Some app
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
greet
|
||||
|
||||
```
|
||||
[--another-flag|-b]
|
||||
[--flag|--fl|-f]=[value]
|
||||
[--socket|-s]=[value]
|
||||
```
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
app [first_arg] [second_arg]
|
||||
|
||||
**Usage**:
|
||||
|
||||
```
|
||||
greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
|
||||
```
|
||||
|
||||
# GLOBAL OPTIONS
|
||||
|
||||
**--another-flag, -b**: another usage text
|
||||
|
||||
**--flag, --fl, -f**="":
|
||||
|
||||
**--socket, -s**="": some 'usage' text (default: value)
|
||||
|
||||
|
||||
# COMMANDS
|
||||
|
||||
## config, c
|
||||
|
||||
another usage test
|
||||
|
||||
**--another-flag, -b**: another usage text
|
||||
|
||||
**--flag, --fl, -f**="":
|
||||
|
||||
### sub-config, s, ss
|
||||
|
||||
another usage test
|
||||
|
||||
**--sub-command-flag, -s**: some usage text
|
||||
|
||||
**--sub-flag, --sub-fl, -s**="":
|
||||
|
||||
## info, i, in
|
||||
|
||||
retrieve generic information
|
||||
|
||||
## some-command
|
||||
|
||||
|
36
testdata/expected-doc-no-commands.md
vendored
Normal file
36
testdata/expected-doc-no-commands.md
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
% greet(8)
|
||||
|
||||
% Harrison
|
||||
|
||||
# NAME
|
||||
|
||||
greet - Some app
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
greet
|
||||
|
||||
```
|
||||
[--another-flag|-b]
|
||||
[--flag|--fl|-f]=[value]
|
||||
[--socket|-s]=[value]
|
||||
```
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
app [first_arg] [second_arg]
|
||||
|
||||
**Usage**:
|
||||
|
||||
```
|
||||
greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
|
||||
```
|
||||
|
||||
# GLOBAL OPTIONS
|
||||
|
||||
**--another-flag, -b**: another usage text
|
||||
|
||||
**--flag, --fl, -f**="":
|
||||
|
||||
**--socket, -s**="": some 'usage' text (default: value)
|
||||
|
47
testdata/expected-doc-no-flags.md
vendored
Normal file
47
testdata/expected-doc-no-flags.md
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
% greet(8)
|
||||
|
||||
% Harrison
|
||||
|
||||
# NAME
|
||||
|
||||
greet - Some app
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
greet
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
app [first_arg] [second_arg]
|
||||
|
||||
**Usage**:
|
||||
|
||||
```
|
||||
greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
|
||||
```
|
||||
|
||||
# COMMANDS
|
||||
|
||||
## config, c
|
||||
|
||||
another usage test
|
||||
|
||||
**--another-flag, -b**: another usage text
|
||||
|
||||
**--flag, --fl, -f**="":
|
||||
|
||||
### sub-config, s, ss
|
||||
|
||||
another usage test
|
||||
|
||||
**--sub-command-flag, -s**: some usage text
|
||||
|
||||
**--sub-flag, --sub-fl, -s**="":
|
||||
|
||||
## info, i, in
|
||||
|
||||
retrieve generic information
|
||||
|
||||
## some-command
|
||||
|
||||
|
28
testdata/expected-fish-full.fish
vendored
Normal file
28
testdata/expected-fish-full.fish
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# greet fish shell completion
|
||||
|
||||
function __fish_greet_no_subcommand --description 'Test if there has been any subcommand yet'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i config c sub-config s ss info i in some-command
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
complete -c greet -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some \'usage\' text'
|
||||
complete -c greet -n '__fish_greet_no_subcommand' -f -l flag -s fl -s f -r
|
||||
complete -c greet -n '__fish_greet_no_subcommand' -f -l another-flag -s b -d 'another usage text'
|
||||
complete -c greet -n '__fish_greet_no_subcommand' -f -l help -s h -d 'show help'
|
||||
complete -c greet -n '__fish_greet_no_subcommand' -f -l version -s v -d 'print the version'
|
||||
complete -c greet -n '__fish_seen_subcommand_from config c' -f -l help -s h -d 'show help'
|
||||
complete -r -c greet -n '__fish_greet_no_subcommand' -a 'config c' -d 'another usage test'
|
||||
complete -c greet -n '__fish_seen_subcommand_from config c' -l flag -s fl -s f -r
|
||||
complete -c greet -n '__fish_seen_subcommand_from config c' -f -l another-flag -s b -d 'another usage text'
|
||||
complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l help -s h -d 'show help'
|
||||
complete -r -c greet -n '__fish_seen_subcommand_from config c' -a 'sub-config s ss' -d 'another usage test'
|
||||
complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-flag -s sub-fl -s s -r
|
||||
complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-command-flag -s s -d 'some usage text'
|
||||
complete -c greet -n '__fish_seen_subcommand_from info i in' -f -l help -s h -d 'show help'
|
||||
complete -r -c greet -n '__fish_greet_no_subcommand' -a 'info i in' -d 'retrieve generic information'
|
||||
complete -c greet -n '__fish_seen_subcommand_from some-command' -f -l help -s h -d 'show help'
|
||||
complete -r -c greet -n '__fish_greet_no_subcommand' -a 'some-command'
|
Loading…
Reference in New Issue
Block a user