Merge pull request #27855 from andreykurilin/cobra_update

Automatic merge from submit-queue

Update github.com/spf13/pflag and github.com/spf13/cobra

Update github.com/spf13/pflag and github.com/spf13/cobra
    
Update:
    github.com/spf13/cobra to f62e98d28ab7ad31d707ba837a966378465c7b57
    github.com/spf13/cobra/doc to f62e98d28ab7ad31d707ba837a966378465c7b57
    github.com/spf13/pflag to 1560c1005499d61b80f865c04d39ca7505bf7f0b

Closes issue #29852

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.kubernetes.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.kubernetes.io/reviews/kubernetes/kubernetes/27855)
<!-- Reviewable:end -->
This commit is contained in:
Kubernetes Submit Queue 2016-08-11 19:05:13 -07:00 committed by GitHub
commit ca92a205d9
15 changed files with 410 additions and 141 deletions

6
Godeps/Godeps.json generated
View File

@ -1989,15 +1989,15 @@
}, },
{ {
"ImportPath": "github.com/spf13/cobra", "ImportPath": "github.com/spf13/cobra",
"Rev": "4c05eb1145f16d0e6bb4a3e1b6d769f4713cb41f" "Rev": "f62e98d28ab7ad31d707ba837a966378465c7b57"
}, },
{ {
"ImportPath": "github.com/spf13/cobra/doc", "ImportPath": "github.com/spf13/cobra/doc",
"Rev": "4c05eb1145f16d0e6bb4a3e1b6d769f4713cb41f" "Rev": "f62e98d28ab7ad31d707ba837a966378465c7b57"
}, },
{ {
"ImportPath": "github.com/spf13/pflag", "ImportPath": "github.com/spf13/pflag",
"Rev": "08b1a584251b5b62f458943640fc8ebd4d50aaa5" "Rev": "1560c1005499d61b80f865c04d39ca7505bf7f0b"
}, },
{ {
"ImportPath": "github.com/stretchr/objx", "ImportPath": "github.com/stretchr/objx",

View File

@ -177,7 +177,7 @@ func TestServerHelp(t *testing.T) {
x := runFull(t, "hyperkube test1 --help") x := runFull(t, "hyperkube test1 --help")
assert.NoError(t, x.err) assert.NoError(t, x.err)
assert.Contains(t, x.output, "A simple server named test1") assert.Contains(t, x.output, "A simple server named test1")
assert.Contains(t, x.output, "--help[=false]: help for hyperkube") assert.Contains(t, x.output, "-h, --help help for hyperkube")
assert.NotContains(t, x.output, "test1 Run") assert.NotContains(t, x.output, "test1 Run")
} }
@ -185,7 +185,7 @@ func TestServerFlagsBad(t *testing.T) {
x := runFull(t, "hyperkube test1 --bad-flag") x := runFull(t, "hyperkube test1 --bad-flag")
assert.EqualError(t, x.err, "unknown flag: --bad-flag") assert.EqualError(t, x.err, "unknown flag: --bad-flag")
assert.Contains(t, x.output, "A simple server named test1") assert.Contains(t, x.output, "A simple server named test1")
assert.Contains(t, x.output, "--help[=false]: help for hyperkube") assert.Contains(t, x.output, "-h, --help help for hyperkube")
assert.NotContains(t, x.output, "test1 Run") assert.NotContains(t, x.output, "test1 Run")
} }

View File

@ -125,7 +125,7 @@ func TestServerHelp(t *testing.T) {
x := runFull(t, "hyperkube test1 --help") x := runFull(t, "hyperkube test1 --help")
assert.NoError(t, x.err) assert.NoError(t, x.err)
assert.Contains(t, x.output, "A simple server named test1") assert.Contains(t, x.output, "A simple server named test1")
assert.Contains(t, x.output, "--help[=false]: help for hyperkube") assert.Contains(t, x.output, "-h, --help help for hyperkube")
assert.NotContains(t, x.output, "test1 Run") assert.NotContains(t, x.output, "test1 Run")
} }
@ -133,7 +133,7 @@ func TestServerFlagsBad(t *testing.T) {
x := runFull(t, "hyperkube test1 --bad-flag") x := runFull(t, "hyperkube test1 --bad-flag")
assert.EqualError(t, x.err, "unknown flag: --bad-flag") assert.EqualError(t, x.err, "unknown flag: --bad-flag")
assert.Contains(t, x.output, "A simple server named test1") assert.Contains(t, x.output, "A simple server named test1")
assert.Contains(t, x.output, "--help[=false]: help for hyperkube") assert.Contains(t, x.output, "-h, --help help for hyperkube")
assert.NotContains(t, x.output, "test1 Run") assert.NotContains(t, x.output, "test1 Run")
} }

View File

@ -19,6 +19,18 @@ _cgo_export.*
_testmain.go _testmain.go
# Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
*.exe *.exe
cobra.test cobra.test

View File

@ -1,9 +1,14 @@
language: go language: go
go: go:
- 1.3.3 - 1.4.3
- 1.4.2 - 1.5.4
- 1.5.1 - 1.6.3
- tip - tip
matrix:
allow_failures:
- go: tip
before_install: before_install:
- mkdir -p bin - mkdir -p bin
- curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.3/shellcheck - curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.3/shellcheck

View File

@ -22,6 +22,7 @@ Many of the most widely used Go projects are built using Cobra including:
[![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) [![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra)
[![CircleCI status](https://circleci.com/gh/spf13/cobra.png?circle-token=:circle-token "CircleCI status")](https://circleci.com/gh/spf13/cobra) [![CircleCI status](https://circleci.com/gh/spf13/cobra.png?circle-token=:circle-token "CircleCI status")](https://circleci.com/gh/spf13/cobra)
[![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra)
![cobra](https://cloud.githubusercontent.com/assets/173412/10911369/84832a8e-8212-11e5-9f82-cc96660a4794.gif) ![cobra](https://cloud.githubusercontent.com/assets/173412/10911369/84832a8e-8212-11e5-9f82-cc96660a4794.gif)
@ -171,6 +172,12 @@ func main() {
Cobra provides its own program that will create your application and add any Cobra provides its own program that will create your application and add any
commands you want. It's the easiest way to incorporate Cobra into your application. commands you want. It's the easiest way to incorporate Cobra into your application.
In order to use the cobra command, compile it using the following command:
> go install github.com/spf13/cobra/cobra
This will create the cobra executable under your go path bin directory!
### cobra init ### cobra init
The `cobra init [yourApp]` command will create your initial application code The `cobra init [yourApp]` command will create your initial application code
@ -226,13 +233,27 @@ The cobra generator will be easier to use if you provide a simple configuration
file which will help you eliminate providing a bunch of repeated information in file which will help you eliminate providing a bunch of repeated information in
flags over and over. flags over and over.
an example ~/.cobra.yaml file: An example ~/.cobra.yaml file:
```yaml ```yaml
author: Steve Francia <spf@spf13.com> author: Steve Francia <spf@spf13.com>
license: MIT license: MIT
``` ```
You can specify no license by setting `license` to `none` or you can specify
a custom license:
```yaml
license:
header: This file is part of {{ .appName }}.
text: |
{{ .copyright }}
This is my license. There are many like it, but this one is mine.
My license is my best friend. It is my life. I must master it as I must
master my life.
```
## Manually implementing Cobra ## Manually implementing Cobra
To manually implement cobra you need to create a bare main.go file and a RootCmd file. To manually implement cobra you need to create a bare main.go file and a RootCmd file.

View File

@ -116,12 +116,12 @@ __handle_reply()
fi fi
local completions local completions
if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
completions=("${must_have_one_flag[@]}")
elif [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
completions=("${must_have_one_noun[@]}")
else
completions=("${commands[@]}") completions=("${commands[@]}")
if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
completions=("${must_have_one_noun[@]}")
fi
if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
completions+=("${must_have_one_flag[@]}")
fi fi
COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
@ -167,6 +167,11 @@ __handle_flag()
must_have_one_flag=() must_have_one_flag=()
fi fi
# if you set a flag which only applies to this command, don't show subcommands
if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
commands=()
fi
# keep flag value with flagname as flaghash # keep flag value with flagname as flaghash
if [ -n "${flagvalue}" ] ; then if [ -n "${flagvalue}" ] ; then
flaghash[${flagname}]=${flagvalue} flaghash[${flagname}]=${flagvalue}
@ -263,6 +268,7 @@ func postscript(w io.Writer, name string) error {
local c=0 local c=0
local flags=() local flags=()
local two_word_flags=() local two_word_flags=()
local local_nonpersistent_flags=()
local flags_with_completion=() local flags_with_completion=()
local flags_completion=() local flags_completion=()
local commands=("%s") local commands=("%s")
@ -360,7 +366,7 @@ func writeFlagHandler(name string, annotations map[string][]string, w io.Writer)
} }
func writeShortFlag(flag *pflag.Flag, w io.Writer) error { func writeShortFlag(flag *pflag.Flag, w io.Writer) error {
b := (flag.Value.Type() == "bool") b := (len(flag.NoOptDefVal) > 0)
name := flag.Shorthand name := flag.Shorthand
format := " " format := " "
if !b { if !b {
@ -374,7 +380,7 @@ func writeShortFlag(flag *pflag.Flag, w io.Writer) error {
} }
func writeFlag(flag *pflag.Flag, w io.Writer) error { func writeFlag(flag *pflag.Flag, w io.Writer) error {
b := (flag.Value.Type() == "bool") b := (len(flag.NoOptDefVal) > 0)
name := flag.Name name := flag.Name
format := " flags+=(\"--%s" format := " flags+=(\"--%s"
if !b { if !b {
@ -387,9 +393,24 @@ func writeFlag(flag *pflag.Flag, w io.Writer) error {
return writeFlagHandler("--"+name, flag.Annotations, w) return writeFlagHandler("--"+name, flag.Annotations, w)
} }
func writeLocalNonPersistentFlag(flag *pflag.Flag, w io.Writer) error {
b := (len(flag.NoOptDefVal) > 0)
name := flag.Name
format := " local_nonpersistent_flags+=(\"--%s"
if !b {
format += "="
}
format += "\")\n"
if _, err := fmt.Fprintf(w, format, name); err != nil {
return err
}
return nil
}
func writeFlags(cmd *Command, w io.Writer) error { func writeFlags(cmd *Command, w io.Writer) error {
_, err := fmt.Fprintf(w, ` flags=() _, err := fmt.Fprintf(w, ` flags=()
two_word_flags=() two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=() flags_with_completion=()
flags_completion=() flags_completion=()
@ -397,6 +418,7 @@ func writeFlags(cmd *Command, w io.Writer) error {
if err != nil { if err != nil {
return err return err
} }
localNonPersistentFlags := cmd.LocalNonPersistentFlags()
var visitErr error var visitErr error
cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
if err := writeFlag(flag, w); err != nil { if err := writeFlag(flag, w); err != nil {
@ -409,6 +431,12 @@ func writeFlags(cmd *Command, w io.Writer) error {
return return
} }
} }
if localNonPersistentFlags.Lookup(flag.Name) != nil {
if err := writeLocalNonPersistentFlag(flag, w); err != nil {
visitErr = err
return
}
}
}) })
if visitErr != nil { if visitErr != nil {
return visitErr return visitErr

View File

@ -117,7 +117,7 @@ cmd := &cobra.Command{
``` ```
The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
the completion aglorithm if entered manually, e.g. in: the completion algorithm if entered manually, e.g. in:
```bash ```bash
# kubectl get rc [tab][tab] # kubectl get rc [tab][tab]
@ -175,7 +175,7 @@ So while there are many other files in the CWD it only shows me subdirs and thos
# Specifiy custom flag completion # Specifiy custom flag completion
Similar to the filename completion and filtering usingn cobra.BashCompFilenameExt, you can specifiy Similar to the filename completion and filtering using cobra.BashCompFilenameExt, you can specifiy
a custom flag completion function with cobra.BashCompCustom: a custom flag completion function with cobra.BashCompCustom:
```go ```go

View File

@ -41,6 +41,10 @@ var initializers []func()
// Set this to true to enable it // Set this to true to enable it
var EnablePrefixMatching = false var EnablePrefixMatching = false
//EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
//To disable sorting, set it to false.
var EnableCommandSorting = true
//AddTemplateFunc adds a template function that's available to Usage and Help //AddTemplateFunc adds a template function that's available to Usage and Help
//template generation. //template generation.
func AddTemplateFunc(name string, tmplFunc interface{}) { func AddTemplateFunc(name string, tmplFunc interface{}) {

View File

@ -21,6 +21,7 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"strings" "strings"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
@ -103,11 +104,13 @@ type Command struct {
commandsMaxUseLen int commandsMaxUseLen int
commandsMaxCommandPathLen int commandsMaxCommandPathLen int
commandsMaxNameLen int commandsMaxNameLen int
// is commands slice are sorted or not
commandsAreSorted bool
flagErrorBuf *bytes.Buffer flagErrorBuf *bytes.Buffer
args []string // actual args parsed from flags args []string // actual args parsed from flags
output *io.Writer // nil means stderr; use Out() method instead output *io.Writer // out writer if set in SetOutput(w)
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
helpTemplate string // Can be defined by Application helpTemplate string // Can be defined by Application
@ -120,6 +123,9 @@ type Command struct {
DisableSuggestions bool DisableSuggestions bool
// If displaying suggestions, allows to set the minimum levenshtein distance to display, must be > 0 // If displaying suggestions, allows to set the minimum levenshtein distance to display, must be > 0
SuggestionsMinimumDistance int SuggestionsMinimumDistance int
// Disable the flag parsing. If this is true all flags will be passed to the command as arguments.
DisableFlagParsing bool
} }
// os.Args[1:] by default, if desired, can be overridden // os.Args[1:] by default, if desired, can be overridden
@ -128,25 +134,6 @@ func (c *Command) SetArgs(a []string) {
c.args = a c.args = a
} }
func (c *Command) getOut(def io.Writer) io.Writer {
if c.output != nil {
return *c.output
}
if c.HasParent() {
return c.parent.Out()
}
return def
}
func (c *Command) Out() io.Writer {
return c.getOut(os.Stderr)
}
func (c *Command) getOutOrStdout() io.Writer {
return c.getOut(os.Stdout)
}
// SetOutput sets the destination for usage and error messages. // SetOutput sets the destination for usage and error messages.
// If output is nil, os.Stderr is used. // If output is nil, os.Stderr is used.
func (c *Command) SetOutput(output io.Writer) { func (c *Command) SetOutput(output io.Writer) {
@ -189,6 +176,26 @@ func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string
} }
} }
func (c *Command) OutOrStdout() io.Writer {
return c.getOut(os.Stdout)
}
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
// or a parent, or it returns a default usage function
func (c *Command) UsageFunc() (f func(*Command) error) { func (c *Command) UsageFunc() (f func(*Command) error) {
if c.usageFunc != nil { if c.usageFunc != nil {
return c.usageFunc return c.usageFunc
@ -198,16 +205,24 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
return c.parent.UsageFunc() return c.parent.UsageFunc()
} }
return func(c *Command) error { return func(c *Command) error {
err := tmpl(c.Out(), c.UsageTemplate(), c) c.mergePersistentFlags()
err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c)
if err != nil { if err != nil {
fmt.Print(err) c.Println(err)
} }
return err return err
} }
} }
// Output 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 // HelpFunc returns either the function set by SetHelpFunc for this command
// or a parent, or it returns a function which calls c.Help() // or a parent, or it returns a function with default help behavior
func (c *Command) HelpFunc() func(*Command, []string) { func (c *Command) HelpFunc() func(*Command, []string) {
cmd := c cmd := c
for cmd != nil { for cmd != nil {
@ -217,13 +232,31 @@ func (c *Command) HelpFunc() func(*Command, []string) {
cmd = cmd.parent cmd = cmd.parent
} }
return func(*Command, []string) { return func(*Command, []string) {
err := c.Help() c.mergePersistentFlags()
err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c)
if err != nil { if err != nil {
c.Println(err) c.Println(err)
} }
} }
} }
// Output 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
}
func (c *Command) UsageString() string {
tmpOutput := c.output
bb := new(bytes.Buffer)
c.SetOutput(bb)
c.Usage()
c.output = tmpOutput
return bb.String()
}
var minUsagePadding = 25 var minUsagePadding = 25
func (c *Command) UsagePadding() int { func (c *Command) UsagePadding() int {
@ -261,7 +294,7 @@ func (c *Command) UsageTemplate() string {
return c.parent.UsageTemplate() return c.parent.UsageTemplate()
} }
return `Usage:{{if .Runnable}} return `Usage:{{if .Runnable}}
{{if .HasFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasSubCommands}} {{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
{{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}} {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
Aliases: Aliases:
@ -272,16 +305,16 @@ Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}} {{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if .IsAvailableCommand}} Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasLocalFlags}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
Flags: Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasInheritedFlags}} {{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}
Global Flags: Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}} {{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}} Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
` `
@ -531,12 +564,17 @@ func (c *Command) execute(a []string) (err error) {
c.Println("\"help\" flag declared as non-bool. Please correct your code") c.Println("\"help\" flag declared as non-bool. Please correct your code")
return err return err
} }
if helpVal || !c.Runnable() { if helpVal || !c.Runnable() {
return flag.ErrHelp return flag.ErrHelp
} }
c.preRun() c.preRun()
argWoFlags := c.Flags().Args() argWoFlags := c.Flags().Args()
if c.DisableFlagParsing {
argWoFlags = a
}
for p := c; p != nil; p = p.Parent() { for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil { if p.PersistentPreRunE != nil {
@ -699,8 +737,7 @@ func (c *Command) initHelpCmd() {
c.Printf("Unknown help topic %#q.", args) c.Printf("Unknown help topic %#q.", args)
c.Root().Usage() c.Root().Usage()
} else { } else {
helpFunc := cmd.HelpFunc() cmd.Help()
helpFunc(cmd, args)
} }
}, },
} }
@ -714,8 +751,20 @@ func (c *Command) ResetCommands() {
c.helpCommand = nil c.helpCommand = nil
} }
//Commands returns a slice of child commands. // 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.
func (c *Command) Commands() []*Command { 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
}
return c.commands return c.commands
} }
@ -744,10 +793,11 @@ func (c *Command) AddCommand(cmds ...*Command) {
x.SetGlobalNormalizationFunc(c.globNormFunc) x.SetGlobalNormalizationFunc(c.globNormFunc)
} }
c.commands = append(c.commands, x) c.commands = append(c.commands, x)
c.commandsAreSorted = false
} }
} }
// 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) { func (c *Command) RemoveCommand(cmds ...*Command) {
commands := []*Command{} commands := []*Command{}
main: main:
@ -781,50 +831,23 @@ main:
} }
} }
// Print is a convenience method to Print to the defined output // Print is a convenience method to Print to the defined output, fallback to Stderr if not set
func (c *Command) Print(i ...interface{}) { func (c *Command) Print(i ...interface{}) {
fmt.Fprint(c.Out(), i...) fmt.Fprint(c.OutOrStderr(), i...)
} }
// Println is a convenience method to Println to the defined output // Println is a convenience method to Println to the defined output, fallback to Stderr if not set
func (c *Command) Println(i ...interface{}) { func (c *Command) Println(i ...interface{}) {
str := fmt.Sprintln(i...) str := fmt.Sprintln(i...)
c.Print(str) c.Print(str)
} }
// Printf is a convenience method to Printf to the defined output // Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set
func (c *Command) Printf(format string, i ...interface{}) { func (c *Command) Printf(format string, i ...interface{}) {
str := fmt.Sprintf(format, i...) str := fmt.Sprintf(format, i...)
c.Print(str) c.Print(str)
} }
// Output 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 {
c.mergePersistentFlags()
err := c.UsageFunc()(c)
return err
}
// Output the help for the command
// Used when a user calls help [command]
// by the default HelpFunc in the commander
func (c *Command) Help() error {
c.mergePersistentFlags()
err := tmpl(c.getOutOrStdout(), c.HelpTemplate(), c)
return err
}
func (c *Command) UsageString() string {
tmpOutput := c.output
bb := new(bytes.Buffer)
c.SetOutput(bb)
c.Usage()
c.output = tmpOutput
return bb.String()
}
// CommandPath returns 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()
@ -1025,6 +1048,19 @@ func (c *Command) Flags() *flag.FlagSet {
return c.flags return c.flags
} }
// 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
}
// Get the local FlagSet specifically set in the current command // Get the local FlagSet specifically set in the current command
func (c *Command) LocalFlags() *flag.FlagSet { func (c *Command) LocalFlags() *flag.FlagSet {
c.mergePersistentFlags() c.mergePersistentFlags()
@ -1114,10 +1150,34 @@ func (c *Command) HasLocalFlags() bool {
return c.LocalFlags().HasFlags() return c.LocalFlags().HasFlags()
} }
// Does the command have flags inherited from its parent command
func (c *Command) HasInheritedFlags() bool { func (c *Command) HasInheritedFlags() bool {
return c.InheritedFlags().HasFlags() return c.InheritedFlags().HasFlags()
} }
// Does the command contain any flags (local plus persistent from the entire
// structure) which are not hidden or deprecated
func (c *Command) HasAvailableFlags() bool {
return c.Flags().HasAvailableFlags()
}
// Does the command contain persistent flags which are not hidden or deprecated
func (c *Command) HasAvailablePersistentFlags() bool {
return c.PersistentFlags().HasAvailableFlags()
}
// Does the command has flags specifically declared locally which are not hidden
// or deprecated
func (c *Command) HasAvailableLocalFlags() bool {
return c.LocalFlags().HasAvailableFlags()
}
// Does the command have flags inherited from its parent command which are
// not hidden or deprecated
func (c *Command) HasAvailableInheritedFlags() bool {
return c.InheritedFlags().HasAvailableFlags()
}
// Flag climbs up the command tree looking for matching flag // Flag climbs up the command tree looking for matching flag
func (c *Command) Flag(name string) (flag *flag.Flag) { func (c *Command) Flag(name string) (flag *flag.Flag) {
flag = c.Flags().Lookup(name) flag = c.Flags().Lookup(name)
@ -1143,6 +1203,9 @@ func (c *Command) persistentFlag(name string) (flag *flag.Flag) {
// ParseFlags parses persistent flag tree & local flags // ParseFlags parses persistent flag tree & local flags
func (c *Command) ParseFlags(args []string) (err error) { func (c *Command) ParseFlags(args []string) (err error) {
if c.DisableFlagParsing {
return nil
}
c.mergePersistentFlags() c.mergePersistentFlags()
err = c.Flags().Parse(args) err = c.Flags().Parse(args)
return return

View File

@ -28,12 +28,23 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
// GenManTree will generate a man page for this command and all decendants // GenManTree will generate a man page for this command and all descendants
// in the directory given. The header may be nil. This function may not work // in the directory given. The header may be nil. This function may not work
// correctly if your command names have - in them. If you have `cmd` with two // correctly if your command names have - in them. If you have `cmd` with two
// subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third` // subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third`
// it is undefined which help output will be in the file `cmd-sub-third.1`. // it is undefined which help output will be in the file `cmd-sub-third.1`.
func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error { func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error {
return GenManTreeFromOpts(cmd, GenManTreeOptions{
Header: header,
Path: dir,
CommandSeparator: "_",
})
}
// GenManTreeFromOpts generates a man page for the command and all descendants.
// The pages are written to the opts.Path directory.
func GenManTreeFromOpts(cmd *cobra.Command, opts GenManTreeOptions) error {
header := opts.Header
if header == nil { if header == nil {
header = &GenManHeader{} header = &GenManHeader{}
} }
@ -41,28 +52,35 @@ func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsHelpCommand() {
continue continue
} }
if err := GenManTree(c, header, dir); err != nil { if err := GenManTreeFromOpts(c, opts); err != nil {
return err return err
} }
} }
needToResetTitle := header.Title == "" section := "1"
if header.Section != "" {
section = header.Section
}
basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".1" separator := "_"
filename := filepath.Join(dir, basename) if opts.CommandSeparator != "" {
separator = opts.CommandSeparator
}
basename := strings.Replace(cmd.CommandPath(), " ", separator, -1)
filename := filepath.Join(opts.Path, basename + "." + section)
f, err := os.Create(filename) f, err := os.Create(filename)
if err != nil { if err != nil {
return err return err
} }
defer f.Close() defer f.Close()
if err := GenMan(cmd, header, f); err != nil { headerCopy := *header
return err return GenMan(cmd, &headerCopy, f)
} }
if needToResetTitle { type GenManTreeOptions struct {
header.Title = "" Header *GenManHeader
} Path string
return nil CommandSeparator string
} }
// GenManHeader is a lot like the .TH header at the start of man pages. These // GenManHeader is a lot like the .TH header at the start of man pages. These
@ -84,9 +102,10 @@ func GenMan(cmd *cobra.Command, header *GenManHeader, w io.Writer) error {
if header == nil { if header == nil {
header = &GenManHeader{} header = &GenManHeader{}
} }
fillHeader(header, cmd.CommandPath())
b := genMan(cmd, header) b := genMan(cmd, header)
final := mangen.Render(b) _, err := w.Write(mangen.Render(b))
_, err := w.Write(final)
return err return err
} }
@ -107,18 +126,22 @@ func fillHeader(header *GenManHeader, name string) {
} }
} }
func manPreamble(out io.Writer, header *GenManHeader, name, short, long string) { func manPreamble(out io.Writer, header *GenManHeader, cmd *cobra.Command, dashedName string) {
dashName := strings.Replace(name, " ", "-", -1) description := cmd.Long
if len(description) == 0 {
description = cmd.Short
}
fmt.Fprintf(out, `%% %s(%s)%s fmt.Fprintf(out, `%% %s(%s)%s
%% %s %% %s
%% %s %% %s
# NAME # NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual) `, header.Title, header.Section, header.date, header.Source, header.Manual)
fmt.Fprintf(out, "%s \\- %s\n\n", dashName, short) fmt.Fprintf(out, "%s \\- %s\n\n", dashedName, cmd.Short)
fmt.Fprintf(out, "# SYNOPSIS\n") fmt.Fprintf(out, "# SYNOPSIS\n")
fmt.Fprintf(out, "**%s** [OPTIONS]\n\n", name) fmt.Fprintf(out, "**%s**\n\n", cmd.UseLine())
fmt.Fprintf(out, "# DESCRIPTION\n") fmt.Fprintf(out, "# DESCRIPTION\n")
fmt.Fprintf(out, "%s\n\n", long) fmt.Fprintf(out, "%s\n\n", description)
} }
func manPrintFlags(out io.Writer, flags *pflag.FlagSet) { func manPrintFlags(out io.Writer, flags *pflag.FlagSet) {
@ -127,10 +150,10 @@ func manPrintFlags(out io.Writer, flags *pflag.FlagSet) {
return return
} }
format := "" format := ""
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 {
format = "**-%s**, **--%s**" format = fmt.Sprintf("**-%s**, **--%s**", flag.Shorthand, flag.Name)
} else { } else {
format = "%s**--%s**" format = fmt.Sprintf("**--%s**", flag.Name)
} }
if len(flag.NoOptDefVal) > 0 { if len(flag.NoOptDefVal) > 0 {
format = format + "[" format = format + "["
@ -145,7 +168,7 @@ func manPrintFlags(out io.Writer, flags *pflag.FlagSet) {
format = format + "]" format = format + "]"
} }
format = format + "\n\t%s\n\n" format = format + "\n\t%s\n\n"
fmt.Fprintf(out, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage) fmt.Fprintf(out, format, flag.DefValue, flag.Usage)
}) })
} }
@ -165,22 +188,12 @@ func manPrintOptions(out io.Writer, command *cobra.Command) {
} }
func genMan(cmd *cobra.Command, header *GenManHeader) []byte { func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
// something like `rootcmd subcmd1 subcmd2`
commandName := cmd.CommandPath()
// something like `rootcmd-subcmd1-subcmd2` // something like `rootcmd-subcmd1-subcmd2`
dashCommandName := strings.Replace(commandName, " ", "-", -1) dashCommandName := strings.Replace(cmd.CommandPath(), " ", "-", -1)
fillHeader(header, commandName)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
short := cmd.Short manPreamble(buf, header, cmd, dashCommandName)
long := cmd.Long
if len(long) == 0 {
long = short
}
manPreamble(buf, header, commandName, short, long)
manPrintOptions(buf, cmd) manPrintOptions(buf, cmd)
if len(cmd.Example) > 0 { if len(cmd.Example) > 0 {
fmt.Fprintf(buf, "# EXAMPLE\n") fmt.Fprintf(buf, "# EXAMPLE\n")

View File

@ -3,9 +3,8 @@ sudo: false
language: go language: go
go: go:
- 1.3 - 1.5.4
- 1.4 - 1.6.3
- 1.5
- tip - tip
install: install:
@ -14,5 +13,5 @@ install:
- go install ./... - go install ./...
script: script:
- verify/all.sh - verify/all.sh -v
- go test ./... - go test ./...

View File

@ -85,7 +85,7 @@ fmt.Println("flagvar has value ", flagvar)
``` ```
There are helpers function to get values later if you have the FlagSet but There are helpers function to get values later if you have the FlagSet but
it was difficult to keep up with all of the the flag pointers in your code. it was difficult to keep up with all of the flag pointers in your code.
If you have a pflag.FlagSet with a flag called 'flagname' of type int you If you have a pflag.FlagSet with a flag called 'flagname' of type int you
can use GetInt() to get the int value. But notice that 'flagname' must exist can use GetInt() to get the int value. But notice that 'flagname' must exist
and it must be an int. GetString("flagname") will fail. and it must be an int. GetString("flagname") will fail.
@ -244,6 +244,25 @@ It is possible to mark a flag as hidden, meaning it will still function as norma
flags.MarkHidden("secretFlag") flags.MarkHidden("secretFlag")
``` ```
## Supporting Go flags when using pflag
In order to support flags defined using Go's `flag` package, they must be added to the `pflag` flagset. This is usually necessary
to support flags defined by third-party dependencies (e.g. `golang/glog`).
**Example**: You want to add the Go flags to the `CommandLine` flagset
```go
import (
goflag "flag"
flag "github.com/spf13/pflag"
)
var ip *int = flag.Int("flagname", 1234, "help message for flagname")
func main() {
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
flag.Parse()
}
```
## More info ## More info
You can see the full reference documentation of the pflag package You can see the full reference documentation of the pflag package

130
vendor/github.com/spf13/pflag/flag.go generated vendored
View File

@ -242,6 +242,17 @@ func (f *FlagSet) HasFlags() bool {
return len(f.formal) > 0 return len(f.formal) > 0
} }
// HasAvailableFlags returns a bool to indicate if the FlagSet has any flags
// definied that are not hidden or deprecated.
func (f *FlagSet) HasAvailableFlags() bool {
for _, flag := range f.formal {
if !flag.Hidden && len(flag.Deprecated) == 0 {
return true
}
}
return false
}
// VisitAll visits the command-line flags in lexicographical order, calling // VisitAll visits the command-line flags in lexicographical order, calling
// fn for each. It visits all flags, even those not set. // fn for each. It visits all flags, even those not set.
func VisitAll(fn func(*Flag)) { func VisitAll(fn func(*Flag)) {
@ -408,41 +419,123 @@ func (f *FlagSet) PrintDefaults() {
fmt.Fprintf(f.out(), "%s", usages) fmt.Fprintf(f.out(), "%s", usages)
} }
// isZeroValue guesses whether the string represents the zero
// value for a flag. It is not accurate but in practice works OK.
func isZeroValue(value string) bool {
switch value {
case "false":
return true
case "<nil>":
return true
case "":
return true
case "0":
return true
}
return false
}
// UnquoteUsage extracts a back-quoted name from the usage
// string for a flag and returns it and the un-quoted usage.
// Given "a `name` to show" it returns ("name", "a name to show").
// If there are no back quotes, the name is an educated guess of the
// type of the flag's value, or the empty string if the flag is boolean.
func UnquoteUsage(flag *Flag) (name string, usage string) {
// Look for a back-quoted name, but avoid the strings package.
usage = flag.Usage
for i := 0; i < len(usage); i++ {
if usage[i] == '`' {
for j := i + 1; j < len(usage); j++ {
if usage[j] == '`' {
name = usage[i+1 : j]
usage = usage[:i] + name + usage[j+1:]
return name, usage
}
}
break // Only one back quote; use type name.
}
}
// No explicit name, so use type if we can find one.
name = "value"
switch flag.Value.(type) {
case boolFlag:
name = ""
case *durationValue:
name = "duration"
case *float64Value:
name = "float"
case *intValue, *int64Value:
name = "int"
case *stringValue:
name = "string"
case *uintValue, *uint64Value:
name = "uint"
}
return
}
// FlagUsages Returns a string containing the usage information for all flags in // FlagUsages Returns a string containing the usage information for all flags in
// the FlagSet // the FlagSet
func (f *FlagSet) FlagUsages() string { func (f *FlagSet) FlagUsages() string {
x := new(bytes.Buffer) x := new(bytes.Buffer)
lines := make([]string, 0, len(f.formal))
maxlen := 0
f.VisitAll(func(flag *Flag) { f.VisitAll(func(flag *Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden { if len(flag.Deprecated) > 0 || flag.Hidden {
return return
} }
format := ""
line := ""
if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 { if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 {
format = " -%s, --%s" line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name)
} else { } else {
format = " %s --%s" line = fmt.Sprintf(" --%s", flag.Name)
}
varname, usage := UnquoteUsage(flag)
if len(varname) > 0 {
line += " " + varname
} }
if len(flag.NoOptDefVal) > 0 { if len(flag.NoOptDefVal) > 0 {
format = format + "[" switch flag.Value.Type() {
case "string":
line += fmt.Sprintf("[=%q]", flag.NoOptDefVal)
case "bool":
if flag.NoOptDefVal != "true" {
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
} }
default:
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
}
}
// This special character will be replaced with spacing once the
// correct alignment is calculated
line += "\x00"
if len(line) > maxlen {
maxlen = len(line)
}
line += usage
if !isZeroValue(flag.DefValue) {
if flag.Value.Type() == "string" { if flag.Value.Type() == "string" {
// put quotes on the value line += fmt.Sprintf(" (default %q)", flag.DefValue)
format = format + "=%q"
} else { } else {
format = format + "=%s" line += fmt.Sprintf(" (default %s)", flag.DefValue)
} }
if len(flag.NoOptDefVal) > 0 {
format = format + "]"
} }
format = format + ": %s\n"
shorthand := flag.Shorthand lines = append(lines, line)
if len(flag.ShorthandDeprecated) > 0 {
shorthand = ""
}
fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage)
}) })
for _, line := range lines {
sidx := strings.Index(line, "\x00")
spacing := strings.Repeat(" ", maxlen-sidx)
fmt.Fprintln(x, line[:sidx], spacing, line[sidx+1:])
}
return x.String() return x.String()
} }
@ -463,6 +556,8 @@ func defaultUsage(f *FlagSet) {
// Usage prints to standard error a usage message documenting all defined command-line flags. // Usage prints to standard error a usage message documenting all defined command-line flags.
// The function is a variable that may be changed to point to a custom function. // The function is a variable that may be changed to point to a custom function.
// By default it prints a simple header and calls PrintDefaults; for details about the
// format of the output and how to control it, see the documentation for PrintDefaults.
var Usage = func() { var Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
PrintDefaults() PrintDefaults()
@ -683,6 +778,9 @@ func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error)
} }
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string) (outShorts string, outArgs []string, err error) { func (f *FlagSet) parseSingleShortArg(shorthands string, args []string) (outShorts string, outArgs []string, err error) {
if strings.HasPrefix(shorthands, "test.") {
return
}
outArgs = args outArgs = args
outShorts = shorthands[1:] outShorts = shorthands[1:]
c := shorthands[0] c := shorthands[0]
@ -806,7 +904,7 @@ func Parsed() bool {
return CommandLine.Parsed() return CommandLine.Parsed()
} }
// The default set of command-line flags, parsed from os.Args. // CommandLine is the default set of command-line flags, parsed from os.Args.
var CommandLine = NewFlagSet(os.Args[0], ExitOnError) var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
// NewFlagSet returns a new, empty flag set with the specified name and // NewFlagSet returns a new, empty flag set with the specified name and

View File

@ -61,6 +61,9 @@ func (v *flagValueWrapper) Type() string {
} }
// PFlagFromGoFlag will return a *pflag.Flag given a *flag.Flag // PFlagFromGoFlag will return a *pflag.Flag given a *flag.Flag
// If the *flag.Flag.Name was a single character (ex: `v`) it will be accessiblei
// with both `-v` and `--v` in flags. If the golang flag was more than a single
// character (ex: `verbose`) it will only be accessible via `--verbose`
func PFlagFromGoFlag(goflag *goflag.Flag) *Flag { func PFlagFromGoFlag(goflag *goflag.Flag) *Flag {
// Remember the default value as a string; it won't change. // Remember the default value as a string; it won't change.
flag := &Flag{ flag := &Flag{
@ -71,6 +74,10 @@ func PFlagFromGoFlag(goflag *goflag.Flag) *Flag {
//DefValue: goflag.DefValue, //DefValue: goflag.DefValue,
DefValue: goflag.Value.String(), DefValue: goflag.Value.String(),
} }
// Ex: if the golang flag was -v, allow both -v and --v to work
if len(flag.Name) == 1 {
flag.Shorthand = flag.Name
}
if fv, ok := goflag.Value.(goBoolFlag); ok && fv.IsBoolFlag() { if fv, ok := goflag.Value.(goBoolFlag); ok && fv.IsBoolFlag() {
flag.NoOptDefVal = "true" flag.NoOptDefVal = "true"
} }