bump(github.com/spf13/cobra):e1e66f7b4e667751cf530ddb6e72b79d6eeb0235

This commit is contained in:
Maciej Szulik 2014-12-19 21:55:52 +01:00
parent 824122f410
commit b28cb1a6dd
5 changed files with 93 additions and 13 deletions

2
Godeps/Godeps.json generated
View File

@ -161,7 +161,7 @@
}, },
{ {
"ImportPath": "github.com/spf13/cobra", "ImportPath": "github.com/spf13/cobra",
"Rev": "b1e90a7943957b51bb96a13b44b844475bcf95c0" "Rev": "e1e66f7b4e667751cf530ddb6e72b79d6eeb0235"
}, },
{ {
"ImportPath": "github.com/spf13/pflag", "ImportPath": "github.com/spf13/pflag",

View File

@ -120,7 +120,9 @@ In this example we are attaching it to the root, but commands can be attached at
### Assign flags to a command ### Assign flags to a command
Since the flags are defined and used in different locations, we need to define a variable outside with the correct scope to assign the flag to work with. Since the flags are defined and used in different locations, we need to
define a variable outside with the correct scope to assign the flag to
work with.
var Verbose bool var Verbose bool
var Source string var Source string

View File

@ -31,12 +31,16 @@ var initializers []func()
// Set this to true to enable it // Set this to true to enable it
var EnablePrefixMatching bool = false var EnablePrefixMatching bool = false
//OnInitialize takes a series of func() arguments and appends them to a slice of func().
func OnInitialize(y ...func()) { func OnInitialize(y ...func()) {
for _, x := range y { for _, x := range y {
initializers = append(initializers, x) initializers = append(initializers, x)
} }
} }
//Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans,
//Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as
//ints and then compared.
func Gt(a interface{}, b interface{}) bool { func Gt(a interface{}, b interface{}) bool {
var left, right int64 var left, right int64
av := reflect.ValueOf(a) av := reflect.ValueOf(a)
@ -64,6 +68,7 @@ func Gt(a interface{}, b interface{}) bool {
return left > right return left > right
} }
//Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic.
func Eq(a interface{}, b interface{}) bool { func Eq(a interface{}, b interface{}) bool {
av := reflect.ValueOf(a) av := reflect.ValueOf(a)
bv := reflect.ValueOf(b) bv := reflect.ValueOf(b)
@ -79,6 +84,7 @@ func Eq(a interface{}, b interface{}) bool {
return false return false
} }
//rpad adds padding to the right of a string
func rpad(s string, padding int) string { func rpad(s string, padding int) string {
template := fmt.Sprintf("%%-%ds", padding) template := fmt.Sprintf("%%-%ds", padding)
return fmt.Sprintf(template, s) return fmt.Sprintf(template, s)

View File

@ -136,6 +136,24 @@ func noRRSetupTest(input string) resulter {
return fullTester(c, input) return fullTester(c, input)
} }
func rootOnlySetupTest(input string) resulter {
c := initializeWithRootCmd()
return simpleTester(c, input)
}
func simpleTester(c *Command, input string) resulter {
buf := new(bytes.Buffer)
// Testing flag with invalid input
c.SetOutput(buf)
c.SetArgs(strings.Split(input, " "))
err := c.Execute()
output := buf.String()
return resulter{err, output, c}
}
func fullTester(c *Command, input string) resulter { func fullTester(c *Command, input string) resulter {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
// Testing flag with invalid input // Testing flag with invalid input
@ -156,6 +174,12 @@ func checkResultContains(t *testing.T, x resulter, check string) {
} }
} }
func checkResultOmits(t *testing.T, x resulter, check string) {
if strings.Contains(x.Output, check) {
t.Errorf("Unexpected response.\nExpecting to omit: \n %q\nGot:\n %q\n", check, x.Output)
}
}
func checkOutputContains(t *testing.T, c *Command, check string) { func checkOutputContains(t *testing.T, c *Command, check string) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
c.SetOutput(buf) c.SetOutput(buf)
@ -437,6 +461,7 @@ func TestRootHelp(t *testing.T) {
x := fullSetupTest("--help") x := fullSetupTest("--help")
checkResultContains(t, x, "Available Commands:") checkResultContains(t, x, "Available Commands:")
checkResultContains(t, x, "for more information about that command")
if strings.Contains(x.Output, "unknown flag: --help") { if strings.Contains(x.Output, "unknown flag: --help") {
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
@ -445,6 +470,7 @@ func TestRootHelp(t *testing.T) {
x = fullSetupTest("echo --help") x = fullSetupTest("echo --help")
checkResultContains(t, x, "Available Commands:") checkResultContains(t, x, "Available Commands:")
checkResultContains(t, x, "for more information about that command")
if strings.Contains(x.Output, "unknown flag: --help") { if strings.Contains(x.Output, "unknown flag: --help") {
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output) t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
@ -452,6 +478,26 @@ func TestRootHelp(t *testing.T) {
} }
func TestRootNoCommandHelp(t *testing.T) {
x := rootOnlySetupTest("--help")
checkResultOmits(t, x, "Available Commands:")
checkResultOmits(t, x, "for more information about that command")
if strings.Contains(x.Output, "unknown flag: --help") {
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
}
x = rootOnlySetupTest("echo --help")
checkResultOmits(t, x, "Available Commands:")
checkResultOmits(t, x, "for more information about that command")
if strings.Contains(x.Output, "unknown flag: --help") {
t.Errorf("--help shouldn't trigger an error, Got: \n %s", x.Output)
}
}
func TestFlagsBeforeCommand(t *testing.T) { func TestFlagsBeforeCommand(t *testing.T) {
// short without space // short without space
x := fullSetupTest("-i10 echo") x := fullSetupTest("-i10 echo")

View File

@ -11,9 +11,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Commands similar to git, go tools and other modern CLI tools //Package cobra is a commander providing a simple interface to create powerful modern CLI interfaces.
// inspired by go, go-Commander, gh and subcommand //In addition to providing an interface, Cobra simultaneously provides a controller to organize your application code.
package cobra package cobra
import ( import (
@ -57,8 +56,9 @@ type Command struct {
commandsMaxCommandPathLen int commandsMaxCommandPathLen int
flagErrorBuf *bytes.Buffer flagErrorBuf *bytes.Buffer
cmdErrorBuf *bytes.Buffer
args []string args []string // actual args parsed from flags
output *io.Writer // nil means stderr; use Out() method instead output *io.Writer // nil means stderr; use Out() method instead
usageFunc func(*Command) error // Usage can be defined by application usageFunc func(*Command) error // Usage can be defined by application
usageTemplate string // Can be defined by Application usageTemplate string // Can be defined by Application
@ -172,6 +172,7 @@ func (c *Command) UsagePadding() int {
var minCommandPathPadding int = 11 var minCommandPathPadding int = 11
//
func (c *Command) CommandPathPadding() int { func (c *Command) CommandPathPadding() int {
if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen { if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen {
return minCommandPathPadding return minCommandPathPadding
@ -203,9 +204,9 @@ Available Commands: {{range .Commands}}{{if .Runnable}}
{{.Flags.FlagUsages}}{{end}}{{if .HasParent}}{{if and (gt .Commands 0) (gt .Parent.Commands 1) }} {{.Flags.FlagUsages}}{{end}}{{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}} 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}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}}
{{end}} {{end}}{{ if .HasSubCommands }}
Use "{{.Root.Name}} help [command]" for more information about that command. Use "{{.Root.Name}} help [command]" for more information about that command.
` {{end}}`
} }
} }
@ -355,10 +356,22 @@ func (c *Command) execute(a []string) (err error) {
err = c.ParseFlags(a) err = c.ParseFlags(a)
if err != nil { if err != nil {
// We're writing subcommand usage to root command's error buffer to have it displayed to the user
r := c.Root()
if r.cmdErrorBuf == nil {
r.cmdErrorBuf = new(bytes.Buffer)
}
// for writing the usage to the buffer we need to switch the output temporarily
// since Out() returns root output, you also need to revert that on root
out := r.Out()
r.SetOutput(r.cmdErrorBuf)
c.Usage()
r.SetOutput(out)
return err return err
} else { } else {
// If help is called, regardless of other flags, we print that // If help is called, regardless of other flags, we print that.
if c.helpFlagVal { // Print help also if c.Run is nil.
if c.helpFlagVal || !c.Runnable() {
c.Help() c.Help()
return nil return nil
} }
@ -430,7 +443,12 @@ func (c *Command) Execute() (err error) {
// Flags parsing had an error. // Flags parsing had an error.
// If an error happens here, we have to report it to the user // If an error happens here, we have to report it to the user
c.Println(c.errorMsgFromParse()) c.Println(c.errorMsgFromParse())
c.Usage() // If an error happens search also for subcommand info about that
if c.cmdErrorBuf != nil && c.cmdErrorBuf.Len() > 0 {
c.Println(c.cmdErrorBuf.String())
} else {
c.Usage()
}
return e return e
} else { } else {
// If help is called, regardless of other flags, we print that // If help is called, regardless of other flags, we print that
@ -465,6 +483,10 @@ func (c *Command) Execute() (err error) {
func (c *Command) initHelp() { func (c *Command) initHelp() {
if c.helpCommand == nil { if c.helpCommand == nil {
if !c.HasSubCommands() {
return
}
c.helpCommand = &Command{ c.helpCommand = &Command{
Use: "help [command]", Use: "help [command]",
Short: "Help about any command", Short: "Help about any command",
@ -479,13 +501,17 @@ func (c *Command) initHelp() {
// Used for testing // Used for testing
func (c *Command) ResetCommands() { func (c *Command) ResetCommands() {
c.commands = nil c.commands = nil
c.helpCommand = nil
c.cmdErrorBuf = new(bytes.Buffer)
c.cmdErrorBuf.Reset()
} }
//Commands returns a slice of child commands.
func (c *Command) Commands() []*Command { func (c *Command) Commands() []*Command {
return c.commands return c.commands
} }
// Add one or many commands as children of this // AddCommand adds one or more commands to this parent command.
func (c *Command) AddCommand(cmds ...*Command) { func (c *Command) AddCommand(cmds ...*Command) {
for i, x := range cmds { for i, x := range cmds {
if cmds[i] == c { if cmds[i] == c {
@ -549,7 +575,7 @@ func (c *Command) UsageString() string {
return bb.String() return bb.String()
} }
// The full path to this command // CommandPath returns the full path to this command.
func (c *Command) CommandPath() string { func (c *Command) CommandPath() string {
str := c.Name() str := c.Name()
x := c x := c