cobra/command.go

1323 lines
37 KiB
Go
Raw Normal View History

2013-09-24 20:47:29 +04:00
// Copyright © 2013 Steve Francia <spf@spf13.com>.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2017-05-05 09:08:38 +03:00
// Package cobra is a commander providing a simple interface to create powerful modern CLI interfaces.
// In addition to providing an interface, Cobra simultaneously provides a controller to organize your application code.
2013-09-24 20:47:29 +04:00
package cobra
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"sort"
2013-09-24 20:47:29 +04:00
"strings"
flag "github.com/spf13/pflag"
2013-09-24 20:47:29 +04:00
)
// Command is just that, a command for your application.
2017-05-09 12:15:05 +03:00
// E.g. 'go run ...' - 'run' is the command. Cobra requires
2013-09-24 20:47:29 +04:00
// you to define the usage and description as part of your command
// definition to ensure usability.
type Command struct {
2017-05-09 12:15:05 +03:00
// Use is the one-line usage message.
2013-09-24 20:47:29 +04:00
Use string
2017-05-09 12:15:05 +03:00
// Aliases is an array of aliases that can be used instead of the first word in Use.
2014-09-26 03:03:34 +04:00
Aliases []string
2017-05-09 12:15:05 +03:00
// SuggestFor is an array of command names for which this command will be suggested -
// similar to aliases but only suggests.
SuggestFor []string
2017-05-09 12:15:05 +03:00
// Short is the short description shown in the 'help' output.
2013-09-24 20:47:29 +04:00
Short string
2017-05-09 12:15:05 +03:00
// Long is the long message shown in the 'help <this-command>' output.
2013-09-24 20:47:29 +04:00
Long string
2017-05-09 12:15:05 +03:00
// Example is examples of how to use the command.
Example string
2017-05-09 12:15:05 +03:00
// ValidArgs is list of all valid non-flag arguments that are accepted in bash completions
ValidArgs []string
2017-07-26 15:51:25 +03:00
// Expected arguments
Args PositionalArgs
2017-05-09 12:15:05 +03:00
// ArgAliases is List of aliases for ValidArgs.
// These are not suggested to the user in the bash completion,
// but accepted if entered manually.
ArgAliases []string
2017-05-09 12:15:05 +03:00
// BashCompletionFunction is custom functions used by the bash autocompletion generator.
BashCompletionFunction string
2017-05-09 12:25:41 +03:00
// Deprecated defines, if this command is deprecated and should print this string when used.
Deprecated string
2017-05-09 12:25:41 +03:00
// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
Hidden bool
// Annotations are key/value pairs that can be used by applications to identify or
2017-05-09 12:15:05 +03:00
// group commands.
Annotations map[string]string
// The *Run functions are executed in the following order:
// * PersistentPreRun()
// * PreRun()
// * Run()
// * PostRun()
// * PersistentPostRun()
2017-05-09 12:15:05 +03:00
// All functions get the same args, the arguments after the command name.
//
2017-05-09 12:15:05 +03:00
// PersistentPreRun: children of this command will inherit and execute.
PersistentPreRun func(cmd *Command, args []string)
2017-05-09 12:15:05 +03:00
// PersistentPreRunE: PersistentPreRun but returns an error.
PersistentPreRunE func(cmd *Command, args []string) error
// PreRun: children of this command will not inherit.
2015-02-10 01:44:14 +03:00
PreRun func(cmd *Command, args []string)
2017-05-09 12:15:05 +03:00
// PreRunE: PreRun but returns an error.
PreRunE func(cmd *Command, args []string) error
2017-05-09 12:15:05 +03:00
// Run: Typically the actual work function. Most commands will only implement this.
Run func(cmd *Command, args []string)
2017-05-09 12:15:05 +03:00
// RunE: Run but returns an error.
RunE func(cmd *Command, args []string) error
// PostRun: run after the Run command.
2015-02-10 01:44:14 +03:00
PostRun func(cmd *Command, args []string)
2017-05-09 12:15:05 +03:00
// PostRunE: PostRun but returns an error.
PostRunE func(cmd *Command, args []string) error
2017-05-09 12:15:05 +03:00
// PersistentPostRun: children of this command will inherit and execute after PostRun.
2015-02-18 00:09:49 +03:00
PersistentPostRun func(cmd *Command, args []string)
2017-05-09 12:15:05 +03:00
// PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) error
// SilenceErrors is an option to quiet errors down stream.
SilenceErrors bool
// SilenceUsage is an option to silence usage when an error occurs.
SilenceUsage bool
2017-05-09 12:15:05 +03:00
// DisableFlagParsing disables the flag parsing.
// If this is true all flags will be passed to the command as arguments.
DisableFlagParsing bool
2017-05-09 12:15:05 +03:00
// DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
// will be printed by generating docs for this command.
DisableAutoGenTag bool
2017-05-09 12:15:05 +03:00
// DisableSuggestions disables the suggestions based on Levenshtein distance
// that go along with 'unknown command' messages.
DisableSuggestions bool
// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0.
SuggestionsMinimumDistance int
// name is the command name, usually the executable's name.
name string
2017-05-09 12:15:05 +03:00
// commands is the list of commands supported by this program.
2013-09-24 20:47:29 +04:00
commands []*Command
2017-05-09 12:15:05 +03:00
// parent is a parent command for this command.
parent *Command
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
2017-05-09 12:25:41 +03:00
// commandsAreSorted defines, if command slice are sorted or not.
commandsAreSorted bool
2017-05-09 12:25:41 +03:00
2017-05-09 12:15:05 +03:00
// args is actual args parsed from flags.
args []string
2017-05-09 12:25:41 +03:00
// flagErrorBuf contains all error messages from pflag.
flagErrorBuf *bytes.Buffer
// flags is full set of flags.
flags *flag.FlagSet
// pflags contains persistent flags.
pflags *flag.FlagSet
// lflags contains local flags.
lflags *flag.FlagSet
// iflags contains inherited flags.
iflags *flag.FlagSet
// parentsPflags is all persistent flags of cmd's parents.
parentsPflags *flag.FlagSet
// globNormFunc is the global normalization function
// that we can use on every pflag set and children commands
globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName
2017-05-09 12:15:05 +03:00
// output is an output writer defined by user.
output io.Writer
// usageFunc is usage func defined by user.
usageFunc func(*Command) error
// usageTemplate is usage template defined by user.
usageTemplate string
// flagErrorFunc is func defined by user and it's called when the parsing of
// flags returns an error.
flagErrorFunc func(*Command, error) error
2017-05-09 12:15:05 +03:00
// helpTemplate is help template defined by user.
helpTemplate string
// helpFunc is help func defined by user.
helpFunc func(*Command, []string)
2017-05-09 12:25:41 +03:00
// helpCommand is command with usage 'help'. If it's not defined by user,
2017-05-09 12:15:05 +03:00
// cobra uses default help command.
helpCommand *Command
2013-09-24 20:47:29 +04:00
}
// SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden
// particularly useful when testing.
func (c *Command) SetArgs(a []string) {
c.args = a
}
// SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used.
func (c *Command) SetOutput(output io.Writer) {
c.output = output
}
// SetUsageFunc sets usage function. Usage can be defined by application.
func (c *Command) SetUsageFunc(f func(*Command) error) {
c.usageFunc = f
}
// SetUsageTemplate sets usage template. Can be defined by Application.
func (c *Command) SetUsageTemplate(s string) {
c.usageTemplate = s
}
// SetFlagErrorFunc sets a function to generate an error when flag parsing
// fails.
func (c *Command) SetFlagErrorFunc(f func(*Command, error) error) {
c.flagErrorFunc = f
}
// SetHelpFunc sets help function. Can be defined by Application.
func (c *Command) SetHelpFunc(f func(*Command, []string)) {
c.helpFunc = f
}
// SetHelpCommand sets help command.
func (c *Command) SetHelpCommand(cmd *Command) {
c.helpCommand = cmd
}
// SetHelpTemplate sets help template to be used. Application can use it to set custom template.
func (c *Command) SetHelpTemplate(s string) {
c.helpTemplate = s
}
// SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands.
// The user should not have a cyclic dependency on commands.
func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) {
c.Flags().SetNormalizeFunc(n)
c.PersistentFlags().SetNormalizeFunc(n)
c.globNormFunc = n
for _, command := range c.commands {
command.SetGlobalNormalizationFunc(n)
}
}
// OutOrStdout returns output to stdout.
func (c *Command) OutOrStdout() io.Writer {
return c.getOut(os.Stdout)
}
// OutOrStderr returns output to stderr
func (c *Command) OutOrStderr() io.Writer {
return c.getOut(os.Stderr)
}
func (c *Command) getOut(def io.Writer) io.Writer {
if c.output != nil {
return c.output
}
if c.HasParent() {
return c.parent.getOut(def)
}
return def
}
// UsageFunc returns either the function set by SetUsageFunc for this command
2016-08-30 20:14:27 +03:00
// or a parent, or it returns a default usage function.
func (c *Command) UsageFunc() (f func(*Command) error) {
if c.usageFunc != nil {
return c.usageFunc
}
if c.HasParent() {
2017-04-23 10:17:44 +03:00
return c.Parent().UsageFunc()
}
return func(c *Command) error {
c.mergePersistentFlags()
err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c)
if err != nil {
c.Println(err)
}
return err
}
}
2016-08-30 20:14:27 +03:00
// Usage puts out the usage for the command.
// Used when a user provides invalid input.
// Can be defined by user by overriding UsageFunc.
func (c *Command) Usage() error {
return c.UsageFunc()(c)
}
// HelpFunc returns either the function set by SetHelpFunc for this command
2016-08-30 20:14:27 +03:00
// or a parent, or it returns a function with default help behavior.
func (c *Command) HelpFunc() func(*Command, []string) {
2017-04-23 10:17:44 +03:00
if c.helpFunc != nil {
return c.helpFunc
}
2017-04-23 10:17:44 +03:00
if c.HasParent() {
return c.Parent().HelpFunc()
}
return func(c *Command, a []string) {
c.mergePersistentFlags()
err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c)
if err != nil {
c.Println(err)
}
}
}
2016-08-30 20:14:27 +03:00
// Help puts out the help for the command.
// Used when a user calls help [command].
// Can be defined by user by overriding HelpFunc.
func (c *Command) Help() error {
c.HelpFunc()(c, []string{})
return nil
}
// UsageString return usage string.
func (c *Command) UsageString() string {
tmpOutput := c.output
bb := new(bytes.Buffer)
c.SetOutput(bb)
c.Usage()
c.output = tmpOutput
return bb.String()
}
// FlagErrorFunc returns either the function set by SetFlagErrorFunc for this
// command or a parent, or it returns a function which returns the original
// error.
func (c *Command) FlagErrorFunc() (f func(*Command, error) error) {
if c.flagErrorFunc != nil {
return c.flagErrorFunc
}
if c.HasParent() {
return c.parent.FlagErrorFunc()
}
return func(c *Command, err error) error {
return err
}
}
var minUsagePadding = 25
// UsagePadding return padding for the usage.
func (c *Command) UsagePadding() int {
if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen {
return minUsagePadding
}
return c.parent.commandsMaxUseLen
}
var minCommandPathPadding = 11
// CommandPathPadding return padding for the command path.
func (c *Command) CommandPathPadding() int {
if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen {
return minCommandPathPadding
}
return c.parent.commandsMaxCommandPathLen
}
var minNamePadding = 11
// NamePadding returns padding for the name.
func (c *Command) NamePadding() int {
if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen {
return minNamePadding
}
return c.parent.commandsMaxNameLen
}
// UsageTemplate returns usage template for the command.
func (c *Command) UsageTemplate() string {
if c.usageTemplate != "" {
return c.usageTemplate
}
if c.HasParent() {
return c.parent.UsageTemplate()
}
return `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
2014-09-26 03:03:34 +04:00
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
}
// HelpTemplate return help template for the command.
func (c *Command) HelpTemplate() string {
if c.helpTemplate != "" {
return c.helpTemplate
}
if c.HasParent() {
return c.parent.HelpTemplate()
}
return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
}
2017-05-05 09:08:38 +03:00
func hasNoOptDefVal(name string, fs *flag.FlagSet) bool {
flag := fs.Lookup(name)
if flag == nil {
return false
}
2017-04-24 16:20:01 +03:00
return flag.NoOptDefVal != ""
}
func shortHasNoOptDefVal(name string, fs *flag.FlagSet) bool {
2017-05-05 09:08:38 +03:00
if len(name) == 0 {
return false
}
flag := fs.ShorthandLookup(name[:1])
if flag == nil {
return false
}
return flag.NoOptDefVal != ""
}
func stripFlags(args []string, c *Command) []string {
2017-04-23 23:38:30 +03:00
if len(args) == 0 {
2014-06-17 20:32:27 +04:00
return args
}
c.mergePersistentFlags()
2014-06-17 20:32:27 +04:00
commands := []string{}
2017-04-24 16:20:01 +03:00
flags := c.Flags()
2017-04-23 23:38:30 +03:00
Loop:
for len(args) > 0 {
s := args[0]
2017-04-24 16:20:01 +03:00
args = args[1:]
switch {
case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags):
// If '--flag arg' then
// delete arg from args.
fallthrough // (do the same as below)
case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags):
// If '-f arg' then
// delete 'arg' from args or break the loop if len(args) <= 1.
if len(args) <= 1 {
break Loop
} else {
args = args[1:]
continue
2014-06-17 20:32:27 +04:00
}
case s != "" && !strings.HasPrefix(s, "-"):
commands = append(commands, s)
2014-06-17 20:32:27 +04:00
}
}
return commands
}
2015-04-24 18:39:11 +03:00
// argsMinusFirstX removes only the first x from args. Otherwise, commands that look like
// openshift admin policy add-role-to-user admin my-user, lose the admin argument (arg[4]).
func argsMinusFirstX(args []string, x string) []string {
for i, y := range args {
if x == y {
ret := []string{}
ret = append(ret, args[:i]...)
ret = append(ret, args[i+1:]...)
return ret
2014-06-17 20:32:27 +04:00
}
}
2015-04-24 18:39:11 +03:00
return args
2014-06-17 20:32:27 +04:00
}
// Find the target command given the args and command tree
2013-09-25 00:03:22 +04:00
// Meant to be run on the highest node. Only searches down.
func (c *Command) Find(args []string) (*Command, []string, error) {
2013-09-24 20:47:29 +04:00
if c == nil {
return nil, nil, fmt.Errorf("Called find() on a nil Command")
}
var innerfind func(*Command, []string) (*Command, []string)
innerfind = func(c *Command, innerArgs []string) (*Command, []string) {
argsWOflags := stripFlags(innerArgs, c)
if len(argsWOflags) == 0 {
return c, innerArgs
}
nextSubCmd := argsWOflags[0]
matches := make([]*Command, 0)
for _, cmd := range c.commands {
if cmd.Name() == nextSubCmd || cmd.HasAlias(nextSubCmd) { // exact name or alias match
return innerfind(cmd, argsMinusFirstX(innerArgs, nextSubCmd))
}
if EnablePrefixMatching {
if strings.HasPrefix(cmd.Name(), nextSubCmd) { // prefix match
matches = append(matches, cmd)
}
for _, x := range cmd.Aliases {
if strings.HasPrefix(x, nextSubCmd) {
matches = append(matches, cmd)
}
}
}
2013-09-24 20:47:29 +04:00
}
// only accept a single prefix match - multiple matches would be ambiguous
if len(matches) == 1 {
return innerfind(matches[0], argsMinusFirstX(innerArgs, argsWOflags[0]))
}
return c, innerArgs
2013-09-24 20:47:29 +04:00
}
commandFound, a := innerfind(c, args)
if commandFound.Args == nil {
return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound))
2013-09-24 20:47:29 +04:00
}
return commandFound, a, nil
2013-09-24 20:47:29 +04:00
}
func (c *Command) findSuggestions(arg string) string {
if c.DisableSuggestions {
return ""
}
if c.SuggestionsMinimumDistance <= 0 {
c.SuggestionsMinimumDistance = 2
}
suggestionsString := ""
if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
suggestionsString += "\n\nDid you mean this?\n"
for _, s := range suggestions {
suggestionsString += fmt.Sprintf("\t%v\n", s)
}
}
return suggestionsString
}
// SuggestionsFor provides suggestions for the typedName.
func (c *Command) SuggestionsFor(typedName string) []string {
suggestions := []string{}
for _, cmd := range c.commands {
if cmd.IsAvailableCommand() {
levenshteinDistance := ld(typedName, cmd.Name(), true)
suggestByLevenshtein := levenshteinDistance <= c.SuggestionsMinimumDistance
suggestByPrefix := strings.HasPrefix(strings.ToLower(cmd.Name()), strings.ToLower(typedName))
if suggestByLevenshtein || suggestByPrefix {
suggestions = append(suggestions, cmd.Name())
}
for _, explicitSuggestion := range cmd.SuggestFor {
if strings.EqualFold(typedName, explicitSuggestion) {
suggestions = append(suggestions, cmd.Name())
}
}
}
}
return suggestions
}
// VisitParents visits all parents of the command and invokes fn on each parent.
func (c *Command) VisitParents(fn func(*Command)) {
if c.HasParent() {
fn(c.Parent())
c.Parent().VisitParents(fn)
}
}
// Root finds root command.
2013-09-25 00:03:22 +04:00
func (c *Command) Root() *Command {
if c.HasParent() {
return c.Parent().Root()
2013-09-24 20:47:29 +04:00
}
return c
2013-09-25 00:03:22 +04:00
}
2015-10-14 01:37:14 +03:00
// ArgsLenAtDash will return the length of f.Args at the moment when a -- was
// found during arg parsing. This allows your program to know which args were
// before the -- and which came after. (Description from
// https://godoc.org/github.com/spf13/pflag#FlagSet.ArgsLenAtDash).
func (c *Command) ArgsLenAtDash() int {
return c.Flags().ArgsLenAtDash()
}
2013-10-01 05:54:46 +04:00
func (c *Command) execute(a []string) (err error) {
2013-09-24 20:47:29 +04:00
if c == nil {
return fmt.Errorf("Called Execute() on a nil Command")
}
if len(c.Deprecated) > 0 {
c.Printf("Command %q is deprecated, %s\n", c.Name(), c.Deprecated)
}
2015-09-01 07:16:41 +03:00
// initialize help flag as the last point possible to allow for user
// overriding
c.InitDefaultHelpFlag()
2015-09-01 07:16:41 +03:00
2013-10-01 05:54:46 +04:00
err = c.ParseFlags(a)
if err != nil {
return c.FlagErrorFunc()(c, err)
}
// If help is called, regardless of other flags, return we want help.
// Also say we need help if the command isn't runnable.
2015-09-01 07:16:41 +03:00
helpVal, err := c.Flags().GetBool("help")
if err != nil {
// should be impossible to get here as we always declare a help
// flag in InitDefaultHelpFlag()
2015-09-01 07:16:41 +03:00
c.Println("\"help\" flag declared as non-bool. Please correct your code")
return err
}
2015-09-01 07:16:41 +03:00
if helpVal || !c.Runnable() {
return flag.ErrHelp
2013-09-24 20:47:29 +04:00
}
c.preRun()
2016-06-04 03:25:52 +03:00
argWoFlags := c.Flags().Args()
2016-06-04 03:25:52 +03:00
if c.DisableFlagParsing {
argWoFlags = a
}
2015-02-10 01:44:14 +03:00
if err := c.ValidateArgs(argWoFlags); err != nil {
return err
}
for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil {
2015-09-30 10:09:17 +03:00
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
return err
}
break
} else if p.PersistentPreRun != nil {
p.PersistentPreRun(c, argWoFlags)
break
}
2015-02-18 00:09:49 +03:00
}
if c.PreRunE != nil {
if err := c.PreRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PreRun != nil {
2015-02-10 01:44:14 +03:00
c.PreRun(c, argWoFlags)
}
if c.RunE != nil {
if err := c.RunE(c, argWoFlags); err != nil {
return err
}
} else {
c.Run(c, argWoFlags)
}
if c.PostRunE != nil {
if err := c.PostRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PostRun != nil {
2015-02-10 01:44:14 +03:00
c.PostRun(c, argWoFlags)
}
for p := c; p != nil; p = p.Parent() {
if p.PersistentPostRunE != nil {
if err := p.PersistentPostRunE(c, argWoFlags); err != nil {
return err
}
break
} else if p.PersistentPostRun != nil {
p.PersistentPostRun(c, argWoFlags)
break
}
2015-02-18 00:09:49 +03:00
}
2015-02-10 01:44:14 +03:00
return nil
2013-09-24 20:47:29 +04:00
}
func (c *Command) preRun() {
for _, x := range initializers {
x()
}
}
2017-05-28 13:56:30 +03:00
// Execute uses the args (os.Args[1:] by default)
// and run through the command tree finding appropriate matches
// for commands and then corresponding flags.
func (c *Command) Execute() error {
_, err := c.ExecuteC()
return err
}
// ExecuteC executes the command.
func (c *Command) ExecuteC() (cmd *Command, err error) {
// Regardless of what command execute is called on, run on Root only
if c.HasParent() {
return c.Root().ExecuteC()
}
// windows hook
if preExecHookFn != nil {
preExecHookFn(c)
}
// initialize help as the last point possible to allow for user
// overriding
c.InitDefaultHelpCmd()
var args []string
// Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155
if c.args == nil && filepath.Base(os.Args[0]) != "cobra.test" {
args = os.Args[1:]
} else {
args = c.args
}
cmd, flags, err := c.Find(args)
if err != nil {
// If found parse to a subcommand and then failed, talk about the subcommand
if cmd != nil {
c = cmd
}
if !c.SilenceErrors {
c.Println("Error:", err.Error())
c.Printf("Run '%v --help' for usage.\n", c.CommandPath())
}
return c, err
}
err = cmd.execute(flags)
if err != nil {
// Always show help if requested, even if SilenceErrors is in
// effect
if err == flag.ErrHelp {
cmd.HelpFunc()(cmd, args)
return cmd, nil
}
// If root command has SilentErrors flagged,
// all subcommands should respect it
2015-10-26 06:17:39 +03:00
if !cmd.SilenceErrors && !c.SilenceErrors {
c.Println("Error:", err.Error())
}
// If root command has SilentUsage flagged,
// all subcommands should respect it
if !cmd.SilenceUsage && !c.SilenceUsage {
c.Println(cmd.UsageString())
}
}
2017-05-28 13:56:30 +03:00
return cmd, err
}
func (c *Command) ValidateArgs(args []string) error {
if c.Args == nil {
return nil
}
return c.Args(c, args)
}
// InitDefaultHelpFlag adds default help flag to c.
// It is called automatically by executing the c or by calling help and usage.
2017-05-07 11:52:40 +03:00
// If c already has help flag, it will do nothing.
func (c *Command) InitDefaultHelpFlag() {
c.mergePersistentFlags()
if c.Flags().Lookup("help") == nil {
usage := "help for "
if c.Name() == "" {
usage += "this command"
} else {
usage += c.Name()
}
c.Flags().BoolP("help", "h", false, usage)
}
2015-09-01 07:16:41 +03:00
}
// InitDefaultHelpCmd adds default help command to c.
// It is called automatically by executing the c or by calling help and usage.
// If c already has help command or c has no subcommands, it will do nothing.
func (c *Command) InitDefaultHelpCmd() {
if !c.HasSubCommands() {
return
}
if c.helpCommand == nil {
c.helpCommand = &Command{
Use: "help [command]",
Short: "Help about any command",
Long: `Help provides help for any command in the application.
Simply type ` + c.Name() + ` help [path to command] for full details.`,
Run: func(c *Command, args []string) {
cmd, _, e := c.Root().Find(args)
if cmd == nil || e != nil {
c.Printf("Unknown help topic %#q\n", args)
c.Root().Usage()
} else {
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
cmd.Help()
}
},
}
}
c.RemoveCommand(c.helpCommand)
c.AddCommand(c.helpCommand)
}
// ResetCommands used for testing.
2013-09-24 20:47:29 +04:00
func (c *Command) ResetCommands() {
c.parent = nil
2013-09-24 20:47:29 +04:00
c.commands = nil
c.helpCommand = nil
c.parentsPflags = nil
2013-09-24 20:47:29 +04:00
}
2016-08-30 20:14:27 +03:00
// Sorts commands by their names.
type commandSorterByName []*Command
func (c commandSorterByName) Len() int { return len(c) }
func (c commandSorterByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c commandSorterByName) Less(i, j int) bool { return c[i].Name() < c[j].Name() }
// Commands returns a sorted slice of child commands.
2013-09-24 20:47:29 +04:00
func (c *Command) Commands() []*Command {
// do not sort commands if it already sorted or sorting was disabled
if EnableCommandSorting && !c.commandsAreSorted {
sort.Sort(commandSorterByName(c.commands))
c.commandsAreSorted = true
}
2013-09-24 20:47:29 +04:00
return c.commands
}
2014-12-13 08:05:43 +03:00
// AddCommand adds one or more commands to this parent command.
2013-09-24 20:47:29 +04:00
func (c *Command) AddCommand(cmds ...*Command) {
for i, x := range cmds {
if cmds[i] == c {
panic("Command can't be a child of itself")
}
cmds[i].parent = c
// update max lengths
usageLen := len(x.Use)
if usageLen > c.commandsMaxUseLen {
c.commandsMaxUseLen = usageLen
}
commandPathLen := len(x.CommandPath())
if commandPathLen > c.commandsMaxCommandPathLen {
c.commandsMaxCommandPathLen = commandPathLen
}
nameLen := len(x.Name())
if nameLen > c.commandsMaxNameLen {
c.commandsMaxNameLen = nameLen
}
// If global normalization function exists, update all children
if c.globNormFunc != nil {
x.SetGlobalNormalizationFunc(c.globNormFunc)
}
2013-09-24 20:47:29 +04:00
c.commands = append(c.commands, x)
c.commandsAreSorted = false
2013-09-24 20:47:29 +04:00
}
}
// RemoveCommand removes one or more commands from a parent command.
func (c *Command) RemoveCommand(cmds ...*Command) {
commands := []*Command{}
main:
for _, command := range c.commands {
for _, cmd := range cmds {
if command == cmd {
command.parent = nil
continue main
}
}
commands = append(commands, command)
}
c.commands = commands
// recompute all lengths
c.commandsMaxUseLen = 0
c.commandsMaxCommandPathLen = 0
c.commandsMaxNameLen = 0
for _, command := range c.commands {
usageLen := len(command.Use)
if usageLen > c.commandsMaxUseLen {
c.commandsMaxUseLen = usageLen
}
commandPathLen := len(command.CommandPath())
if commandPathLen > c.commandsMaxCommandPathLen {
c.commandsMaxCommandPathLen = commandPathLen
}
nameLen := len(command.Name())
if nameLen > c.commandsMaxNameLen {
c.commandsMaxNameLen = nameLen
}
}
}
2016-08-30 20:14:27 +03:00
// Print is a convenience method to Print to the defined output, fallback to Stderr if not set.
2013-09-24 20:47:29 +04:00
func (c *Command) Print(i ...interface{}) {
fmt.Fprint(c.OutOrStderr(), i...)
2013-09-24 20:47:29 +04:00
}
2016-08-30 20:14:27 +03:00
// Println is a convenience method to Println to the defined output, fallback to Stderr if not set.
2013-09-24 20:47:29 +04:00
func (c *Command) Println(i ...interface{}) {
c.Print(fmt.Sprintln(i...))
2013-09-24 20:47:29 +04:00
}
2016-08-30 20:14:27 +03:00
// Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set.
2013-09-24 20:47:29 +04:00
func (c *Command) Printf(format string, i ...interface{}) {
c.Print(fmt.Sprintf(format, i...))
2013-09-24 20:47:29 +04:00
}
2014-12-13 08:05:43 +03:00
// CommandPath returns the full path to this command.
2013-09-24 20:47:29 +04:00
func (c *Command) CommandPath() string {
if c.HasParent() {
return c.Parent().CommandPath() + " " + c.Name()
2013-09-24 20:47:29 +04:00
}
return c.Name()
2013-09-24 20:47:29 +04:00
}
2016-08-30 20:14:27 +03:00
// UseLine puts out the full usage for a given command (including parents).
2013-09-24 20:47:29 +04:00
func (c *Command) UseLine() string {
var useline string
2013-09-24 20:47:29 +04:00
if c.HasParent() {
useline = c.parent.CommandPath() + " " + c.Use
} else {
useline = c.Use
}
if c.HasAvailableFlags() && !strings.Contains(useline, "[flags]") {
useline += " [flags]"
2013-09-24 20:47:29 +04:00
}
return useline
2013-09-24 20:47:29 +04:00
}
// DebugFlags used to determine which flags have been assigned to which commands
2016-08-30 20:14:27 +03:00
// and which persist.
2013-09-24 20:47:29 +04:00
func (c *Command) DebugFlags() {
c.Println("DebugFlags called on", c.Name())
var debugflags func(*Command)
debugflags = func(x *Command) {
if x.HasFlags() || x.HasPersistentFlags() {
c.Println(x.Name())
}
if x.HasFlags() {
x.flags.VisitAll(func(f *flag.Flag) {
if x.HasPersistentFlags() && x.persistentFlag(f.Name) != nil {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [LP]")
2013-09-24 20:47:29 +04:00
} else {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [L]")
}
})
}
if x.HasPersistentFlags() {
x.pflags.VisitAll(func(f *flag.Flag) {
if x.HasFlags() {
if x.flags.Lookup(f.Name) == nil {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [P]")
}
} else {
c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [P]")
}
})
}
c.Println(x.flagErrorBuf)
2013-09-24 20:47:29 +04:00
if x.HasSubCommands() {
for _, y := range x.commands {
debugflags(y)
}
}
}
debugflags(c)
}
// Name returns the command's name: the first word in the use line.
func (c *Command) Name() string {
if c.name == "" {
name := c.Use
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
c.name = name
2013-09-24 20:47:29 +04:00
}
2017-02-09 19:54:17 +03:00
return c.name
2013-09-24 20:47:29 +04:00
}
// HasAlias determines if a given string is an alias of the command.
2014-09-26 03:03:34 +04:00
func (c *Command) HasAlias(s string) bool {
for _, a := range c.Aliases {
if a == s {
return true
}
}
return false
}
// NameAndAliases returns string containing name and all aliases
2014-09-26 03:03:34 +04:00
func (c *Command) NameAndAliases() string {
return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ")
}
// HasExample determines if the command has example.
func (c *Command) HasExample() bool {
return len(c.Example) > 0
}
2016-08-30 20:14:27 +03:00
// Runnable determines if the command is itself runnable.
2013-09-24 20:47:29 +04:00
func (c *Command) Runnable() bool {
return c.Run != nil || c.RunE != nil
2013-09-24 20:47:29 +04:00
}
2016-08-30 20:14:27 +03:00
// HasSubCommands determines if the command has children commands.
2013-09-24 20:47:29 +04:00
func (c *Command) HasSubCommands() bool {
return len(c.commands) > 0
}
// IsAvailableCommand determines if a command is available as a non-help command
2016-08-30 20:14:27 +03:00
// (this includes all non deprecated/hidden commands).
func (c *Command) IsAvailableCommand() bool {
if len(c.Deprecated) != 0 || c.Hidden {
return false
}
2015-09-12 01:18:36 +03:00
if c.HasParent() && c.Parent().helpCommand == c {
return false
}
if c.Runnable() || c.HasAvailableSubCommands() {
return true
}
return false
}
// IsAdditionalHelpTopicCommand determines if a command is an additional
// help topic command; additional help topic command is determined by the
// fact that it is NOT runnable/hidden/deprecated, and has no sub commands that
// are runnable/hidden/deprecated.
// Concrete example: https://github.com/spf13/cobra/issues/393#issuecomment-282741924.
func (c *Command) IsAdditionalHelpTopicCommand() bool {
// if a command is runnable, deprecated, or hidden it is not a 'help' command
if c.Runnable() || len(c.Deprecated) != 0 || c.Hidden {
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
return false
}
// if any non-help sub commands are found, the command is not a 'help' command
for _, sub := range c.commands {
if !sub.IsAdditionalHelpTopicCommand() {
return false
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
}
}
// the command either has no sub commands, or no non-help sub commands
return true
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
}
2016-08-20 10:04:53 +03:00
// HasHelpSubCommands determines if a command has any available 'help' sub commands
// that need to be shown in the usage/help default template under 'additional help
2016-08-30 20:14:27 +03:00
// topics'.
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
func (c *Command) HasHelpSubCommands() bool {
// return true on the first found available 'help' sub command
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
for _, sub := range c.commands {
if sub.IsAdditionalHelpTopicCommand() {
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
return true
}
}
// the command either has no sub commands, or no available 'help' sub commands
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
return false
}
// HasAvailableSubCommands determines if a command has available sub commands that
2016-08-30 20:14:27 +03:00
// need to be shown in the usage/help default template under 'available commands'.
func (c *Command) HasAvailableSubCommands() bool {
// return true on the first found available (non deprecated/help/hidden)
// sub command
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
for _, sub := range c.commands {
if sub.IsAvailableCommand() {
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
return true
}
}
// the command either has no sub comamnds, or no available (non deprecated/help/hidden)
// sub commands
Fix additional help topics template 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}}
2015-03-20 21:47:56 +03:00
return false
}
2016-08-30 20:14:27 +03:00
// HasParent determines if the command is a child command.
2013-09-24 20:47:29 +04:00
func (c *Command) HasParent() bool {
return c.parent != nil
}
2016-08-30 20:14:27 +03:00
// GlobalNormalizationFunc returns the global normalization function or nil if doesn't exists.
func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) flag.NormalizedName {
return c.globNormFunc
}
// Flags returns the complete FlagSet that applies
2016-08-30 20:14:27 +03:00
// to this command (local and persistent declared here and by all parents).
2013-09-24 20:47:29 +04:00
func (c *Command) Flags() *flag.FlagSet {
if c.flags == nil {
c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer)
}
c.flags.SetOutput(c.flagErrorBuf)
2013-09-24 20:47:29 +04:00
}
2013-09-24 20:47:29 +04:00
return c.flags
}
2016-08-30 20:14:27 +03:00
// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands.
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
persistentFlags := c.PersistentFlags()
out := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
c.LocalFlags().VisitAll(func(f *flag.Flag) {
if persistentFlags.Lookup(f.Name) == nil {
out.AddFlag(f)
}
})
return out
}
2016-08-30 20:14:27 +03:00
// LocalFlags returns the local FlagSet specifically set in the current command.
func (c *Command) LocalFlags() *flag.FlagSet {
c.mergePersistentFlags()
if c.lflags == nil {
c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer)
}
c.lflags.SetOutput(c.flagErrorBuf)
}
c.lflags.SortFlags = c.Flags().SortFlags
if c.globNormFunc != nil {
c.lflags.SetNormalizeFunc(c.globNormFunc)
}
addToLocal := func(f *flag.Flag) {
if c.lflags.Lookup(f.Name) == nil && c.parentsPflags.Lookup(f.Name) == nil {
c.lflags.AddFlag(f)
}
}
c.Flags().VisitAll(addToLocal)
c.PersistentFlags().VisitAll(addToLocal)
return c.lflags
}
2016-08-30 20:14:27 +03:00
// InheritedFlags returns all flags which were inherited from parents commands.
func (c *Command) InheritedFlags() *flag.FlagSet {
c.mergePersistentFlags()
if c.iflags == nil {
c.iflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer)
}
c.iflags.SetOutput(c.flagErrorBuf)
}
local := c.LocalFlags()
if c.globNormFunc != nil {
c.iflags.SetNormalizeFunc(c.globNormFunc)
}
c.parentsPflags.VisitAll(func(f *flag.Flag) {
if c.iflags.Lookup(f.Name) == nil && local.Lookup(f.Name) == nil {
c.iflags.AddFlag(f)
}
})
return c.iflags
}
2016-08-30 20:14:27 +03:00
// NonInheritedFlags returns all flags which were not inherited from parent commands.
func (c *Command) NonInheritedFlags() *flag.FlagSet {
return c.LocalFlags()
}
2016-08-30 20:14:27 +03:00
// PersistentFlags returns the persistent FlagSet specifically set in the current command.
2013-09-24 20:47:29 +04:00
func (c *Command) PersistentFlags() *flag.FlagSet {
if c.pflags == nil {
c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer)
}
c.pflags.SetOutput(c.flagErrorBuf)
2013-09-24 20:47:29 +04:00
}
return c.pflags
}
2016-08-30 20:14:27 +03:00
// ResetFlags is used in testing.
2013-09-24 20:47:29 +04:00
func (c *Command) ResetFlags() {
c.flagErrorBuf = new(bytes.Buffer)
c.flagErrorBuf.Reset()
2013-09-24 20:47:29 +04:00
c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
c.flags.SetOutput(c.flagErrorBuf)
2013-09-24 20:47:29 +04:00
c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
c.pflags.SetOutput(c.flagErrorBuf)
c.lflags = nil
c.iflags = nil
c.parentsPflags = nil
2013-09-24 20:47:29 +04:00
}
// HasFlags checks if the command contains any flags (local plus persistent from the entire structure).
2013-09-24 20:47:29 +04:00
func (c *Command) HasFlags() bool {
return c.Flags().HasFlags()
}
// HasPersistentFlags checks if the command contains persistent flags.
2013-09-24 20:47:29 +04:00
func (c *Command) HasPersistentFlags() bool {
return c.PersistentFlags().HasFlags()
}
// HasLocalFlags checks if the command has flags specifically declared locally.
func (c *Command) HasLocalFlags() bool {
return c.LocalFlags().HasFlags()
}
// HasInheritedFlags checks if the command has flags inherited from its parent command.
func (c *Command) HasInheritedFlags() bool {
return c.InheritedFlags().HasFlags()
}
// HasAvailableFlags checks if the command contains any flags (local plus persistent from the entire
2016-08-30 20:14:27 +03:00
// structure) which are not hidden or deprecated.
func (c *Command) HasAvailableFlags() bool {
return c.Flags().HasAvailableFlags()
}
// HasAvailablePersistentFlags checks if the command contains persistent flags which are not hidden or deprecated.
func (c *Command) HasAvailablePersistentFlags() bool {
return c.PersistentFlags().HasAvailableFlags()
}
// HasAvailableLocalFlags checks if the command has flags specifically declared locally which are not hidden
2016-08-30 20:14:27 +03:00
// or deprecated.
func (c *Command) HasAvailableLocalFlags() bool {
return c.LocalFlags().HasAvailableFlags()
}
// HasAvailableInheritedFlags checks if the command has flags inherited from its parent command which are
2016-08-30 20:14:27 +03:00
// not hidden or deprecated.
func (c *Command) HasAvailableInheritedFlags() bool {
return c.InheritedFlags().HasAvailableFlags()
}
2016-08-30 20:14:27 +03:00
// Flag climbs up the command tree looking for matching flag.
2013-09-24 20:47:29 +04:00
func (c *Command) Flag(name string) (flag *flag.Flag) {
flag = c.Flags().Lookup(name)
if flag == nil {
flag = c.persistentFlag(name)
}
return
}
2016-08-30 20:14:27 +03:00
// Recursively find matching persistent flag.
2013-09-24 20:47:29 +04:00
func (c *Command) persistentFlag(name string) (flag *flag.Flag) {
if c.HasPersistentFlags() {
flag = c.PersistentFlags().Lookup(name)
}
if flag == nil {
c.updateParentsPflags()
flag = c.parentsPflags.Lookup(name)
2013-09-24 20:47:29 +04:00
}
return
}
2016-08-30 20:14:27 +03:00
// ParseFlags parses persistent flag tree and local flags.
func (c *Command) ParseFlags(args []string) error {
2016-06-04 03:25:52 +03:00
if c.DisableFlagParsing {
return nil
}
beforeErrorBufLen := c.flagErrorBuf.Len()
2013-09-24 20:47:29 +04:00
c.mergePersistentFlags()
err := c.Flags().Parse(args)
// Print warnings if they occurred (e.g. deprecated flag messages).
if c.flagErrorBuf.Len()-beforeErrorBufLen > 0 && err == nil {
c.Print(c.flagErrorBuf.String())
}
return err
2013-09-24 20:47:29 +04:00
}
2016-08-30 20:14:27 +03:00
// Parent returns a commands parent command.
func (c *Command) Parent() *Command {
return c.parent
}
// mergePersistentFlags merges c.PersistentFlags() to c.Flags()
// and adds missing persistent flags of all parents.
func (c *Command) mergePersistentFlags() {
c.updateParentsPflags()
c.Flags().AddFlagSet(c.PersistentFlags())
c.Flags().AddFlagSet(c.parentsPflags)
}
// updateParentsPflags updates c.parentsPflags by adding
// new persistent flags of all parents.
// If c.parentsPflags == nil, it makes new.
func (c *Command) updateParentsPflags() {
if c.parentsPflags == nil {
c.parentsPflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
c.parentsPflags.SetOutput(c.flagErrorBuf)
c.parentsPflags.SortFlags = false
}
2013-09-24 20:47:29 +04:00
if c.globNormFunc != nil {
c.parentsPflags.SetNormalizeFunc(c.globNormFunc)
}
c.Root().PersistentFlags().AddFlagSet(flag.CommandLine)
c.VisitParents(func(parent *Command) {
c.parentsPflags.AddFlagSet(parent.PersistentFlags())
})
2013-09-24 20:47:29 +04:00
}