diff --git a/hack/vendor.sh b/hack/vendor.sh index 0779e39c9e..b03f7f698c 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -135,7 +135,7 @@ clone git google.golang.org/cloud dae7e3d993bc3812a2185af60552bb6b847e52a0 https clone git github.com/docker/containerd 57b7c3da915ebe943bd304c00890959b191e5264 # cli -clone git github.com/spf13/cobra 0f866a6211e33cde2091d9290c08f6afd6c9ebbc +clone git github.com/spf13/cobra acf60156558542e78c6f3695f74b0f871614ff55 https://github.com/dnephin/cobra.git clone git github.com/spf13/pflag cb88ea77998c3f024757528e3305022ab50b43be clone git github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 diff --git a/vendor/src/github.com/spf13/cobra/README.md b/vendor/src/github.com/spf13/cobra/README.md index 34c79eb8eb..6814a6d483 100644 --- a/vendor/src/github.com/spf13/cobra/README.md +++ b/vendor/src/github.com/spf13/cobra/README.md @@ -406,6 +406,42 @@ A flag can also be assigned locally which will only apply to that specific comma RootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") ``` +### Positional Arguments + +Validation of positional arguments can be specified using the `Args` field, which accepts +one of the following values: + +- `NoArgs` - the command will report an error if there are any positional args. +- `ArbitraryArgs` - the command will accept any args. +- `OnlyValidArgs` - the command will report an error if there are any positiona + args that are not in the `ValidArgs` list. +- `MinimumNArgs(int)` - the command will report an error if there are not at + least N positional args. +- `MaximumNArgs(int)` - the command will report an error if there are more than + N positional args. +- `ExactArgs(int)` - the command will report an error if there are not + exactly N positional args. +- `RangeArgs(min, max)` - the command will report an error if the number of args + is not between the minimum and maximum number of expected args. + +By default, `Args` uses the following legacy behaviour: +- root commands with no subcommands can take arbitrary arguments +- root commands with subcommands will do subcommand validity checking +- subcommands will always accept arbitrary arguments and do no subsubcommand validity checking + + +```go +var HugoCmd = &cobra.Command{ + Use: "hugo", + Short: "Hugo is a very fast static site generator", + ValidArgs: []string{"one", "two"} + Args: cobra.OnlyValidArgs + Run: func(cmd *cobra.Command, args []string) { + // args will only have the values one, two + // or the cmd.Execute() will fail. + }, +} +``` ## Example diff --git a/vendor/src/github.com/spf13/cobra/args.go b/vendor/src/github.com/spf13/cobra/args.go new file mode 100644 index 0000000000..639fae7cd0 --- /dev/null +++ b/vendor/src/github.com/spf13/cobra/args.go @@ -0,0 +1,98 @@ +package cobra + +import ( + "fmt" +) + +type PositionalArgs func(cmd *Command, args []string) error + +// Legacy arg validation has the following behaviour: +// - root commands with no subcommands can take arbitrary arguments +// - root commands with subcommands will do subcommand validity checking +// - subcommands will always accept arbitrary arguments +func legacyArgs(cmd *Command, args []string) error { + // no subcommand, always take args + if !cmd.HasSubCommands() { + return nil + } + + // root command with subcommands, do subcommand checking + if !cmd.HasParent() && len(args) > 0 { + return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])) + } + return nil +} + +// NoArgs returns an error if any args are included +func NoArgs(cmd *Command, args []string) error { + if len(args) > 0 { + return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath()) + } + return nil +} + +// OnlyValidArgs returns an error if any args are not in the list of ValidArgs +func OnlyValidArgs(cmd *Command, args []string) error { + if len(cmd.ValidArgs) > 0 { + for _, v := range args { + if !stringInSlice(v, cmd.ValidArgs) { + return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0])) + } + } + } + return nil +} + +func stringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} + +// ArbitraryArgs never returns an error +func ArbitraryArgs(cmd *Command, args []string) error { + return nil +} + +// MinimumNArgs returns an error if there is not at least N args +func MinimumNArgs(n int) PositionalArgs { + return func(cmd *Command, args []string) error { + if len(args) < n { + return fmt.Errorf("requires at least %d arg(s), only received %d", n, len(args)) + } + return nil + } +} + +// MaximumNArgs returns an error if there are more than N args +func MaximumNArgs(n int) PositionalArgs { + return func(cmd *Command, args []string) error { + if len(args) > n { + return fmt.Errorf("accepts at most %d arg(s), received %d", n, len(args)) + } + return nil + } +} + +// ExactArgs returns an error if there are not exactly n args +func ExactArgs(n int) PositionalArgs { + return func(cmd *Command, args []string) error { + if len(args) != n { + return fmt.Errorf("accepts %d arg(s), received %d", n, len(args)) + } + return nil + } +} + +// RangeArgs returns an error if the number of args is not within the expected range +func RangeArgs(min int, max int) PositionalArgs { + return func(cmd *Command, args []string) error { + if len(args) < min || len(args) > max { + return fmt.Errorf("accepts between %d and %d arg(s), received %d", min, max, len(args)) + } + return nil + } +} diff --git a/vendor/src/github.com/spf13/cobra/command.go b/vendor/src/github.com/spf13/cobra/command.go index aa3f33aa8a..f516af9b0e 100644 --- a/vendor/src/github.com/spf13/cobra/command.go +++ b/vendor/src/github.com/spf13/cobra/command.go @@ -50,6 +50,8 @@ type Command struct { // List of aliases for ValidArgs. These are not suggested to the user in the bash // completion, but accepted if entered manually. ArgAliases []string + // Expected arguments + Args PositionalArgs // Custom functions used by the bash autocompletion generator BashCompletionFunction string // Is this command deprecated and should print this string when used? @@ -110,6 +112,7 @@ type Command struct { output *io.Writer // nil means stderr; use Out() method instead usageFunc func(*Command) error // Usage can be defined by application usageTemplate string // Can be defined by Application + flagErrorFunc func(*Command, error) error helpTemplate string // Can be defined by Application helpFunc func(*Command, []string) // Help can be defined by application helpCommand *Command // The help command @@ -163,6 +166,12 @@ 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 +} + // Can be defined by Application func (c *Command) SetHelpFunc(f func(*Command, []string)) { c.helpFunc = f @@ -224,6 +233,22 @@ func (c *Command) HelpFunc() func(*Command, []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 func (c *Command) UsagePadding() int { @@ -422,33 +447,29 @@ func (c *Command) Find(args []string) (*Command, []string, error) { } commandFound, a := innerfind(c, args) - argsWOflags := stripFlags(a, commandFound) - - // no subcommand, always take args - if !commandFound.HasSubCommands() { - return commandFound, a, nil + if commandFound.Args == nil { + return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound)) } - - // root command with subcommands, do subcommand checking - if commandFound == c && len(argsWOflags) > 0 { - suggestionsString := "" - if !c.DisableSuggestions { - if c.SuggestionsMinimumDistance <= 0 { - c.SuggestionsMinimumDistance = 2 - } - if suggestions := c.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 { - suggestionsString += "\n\nDid you mean this?\n" - for _, s := range suggestions { - suggestionsString += fmt.Sprintf("\t%v\n", s) - } - } - } - return commandFound, a, fmt.Errorf("unknown command %q for %q%s", argsWOflags[0], commandFound.CommandPath(), suggestionsString) - } - return commandFound, a, nil } +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 +} + func (c *Command) SuggestionsFor(typedName string) []string { suggestions := []string{} for _, cmd := range c.commands { @@ -520,7 +541,7 @@ func (c *Command) execute(a []string) (err error) { err = c.ParseFlags(a) if err != nil { - return err + 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. @@ -535,6 +556,10 @@ func (c *Command) execute(a []string) (err error) { return flag.ErrHelp } + if err := c.ValidateArgs(a); err != nil { + return err + } + c.preRun() argWoFlags := c.Flags().Args() @@ -673,7 +698,15 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { return cmd, nil } +func (c *Command) ValidateArgs(args []string) error { + if c.Args == nil { + return nil + } + return c.Args(c, stripFlags(args, c)) +} + func (c *Command) initHelpFlag() { + c.mergePersistentFlags() if c.Flags().Lookup("help") == nil { c.Flags().BoolP("help", "h", false, "help for "+c.Name()) } @@ -747,7 +780,7 @@ func (c *Command) AddCommand(cmds ...*Command) { } } -// AddCommand removes one or more commands from a parent command. +// RemoveCommand removes one or more commands from a parent command. func (c *Command) RemoveCommand(cmds ...*Command) { commands := []*Command{} main: