Merge pull request #4723 from mitchellh/cliupdate

update cli library
This commit is contained in:
Matthew Hooker 2017-03-28 09:43:49 -07:00 committed by GitHub
commit 46b09f8aa3
4 changed files with 54 additions and 24 deletions

View File

@ -3,8 +3,11 @@
cli is a library for implementing powerful command-line interfaces in Go. cli is a library for implementing powerful command-line interfaces in Go.
cli is the library that powers the CLI for cli is the library that powers the CLI for
[Packer](https://github.com/mitchellh/packer), [Packer](https://github.com/mitchellh/packer),
[Serf](https://github.com/hashicorp/serf), and [Serf](https://github.com/hashicorp/serf),
[Consul](https://github.com/hashicorp/consul). [Consul](https://github.com/hashicorp/consul),
[Vault](https://github.com/hashicorp/vault),
[Terraform](https://github.com/hashicorp/terraform), and
[Nomad](https://github.com/hashicorp/nomad).
## Features ## Features

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"regexp"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -24,7 +25,7 @@ import (
// //
// * We use longest prefix matching to find a matching subcommand. This // * We use longest prefix matching to find a matching subcommand. This
// means if you register "foo bar" and the user executes "cli foo qux", // means if you register "foo bar" and the user executes "cli foo qux",
// the "foo" commmand will be executed with the arg "qux". It is up to // the "foo" command will be executed with the arg "qux". It is up to
// you to handle these args. One option is to just return the special // you to handle these args. One option is to just return the special
// help return code `RunResultHelp` to display help and exit. // help return code `RunResultHelp` to display help and exit.
// //
@ -119,7 +120,13 @@ func (c *CLI) Run() (int, error) {
// Just show the version and exit if instructed. // Just show the version and exit if instructed.
if c.IsVersion() && c.Version != "" { if c.IsVersion() && c.Version != "" {
c.HelpWriter.Write([]byte(c.Version + "\n")) c.HelpWriter.Write([]byte(c.Version + "\n"))
return 1, nil return 0, nil
}
// Just print the help when only '-h' or '--help' is passed.
if c.IsHelp() && c.Subcommand() == "" {
c.HelpWriter.Write([]byte(c.HelpFunc(c.Commands) + "\n"))
return 0, nil
} }
// Attempt to get the factory function for creating the command // Attempt to get the factory function for creating the command
@ -132,13 +139,13 @@ func (c *CLI) Run() (int, error) {
command, err := raw.(CommandFactory)() command, err := raw.(CommandFactory)()
if err != nil { if err != nil {
return 0, err return 1, err
} }
// If we've been instructed to just print the help, then print it // If we've been instructed to just print the help, then print it
if c.IsHelp() { if c.IsHelp() {
c.commandHelp(command) c.commandHelp(command)
return 1, nil return 0, nil
} }
// If there is an invalid flag, then error // If there is an invalid flag, then error
@ -249,7 +256,7 @@ func (c *CLI) init() {
c.commandTree.Walk(walkFn) c.commandTree.Walk(walkFn)
// Insert any that we're missing // Insert any that we're missing
for k, _ := range toInsert { for k := range toInsert {
var f CommandFactory = func() (Command, error) { var f CommandFactory = func() (Command, error) {
return &MockCommand{ return &MockCommand{
HelpText: "This command is accessed by using one of the subcommands below.", HelpText: "This command is accessed by using one of the subcommands below.",
@ -294,7 +301,7 @@ func (c *CLI) commandHelp(command Command) {
// Get the matching keys // Get the matching keys
subcommands := c.helpCommands(c.Subcommand()) subcommands := c.helpCommands(c.Subcommand())
keys := make([]string, 0, len(subcommands)) keys := make([]string, 0, len(subcommands))
for k, _ := range subcommands { for k := range subcommands {
keys = append(keys, k) keys = append(keys, k)
} }
@ -311,8 +318,13 @@ func (c *CLI) commandHelp(command Command) {
// Go through and create their structures // Go through and create their structures
subcommandsTpl = make([]map[string]interface{}, 0, len(subcommands)) subcommandsTpl = make([]map[string]interface{}, 0, len(subcommands))
for k, raw := range subcommands { for _, k := range keys {
// Get the command // Get the command
raw, ok := subcommands[k]
if !ok {
c.HelpWriter.Write([]byte(fmt.Sprintf(
"Error getting subcommand %q", k)))
}
sub, err := raw() sub, err := raw()
if err != nil { if err != nil {
c.HelpWriter.Write([]byte(fmt.Sprintf( c.HelpWriter.Write([]byte(fmt.Sprintf(
@ -382,16 +394,22 @@ func (c *CLI) helpCommands(prefix string) map[string]CommandFactory {
func (c *CLI) processArgs() { func (c *CLI) processArgs() {
for i, arg := range c.Args { for i, arg := range c.Args {
if arg == "--" {
break
}
// Check for help flags.
if arg == "-h" || arg == "-help" || arg == "--help" {
c.isHelp = true
continue
}
if c.subcommand == "" { if c.subcommand == "" {
// Check for version and help flags if not in a subcommand // Check for version flags if not in a subcommand.
if arg == "-v" || arg == "-version" || arg == "--version" { if arg == "-v" || arg == "-version" || arg == "--version" {
c.isVersion = true c.isVersion = true
continue continue
} }
if arg == "-h" || arg == "-help" || arg == "--help" {
c.isHelp = true
continue
}
if arg != "" && arg[0] == '-' { if arg != "" && arg[0] == '-' {
// Record the arg... // Record the arg...
@ -400,16 +418,24 @@ func (c *CLI) processArgs() {
} }
// If we didn't find a subcommand yet and this is the first non-flag // If we didn't find a subcommand yet and this is the first non-flag
// argument, then this is our subcommand. j // argument, then this is our subcommand.
if c.subcommand == "" && arg != "" && arg[0] != '-' { if c.subcommand == "" && arg != "" && arg[0] != '-' {
c.subcommand = arg c.subcommand = arg
if c.commandNested { if c.commandNested {
// Nested CLI, the subcommand is actually the entire // Nested CLI, the subcommand is actually the entire
// arg list up to a flag that is still a valid subcommand. // arg list up to a flag that is still a valid subcommand.
k, _, ok := c.commandTree.LongestPrefix(strings.Join(c.Args[i:], " ")) searchKey := strings.Join(c.Args[i:], " ")
k, _, ok := c.commandTree.LongestPrefix(searchKey)
if ok { if ok {
c.subcommand = k // k could be a prefix that doesn't contain the full
i += strings.Count(k, " ") // command such as "foo" instead of "foobar", so we
// need to verify that we have an entire key. To do that,
// we look for an ending in a space or an end of string.
reVerify := regexp.MustCompile(regexp.QuoteMeta(k) + `( |$)`)
if reVerify.MatchString(searchKey) {
c.subcommand = k
i += strings.Count(k, " ")
}
} }
} }
@ -434,7 +460,7 @@ const defaultHelpTemplate = `
{{.Help}}{{if gt (len .Subcommands) 0}} {{.Help}}{{if gt (len .Subcommands) 0}}
Subcommands: Subcommands:
{{ range $value := .Subcommands }} {{- range $value := .Subcommands }}
{{ $value.NameAligned }} {{ $value.Synopsis }}{{ end }} {{ $value.NameAligned }} {{ $value.Synopsis }}{{ end }}
{{ end }} {{- end }}
` `

View File

@ -18,7 +18,7 @@ func BasicHelpFunc(app string) HelpFunc {
return func(commands map[string]CommandFactory) string { return func(commands map[string]CommandFactory) string {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString(fmt.Sprintf( buf.WriteString(fmt.Sprintf(
"usage: %s [--version] [--help] <command> [<args>]\n\n", "Usage: %s [--version] [--help] <command> [<args>]\n\n",
app)) app))
buf.WriteString("Available commands are:\n") buf.WriteString("Available commands are:\n")
@ -26,7 +26,7 @@ func BasicHelpFunc(app string) HelpFunc {
// key length so they can be aligned properly. // key length so they can be aligned properly.
keys := make([]string, 0, len(commands)) keys := make([]string, 0, len(commands))
maxKeyLen := 0 maxKeyLen := 0
for key, _ := range commands { for key := range commands {
if len(key) > maxKeyLen { if len(key) > maxKeyLen {
maxKeyLen = len(key) maxKeyLen = len(key)
} }

5
vendor/vendor.json vendored
View File

@ -596,9 +596,10 @@
"revision": "56b76bdf51f7708750eac80fa38b952bb9f32639" "revision": "56b76bdf51f7708750eac80fa38b952bb9f32639"
}, },
{ {
"checksumSHA1": "WbXdGQiD4hQNID4Xo/RJumaimk0=", "checksumSHA1": "UP+pXl+ic9y6qrpZA5MqDIAuGfw=",
"path": "github.com/mitchellh/cli", "path": "github.com/mitchellh/cli",
"revision": "5c87c51cedf76a1737bf5ca3979e8644871598a6" "revision": "ee8578a9c12a5bb9d55303b9665cc448772c81b8",
"revisionTime": "2017-03-28T05:23:52Z"
}, },
{ {
"checksumSHA1": "mVqDwKcibat0IKAdzAhfGIHPwI8=", "checksumSHA1": "mVqDwKcibat0IKAdzAhfGIHPwI8=",