I think It's more obvious now to understand the inheritance of flags.
Fix#403Fix#404
Performance improvements:
benchmark old ns/op new ns/op delta
BenchmarkInheritedFlags-4 6536 5595 -14.40%
BenchmarkLocalFlags-4 3193 3235 +1.32%
benchmark old allocs new allocs delta
BenchmarkInheritedFlags-4 49 39 -20.41%
BenchmarkLocalFlags-4 23 21 -8.70%
benchmark old bytes new bytes delta
BenchmarkInheritedFlags-4 2040 1000 -50.98%
BenchmarkLocalFlags-4 1008 544 -46.03%
* Fix shellcheck
Before this change:
In - line 204:
declare -F $next_command >/dev/null && $next_command
^-- SC2086: Double quote to prevent globbing and word splitting.
--- FAIL: TestBashCompletions (0.34s)
bash_completions_test.go:138: shellcheck failed: exit status 1
* Avoid storing pointer to nil
Before this change, the new test fails with:
--- FAIL: TestSetOutput (0.00s)
command_test.go:198: expected setting output to nil to revert back to stdout, got <nil>
If one ran a command like
./root --boolFlag subcmd1 subcmd2
Thing worked fine. The code recognized that --boolFlag followed by a
space meant the next word was not the argument to --boolFlag. But other
flag types with a NoOptDefValue (like a Count flag) would not ignore the
"argument". On a command like:
./root --countflag subcmd1 subcmd2
The processor, when looking for a subcommand, would first throw out the
`--countflag subcmd1` and then look for subcmd2 under root.
The fix is to ignore the next word after any NoOptDefVal flag, not just
boolean flags.
The default pflag error is to only print the bad flag. This enables an application
to include a usage message or other details about the error.
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
If a user specifies a flag to a command which doesn't make sense to a
subcommand do not show subcommands as a suggestion.
This also changes things to show both 'required flags' and 'commands'
instead of only 'required flags'
If a command has one flag which is hidden, it should not, for
instance, show the `Flags: ` heading. Likewise there are other
items in the help template which should respect hidden/deprecated
state.
* Moving final return outside of if-else
* Removing type declarations that Go can infer from values
* Cleaning up some existing comments
* Changing snake_case variables to camelCase
so that full path to the executable or a renamed executable
parses command-line arguments correctly as before.
Special thanks to @apriendeau for discovering "go test -v" failing
and for providing the initial workaround, see #155 and subsequent
discussions.
The flags usage template from pflags has a trailing \n. We need to
include a newline in case there are no flags in our template. This will
trim the newline from the end of the flags from pflag and we can do it
right outselves.
This slightly changes IsAvailableCommand in that a non-runnable command
with a runnable subcommand is now 'Available'
We also use IsAvailableCommand in the rest of the codebase instead of
half kinda sorta doing it incorrectly other places.
Added the ability to have hidden commands that cobra will still run as intended, however they won't show up in any usage/help text
adding internal field to command
private is a better name
hiding private commands in default help/usage
opting for 'hidden' over 'private'
updating all 'help command' checks to exclude hidden commands
updating how commands are displayed in usage/help text by updating/adding some methods. added tests for hidden/deprecated commands
making command hidden when testing hidden command execution
test now leverage the included suite and are much less custom. also removed deprecation tests, once I discovered them in cobra_test.go
updating hidden command test to be more reliable
removing unnecessary () when checking len(c.Deprecated)
updating command comments to be godoc friendly
We were just calling Help() when a user set the --help flag. You could
overwrite how the help subcommand worked with SetHelpFunc, but not now
the --help flag worked.
Today the HelpFunc() seemed to be tailor built for the `help`
subcommand. Which has a rather weird purpose as its `Run` needs to
find the actual command we want to get help about.
Instead make the HelpFunc() for a command be about that command,
rather than having it search for some other command...
```go
package main
import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func main() {
cmd := &cobra.Command{
Use: "min",
Short: "minimal command",
Run: func(_ *cobra.Command, _ []string) {},
}
pflag.String("oncmdline", "oncmdline", "oncmdline")
cmd.Execute()
}
```
Is a minimal cobra program. When --help is displayed without this patch
you only get:
But with the patch --oncmdline is shows under flags.
The template had gotten out of control. It was basically unparsable.
This does a little more work in functions and a little less in the
template. Overall it should be basically the same. It might output the
'additional help topics' in a couple of fewer places, but I doubt people
complain too much...
Calling `cobra-test echo times one two turkey` where `one` and `two` are
valid arguments but `turkey` is not now results in.
Error: invalid argument "turkey" for "cobra-test echo times"
Run 'cobra-test echo times --help' for usage.
Inside Command.Execute() we were checking for pflag.ErrHelp. But
Command.execute() never returns that value. It just complicates the code
and isn't used.
We had lots of quirky if statements like `commandFound.Name() ==
c.Name() && len(stripFlags(args, c)) > 0 && commandFound.Name() !=
args[0]` which embeed all sorts of artifacts which are hard to parse. So
in general, just try to simplify and make stuff readable.
This fixes a problem where if you had a root command and a grand child
with the same name, the parser would break and would not run the
grandchild. The code was special casing if the immediate child had the
same name, but didn't handle grand-children
by now, if someone calls: `program --validflag unknowncommand` the
output will be:
```
Error: unknown command "--validflag"
Run 'program help' for usage.
```
This patch strips out flags so the unknown command is printed:
```
Error: unknown command "unknowncommand"
Run 'program help' for usage.
```
We had some stuff that created a new empty []string if args was already
and empty string (why?)
We had some stuff that called Help() if it wasn't runnable (but
.execute() already does that)
Just remove the special case stuff.
The special case code to handle a runnable root command had some
problems. It was noticed that if you created a runnable root and a
subcommand. And the subcommand was then executed with both a valid and
invalid flag, the error message was about the valid flag being invalid.
For example
./command subcommand --goodflag=10 --badflag=10
Would fail and tell you that --goodflag was an invalid flag. Instead if
we just do away with the special Command.execute() for the root command
the parser for subcommand is what prints the error and it gets it
right...
The additional help topics were really hard to ever get to show. The
required conditionals were difficult to meet and did not seem to really
be logical.
Problems I see:
1) the top level command could never have additional topics.
2) you must have at least one sibling command AND one subcommand
3) we had the AND above, but then test both conditionals a second time
4) if the sub command was runnable we wouldn't print anything
5) if the sibling commands were not runnable we wouldn't print anything
4+5) it's possible that we printed "Additional help topics:" with nothing following it
6) We always printed ourselves as a sibling in the additional info
Whew, I think I fixed all of those! Again, using
https://github.com/eparis/readable-golang-template
I'm actually able to visualize the template and see this craziness.
The conditionals BEFORE this change:
{{if .HasParent}}
{{if and (gt .Commands 0) (gt .Parent.Commands 1) }}
Additional help topics:
{{if gt .Commands 0 }}
{{range .Commands}}
{{if not .Runnable}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}
{{end}}
{{end}}
{{end}}
{{if gt .Parent.Commands 1 }}
{{range .Parent.Commands}}
{{if .Runnable}}
{{if not (eq .Name $cmd.Name) }}
{{end}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
The conditionals AFTER this change:
{{if or (.HasHelpSubCommands) (.HasRunnableSiblings)}}
Additional help topics:
{{if .HasHelpSubCommands}}
{{range .Commands}}
{{if not .Runnable}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}
{{end}}
{{end}}
{{end}}
{{if .HasRunnableSiblings }}
{{range .Parent.Commands}}
{{if .Runnable}}
{{if not (eq .Name $cmd.Name) }}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
I wrote https://github.com/eparis/readable-golang-template which
converts golang templates into something structured around the
conditionals. Obviously you can't just USE the output, but you can SEE
the problems. In this case the output shows something like:
{{if .HasParent}}
{{if and (gt .Commands 0) (gt .Parent.Commands 1) }}
Additional help topics:
{{if gt .Commands 0 }}
{{range .Commands}}
{{if not .Runnable}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}
{{end}}
{{end}}
{{end}}
{{if gt .Parent.Commands 1 }}
{{range .Parent.Commands}}
{{if .Runnable}}
{{if not (eq .Name $cmd.Name) }}
{{end}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}
We have a completely unused "{{if not (eq .Name $cmd.Name) }}"
Move the {{end}} after the {{rpad...}}
Some projects pick up flags from other projects they include. A great
example would be projects that use glog and thus get all of those flags.
Kubernetes, for example, merges those flags manually into its commands.
This was reported in https://github.com/spf13/cobra/issues/44
What this patch does is merge those flags into the PersistentFlags on
the highest parent. This allows kubernetes to stop having to merge
things themselves...
If the command has not set an output explicitly everything will go to
stderr. This makes a lot of sense, but is a huge PITA for people who
want to be able to grep the help output. It is very common for users to
want to do
command --help | grep flag
This patch fixes that by default help output (but not error output like
an invalid command) to stdout instead of defaulting to stderr.
This method removes children commands of an existing command.
This allows to build CLI clients that can be extended by 3rd party tools,
for instance by adding commands _and replacing the “version” command_.
For now the 1st defined command will be executed, so it is not possible
to override an existing command. But anyway, deleting old command then
adding a new one is the ultimate way to be certain there is no
confusion.
The current (desired) behavior when a Command specifies a flag that
has the same name as a persistent/inherited flag, is that the local
definition takes precedence. This change updates the various
Flag subset functions to respect that behavior:
* LocalFlags: now returns only the set of flags and persistent flags
attached to the Command itself.
* InheritedFlags: now returns only the set of persistent flags inherited
from the Command's parent(s), excluding any that are overwritten by a
local flag.
* NonInheritedFlags: changed to an alias of LocalFlags.
* AllPersistentFlags: removed as not very useful; it returned the set
of all persistent flags attached to the Command and its parent(s).
Default UsageTemplate updated to use LocalFlags and InheritedFlags
A corner case exists where c.Runnable() is not checked
before c.Run() is called, thus a nil c.Run is executed
leading to "panic: runtime error: invalid memory address
or nil pointer dereference".
This patch adds an extra c.Runnable() check in execute()
to catch that corner case.
Fixes#37.
For a single root command with a Run method, the help output still
contains 'help [command]' as a subcommand (because Help is always
added). Since the only subcommand would be 'help', the help is better
off omitted.
This change allows a command to be used both as a subcommand
or a root command without having to define a custom help that elides
the help command when no subcommands are added. Instead, the default
help command is only added when subcommands are present.
If, for some reason, you have an application with some name "foo", and your
app has a subcommand "foo", cobra should behave properly when you call
"foo foo", and it should also behave if you call "foo f".
These changes verify both of these cases and ensure cobra responds properly.
A command can now be invoked with a prefix of its own name, assuming that
prefix is unambiguous (ie it isn't also a prefix of any sibling command's
name).