2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-26 14:56:29 +00:00

Update spf13/cobra

This commit is contained in:
Alexander Neumann 2017-04-18 21:35:33 +02:00
parent 27c5a2825a
commit 5a7e463ef6
17 changed files with 572 additions and 91 deletions

2
vendor/manifest vendored
View File

@ -64,7 +64,7 @@
{ {
"importpath": "github.com/spf13/cobra", "importpath": "github.com/spf13/cobra",
"repository": "https://github.com/spf13/cobra", "repository": "https://github.com/spf13/cobra",
"revision": "9c28e4bbd74e5c3ed7aacbc552b2cab7cfdfe744", "revision": "b6cb3958937245a12d4d7728be080a6c758f4136",
"branch": "master" "branch": "master"
}, },
{ {

View File

@ -8,6 +8,7 @@ Many of the most widely used Go projects are built using Cobra including:
* [Hugo](http://gohugo.io) * [Hugo](http://gohugo.io)
* [rkt](https://github.com/coreos/rkt) * [rkt](https://github.com/coreos/rkt)
* [etcd](https://github.com/coreos/etcd) * [etcd](https://github.com/coreos/etcd)
* [Docker](https://github.com/docker/docker)
* [Docker (distribution)](https://github.com/docker/distribution) * [Docker (distribution)](https://github.com/docker/distribution)
* [OpenShift](https://www.openshift.com/) * [OpenShift](https://www.openshift.com/)
* [Delve](https://github.com/derekparker/delve) * [Delve](https://github.com/derekparker/delve)
@ -157,12 +158,17 @@ In a Cobra app, typically the main.go file is very bare. It serves, one purpose,
```go ```go
package main package main
import "{pathToYourApp}/cmd" import (
"fmt"
"os"
"{pathToYourApp}/cmd"
)
func main() { func main() {
if err := cmd.RootCmd.Execute(); err != nil { if err := cmd.RootCmd.Execute(); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(-1) os.Exit(1)
} }
} }
``` ```
@ -313,12 +319,17 @@ In a Cobra app, typically the main.go file is very bare. It serves, one purpose,
```go ```go
package main package main
import "{pathToYourApp}/cmd" import (
"fmt"
"os"
"{pathToYourApp}/cmd"
)
func main() { func main() {
if err := cmd.RootCmd.Execute(); err != nil { if err := cmd.RootCmd.Execute(); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(-1) os.Exit(1)
} }
} }
``` ```
@ -337,6 +348,7 @@ package cmd
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"fmt"
) )
func init() { func init() {
@ -744,7 +756,7 @@ providing a way to handle the errors in one location. The current list of functi
* PersistentPostRunE * PersistentPostRunE
If you would like to silence the default `error` and `usage` output in favor of your own, you can set `SilenceUsage` If you would like to silence the default `error` and `usage` output in favor of your own, you can set `SilenceUsage`
and `SilenceErrors` to `false` on the command. A child command respects these flags if they are set on the parent and `SilenceErrors` to `true` on the command. A child command respects these flags if they are set on the parent
command. command.
**Example Usage using RunE:** **Example Usage using RunE:**

View File

@ -10,6 +10,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
// Annotations for Bash completion.
const ( const (
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions"
BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompCustom = "cobra_annotation_bash_completion_custom"
@ -22,7 +23,7 @@ func preamble(out io.Writer, name string) error {
if err != nil { if err != nil {
return err return err
} }
_, err = fmt.Fprint(out, ` preamStr := `
__debug() __debug()
{ {
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
@ -87,8 +88,8 @@ __handle_reply()
local index flag local index flag
flag="${cur%%=*}" flag="${cur%%=*}"
__index_of_word "${flag}" "${flags_with_completion[@]}" __index_of_word "${flag}" "${flags_with_completion[@]}"
COMPREPLY=()
if [[ ${index} -ge 0 ]]; then if [[ ${index} -ge 0 ]]; then
COMPREPLY=()
PREFIX="" PREFIX=""
cur="${cur#*=}" cur="${cur#*=}"
${flags_completion[${index}]} ${flags_completion[${index}]}
@ -224,7 +225,7 @@ __handle_command()
fi fi
c=$((c+1)) c=$((c+1))
__debug "${FUNCNAME[0]}: looking for ${next_command}" __debug "${FUNCNAME[0]}: looking for ${next_command}"
declare -F $next_command >/dev/null && $next_command declare -F "$next_command" >/dev/null && $next_command
} }
__handle_word() __handle_word()
@ -246,7 +247,8 @@ __handle_word()
__handle_word __handle_word
} }
`) `
_, err = fmt.Fprint(out, preamStr)
return err return err
} }
@ -566,6 +568,7 @@ func gen(cmd *Command, w io.Writer) error {
return nil return nil
} }
// GenBashCompletion generates bash completion file and writes to the passed writer.
func (cmd *Command) GenBashCompletion(w io.Writer) error { func (cmd *Command) GenBashCompletion(w io.Writer) error {
if err := preamble(w, cmd.Name()); err != nil { if err := preamble(w, cmd.Name()); err != nil {
return err return err
@ -585,6 +588,7 @@ func nonCompletableFlag(flag *pflag.Flag) bool {
return flag.Hidden || len(flag.Deprecated) > 0 return flag.Hidden || len(flag.Deprecated) > 0
} }
// GenBashCompletionFile generates bash completion file.
func (cmd *Command) GenBashCompletionFile(filename string) error { func (cmd *Command) GenBashCompletionFile(filename string) error {
outFile, err := os.Create(filename) outFile, err := os.Create(filename)
if err != nil { if err != nil {

View File

@ -18,7 +18,7 @@ func main() {
} }
``` ```
That will get you completions of subcommands and flags. If you make additional annotations to your code, you can get even more intelligent and flexible behavior. `out.sh` will get you completions of subcommands and flags. Copy it to `/etc/bash_completion.d/` as described [here](https://debian-administration.org/article/316/An_introduction_to_bash_completion_part_1) and reset your terminal to use autocompletion. If you make additional annotations to your code, you can get even more intelligent and flexible behavior.
## Creating your own custom functions ## Creating your own custom functions

View File

@ -37,7 +37,8 @@ var templateFuncs = template.FuncMap{
var initializers []func() var initializers []func()
// Automatic prefix matching can be a dangerous thing to automatically enable in CLI tools. // EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing
// to automatically enable in CLI tools.
// Set this to true to enable it. // Set this to true to enable it.
var EnablePrefixMatching = false var EnablePrefixMatching = false
@ -51,7 +52,7 @@ func AddTemplateFunc(name string, tmplFunc interface{}) {
templateFuncs[name] = tmplFunc templateFuncs[name] = tmplFunc
} }
// AddTemplateFuncs adds multiple template functions availalble to Usage and // AddTemplateFuncs adds multiple template functions that are available to Usage and
// Help template generation. // Help template generation.
func AddTemplateFuncs(tmplFuncs template.FuncMap) { func AddTemplateFuncs(tmplFuncs template.FuncMap) {
for k, v := range tmplFuncs { for k, v := range tmplFuncs {

View File

@ -612,7 +612,7 @@ func TestSubcommandExecuteC(t *testing.T) {
Use: "echo message", Use: "echo message",
Run: func(c *Command, args []string) { Run: func(c *Command, args []string) {
msg := strings.Join(args, " ") msg := strings.Join(args, " ")
c.Println(msg, msg) c.Println(msg)
}, },
} }

View File

@ -57,6 +57,9 @@ type Command struct {
Deprecated string Deprecated string
// Is this command hidden and should NOT show up in the list of available commands? // Is this command hidden and should NOT show up in the list of available commands?
Hidden bool Hidden bool
// Annotations are key/value pairs that can be used by applications to identify or
// group commands
Annotations map[string]string
// Full set of flags // Full set of flags
flags *flag.FlagSet flags *flag.FlagSet
// Set of flags childrens of this command will inherit // Set of flags childrens of this command will inherit
@ -109,10 +112,11 @@ type Command struct {
flagErrorBuf *bytes.Buffer flagErrorBuf *bytes.Buffer
args []string // actual args parsed from flags args []string // actual args parsed from flags
output *io.Writer // out writer if set in SetOutput(w) 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
flagErrorFunc func(*Command, error) error
helpTemplate string // Can be defined by Application helpTemplate string // Can be defined by Application
helpFunc func(*Command, []string) // Help can be defined by application helpFunc func(*Command, []string) // Help can be defined by application
helpCommand *Command // The help command helpCommand *Command // The help command
@ -128,7 +132,7 @@ type Command struct {
DisableFlagParsing bool DisableFlagParsing bool
} }
// os.Args[1:] by default, if desired, can be overridden // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden
// particularly useful when testing. // particularly useful when testing.
func (c *Command) SetArgs(a []string) { func (c *Command) SetArgs(a []string) {
c.args = a c.args = a
@ -137,29 +141,36 @@ func (c *Command) SetArgs(a []string) {
// 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) {
c.output = &output c.output = output
} }
// Usage can be defined by application. // SetUsageFunc sets usage function. Usage can be defined by application.
func (c *Command) SetUsageFunc(f func(*Command) error) { func (c *Command) SetUsageFunc(f func(*Command) error) {
c.usageFunc = f c.usageFunc = f
} }
// Can be defined by Application. // SetUsageTemplate sets usage template. Can be defined by Application.
func (c *Command) SetUsageTemplate(s string) { func (c *Command) SetUsageTemplate(s string) {
c.usageTemplate = s c.usageTemplate = s
} }
// Can be defined by Application. // 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)) { func (c *Command) SetHelpFunc(f func(*Command, []string)) {
c.helpFunc = f c.helpFunc = f
} }
// SetHelpCommand sets help command.
func (c *Command) SetHelpCommand(cmd *Command) { func (c *Command) SetHelpCommand(cmd *Command) {
c.helpCommand = cmd c.helpCommand = cmd
} }
// Can be defined by Application. // SetHelpTemplate sets help template to be used. Application can use it to set custom template.
func (c *Command) SetHelpTemplate(s string) { func (c *Command) SetHelpTemplate(s string) {
c.helpTemplate = s c.helpTemplate = s
} }
@ -176,17 +187,19 @@ func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string
} }
} }
// OutOrStdout returns output to stdout.
func (c *Command) OutOrStdout() io.Writer { func (c *Command) OutOrStdout() io.Writer {
return c.getOut(os.Stdout) return c.getOut(os.Stdout)
} }
// OutOrStderr returns output to stderr
func (c *Command) OutOrStderr() io.Writer { func (c *Command) OutOrStderr() io.Writer {
return c.getOut(os.Stderr) return c.getOut(os.Stderr)
} }
func (c *Command) getOut(def io.Writer) io.Writer { func (c *Command) getOut(def io.Writer) io.Writer {
if c.output != nil { if c.output != nil {
return *c.output return c.output
} }
if c.HasParent() { if c.HasParent() {
return c.parent.getOut(def) return c.parent.getOut(def)
@ -224,12 +237,8 @@ func (c *Command) Usage() error {
// 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 with default help behavior. // 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 if helpFunc := c.checkHelpFunc(); helpFunc != nil {
for cmd != nil { return helpFunc
if cmd.helpFunc != nil {
return cmd.helpFunc
}
cmd = cmd.parent
} }
return func(*Command, []string) { return func(*Command, []string) {
c.mergePersistentFlags() c.mergePersistentFlags()
@ -240,6 +249,20 @@ func (c *Command) HelpFunc() func(*Command, []string) {
} }
} }
// checkHelpFunc checks if there is helpFunc in ancestors of c.
func (c *Command) checkHelpFunc() func(*Command, []string) {
if c == nil {
return nil
}
if c.helpFunc != nil {
return c.helpFunc
}
if c.HasParent() {
return c.parent.checkHelpFunc()
}
return nil
}
// Help puts out the help for the command. // Help puts out the help for the command.
// Used when a user calls help [command]. // Used when a user calls help [command].
// Can be defined by user by overriding HelpFunc. // Can be defined by user by overriding HelpFunc.
@ -248,6 +271,7 @@ func (c *Command) Help() error {
return nil return nil
} }
// UsageString return usage string.
func (c *Command) UsageString() string { func (c *Command) UsageString() string {
tmpOutput := c.output tmpOutput := c.output
bb := new(bytes.Buffer) bb := new(bytes.Buffer)
@ -257,8 +281,25 @@ func (c *Command) UsageString() string {
return bb.String() 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 var minUsagePadding = 25
// UsagePadding return padding for the usage.
func (c *Command) UsagePadding() int { func (c *Command) UsagePadding() int {
if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen { if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen {
return minUsagePadding return minUsagePadding
@ -268,7 +309,7 @@ func (c *Command) UsagePadding() int {
var minCommandPathPadding = 11 var minCommandPathPadding = 11
// // CommandPathPadding return padding for the command path.
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
@ -278,6 +319,7 @@ func (c *Command) CommandPathPadding() int {
var minNamePadding = 11 var minNamePadding = 11
// NamePadding returns padding for the name.
func (c *Command) NamePadding() int { func (c *Command) NamePadding() int {
if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen { if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen {
return minNamePadding return minNamePadding
@ -285,6 +327,7 @@ func (c *Command) NamePadding() int {
return c.parent.commandsMaxNameLen return c.parent.commandsMaxNameLen
} }
// UsageTemplate returns usage template for the command.
func (c *Command) UsageTemplate() string { func (c *Command) UsageTemplate() string {
if c.usageTemplate != "" { if c.usageTemplate != "" {
return c.usageTemplate return c.usageTemplate
@ -298,28 +341,28 @@ func (c *Command) UsageTemplate() string {
{{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}} {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
Aliases: Aliases:
{{.NameAndAliases}} {{.NameAndAliases}}{{end}}{{if .HasExample}}
{{end}}{{if .HasExample}}
Examples: Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}} {{ .Example }}{{end}}{{if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if .IsAvailableCommand}} Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags: Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}} {{.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 .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }} {{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}}
` `
} }
// HelpTemplate return help template for the command.
func (c *Command) HelpTemplate() string { func (c *Command) HelpTemplate() string {
if c.helpTemplate != "" { if c.helpTemplate != "" {
return c.helpTemplate return c.helpTemplate
@ -340,20 +383,18 @@ func (c *Command) resetChildrensParents() {
} }
} }
// Test if the named flag is a boolean flag. func hasNoOptDefVal(name string, f *flag.FlagSet) bool {
func isBooleanFlag(name string, f *flag.FlagSet) bool {
flag := f.Lookup(name) flag := f.Lookup(name)
if flag == nil { if flag == nil {
return false return false
} }
return flag.Value.Type() == "bool" return len(flag.NoOptDefVal) > 0
} }
// Test if the named flag is a boolean flag. func shortHasNoOptDefVal(name string, fs *flag.FlagSet) bool {
func isBooleanShortFlag(name string, f *flag.FlagSet) bool {
result := false result := false
f.VisitAll(func(f *flag.Flag) { fs.VisitAll(func(flag *flag.Flag) {
if f.Shorthand == name && f.Value.Type() == "bool" { if flag.Shorthand == name && len(flag.NoOptDefVal) > 0 {
result = true result = true
} }
}) })
@ -379,13 +420,13 @@ func stripFlags(args []string, c *Command) []string {
inQuote = true inQuote = true
case strings.HasPrefix(y, "--") && !strings.Contains(y, "="): case strings.HasPrefix(y, "--") && !strings.Contains(y, "="):
// TODO: this isn't quite right, we should really check ahead for 'true' or 'false' // TODO: this isn't quite right, we should really check ahead for 'true' or 'false'
inFlag = !isBooleanFlag(y[2:], c.Flags()) inFlag = !hasNoOptDefVal(y[2:], c.Flags())
case strings.HasPrefix(y, "-") && !strings.Contains(y, "=") && len(y) == 2 && !isBooleanShortFlag(y[1:], c.Flags()): case strings.HasPrefix(y, "-") && !strings.Contains(y, "=") && len(y) == 2 && !shortHasNoOptDefVal(y[1:], c.Flags()):
inFlag = true inFlag = true
case inFlag: case inFlag:
inFlag = false inFlag = false
case y == "": case y == "":
// strip empty commands, as the go tests expect this to be ok.... // strip empty commands, as the go tests expect this to be ok....
case !strings.HasPrefix(y, "-"): case !strings.HasPrefix(y, "-"):
commands = append(commands, y) commands = append(commands, y)
inFlag = false inFlag = false
@ -414,7 +455,7 @@ func argsMinusFirstX(args []string, x string) []string {
return args return args
} }
// find the target command given the args and command tree // Find the target command given the args and command tree
// Meant to be run on the highest node. Only searches down. // Meant to be run on the highest node. Only searches down.
func (c *Command) Find(args []string) (*Command, []string, error) { func (c *Command) Find(args []string) (*Command, []string, error) {
if c == nil { if c == nil {
@ -482,6 +523,7 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
return commandFound, a, nil return commandFound, a, nil
} }
// SuggestionsFor provides suggestions for the typedName.
func (c *Command) SuggestionsFor(typedName string) []string { func (c *Command) SuggestionsFor(typedName string) []string {
suggestions := []string{} suggestions := []string{}
for _, cmd := range c.commands { for _, cmd := range c.commands {
@ -502,6 +544,7 @@ func (c *Command) SuggestionsFor(typedName string) []string {
return suggestions return suggestions
} }
// VisitParents visits all parents of the command and invokes fn on each parent.
func (c *Command) VisitParents(fn func(*Command)) { func (c *Command) VisitParents(fn func(*Command)) {
var traverse func(*Command) *Command var traverse func(*Command) *Command
@ -517,6 +560,7 @@ func (c *Command) VisitParents(fn func(*Command)) {
traverse(c) traverse(c)
} }
// Root finds root command.
func (c *Command) Root() *Command { func (c *Command) Root() *Command {
var findRoot func(*Command) *Command var findRoot func(*Command) *Command
@ -553,7 +597,7 @@ func (c *Command) execute(a []string) (err error) {
err = c.ParseFlags(a) err = c.ParseFlags(a)
if err != nil { if err != nil {
return err return c.FlagErrorFunc()(c, err)
} }
// If help is called, regardless of other flags, return we want help // If help is called, regardless of other flags, return we want help
// Also say we need help if the command isn't runnable. // Also say we need help if the command isn't runnable.
@ -641,7 +685,7 @@ func (c *Command) errorMsgFromParse() string {
return "" return ""
} }
// Call execute to use the args (os.Args[1:] by default) // Execute Call execute to use the args (os.Args[1:] by default)
// and run through the command tree finding appropriate matches // and run through the command tree finding appropriate matches
// for commands and then corresponding flags. // for commands and then corresponding flags.
func (c *Command) Execute() error { func (c *Command) Execute() error {
@ -649,8 +693,8 @@ func (c *Command) Execute() error {
return err return err
} }
// ExecuteC executes the command.
func (c *Command) ExecuteC() (cmd *Command, err error) { func (c *Command) ExecuteC() (cmd *Command, err error) {
// Regardless of what command execute is called on, run on Root only // Regardless of what command execute is called on, run on Root only
if c.HasParent() { if c.HasParent() {
return c.Root().ExecuteC() return c.Root().ExecuteC()
@ -712,6 +756,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
} }
func (c *Command) initHelpFlag() { func (c *Command) initHelpFlag() {
c.mergePersistentFlags()
if c.Flags().Lookup("help") == nil { if c.Flags().Lookup("help") == nil {
c.Flags().BoolP("help", "h", false, "help for "+c.Name()) c.Flags().BoolP("help", "h", false, "help for "+c.Name())
} }
@ -734,7 +779,7 @@ func (c *Command) initHelpCmd() {
Run: func(c *Command, args []string) { Run: func(c *Command, args []string) {
cmd, _, e := c.Root().Find(args) cmd, _, e := c.Root().Find(args)
if cmd == nil || e != nil { if cmd == nil || e != nil {
c.Printf("Unknown help topic %#q.", args) c.Printf("Unknown help topic %#q\n", args)
c.Root().Usage() c.Root().Usage()
} else { } else {
cmd.Help() cmd.Help()
@ -742,10 +787,11 @@ func (c *Command) initHelpCmd() {
}, },
} }
} }
c.RemoveCommand(c.helpCommand)
c.AddCommand(c.helpCommand) c.AddCommand(c.helpCommand)
} }
// Used for testing. // ResetCommands used for testing.
func (c *Command) ResetCommands() { func (c *Command) ResetCommands() {
c.commands = nil c.commands = nil
c.helpCommand = nil c.helpCommand = nil
@ -868,7 +914,7 @@ func (c *Command) UseLine() string {
return str + c.Use return str + c.Use
} }
// For use in determining which flags have been assigned to which commands // DebugFlags used to determine which flags have been assigned to which commands
// and which persist. // and which persist.
func (c *Command) DebugFlags() { func (c *Command) DebugFlags() {
c.Println("DebugFlags called on", c.Name()) c.Println("DebugFlags called on", c.Name())
@ -923,7 +969,8 @@ func (c *Command) Name() string {
if i >= 0 { if i >= 0 {
name = name[:i] name = name[:i]
} }
return name c.name = name
return c.name
} }
// HasAlias determines if a given string is an alias of the command. // HasAlias determines if a given string is an alias of the command.
@ -936,10 +983,12 @@ func (c *Command) HasAlias(s string) bool {
return false return false
} }
// NameAndAliases returns string containing name and all aliases
func (c *Command) NameAndAliases() string { func (c *Command) NameAndAliases() string {
return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ") return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ")
} }
// HasExample determines if the command has example.
func (c *Command) HasExample() bool { func (c *Command) HasExample() bool {
return len(c.Example) > 0 return len(c.Example) > 0
} }
@ -972,11 +1021,12 @@ func (c *Command) IsAvailableCommand() bool {
return false return false
} }
// IsHelpCommand determines if a command is a 'help' command; a help command is // IsAdditionalHelpTopicCommand determines if a command is an additional
// determined by the fact that it is NOT runnable/hidden/deprecated, and has no // help topic command; additional help topic command is determined by the
// sub commands that are runnable/hidden/deprecated. // fact that it is NOT runnable/hidden/deprecated, and has no sub commands that
func (c *Command) IsHelpCommand() bool { // 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 a command is runnable, deprecated, or hidden it is not a 'help' command
if c.Runnable() || len(c.Deprecated) != 0 || c.Hidden { if c.Runnable() || len(c.Deprecated) != 0 || c.Hidden {
return false return false
@ -984,7 +1034,7 @@ func (c *Command) IsHelpCommand() bool {
// if any non-help sub commands are found, the command is not a 'help' command // if any non-help sub commands are found, the command is not a 'help' command
for _, sub := range c.commands { for _, sub := range c.commands {
if !sub.IsHelpCommand() { if !sub.IsAdditionalHelpTopicCommand() {
return false return false
} }
} }
@ -997,10 +1047,9 @@ func (c *Command) IsHelpCommand() bool {
// that need to be shown in the usage/help default template under 'additional help // that need to be shown in the usage/help default template under 'additional help
// topics'. // topics'.
func (c *Command) HasHelpSubCommands() bool { func (c *Command) HasHelpSubCommands() bool {
// return true on the first found available 'help' sub command // return true on the first found available 'help' sub command
for _, sub := range c.commands { for _, sub := range c.commands {
if sub.IsHelpCommand() { if sub.IsAdditionalHelpTopicCommand() {
return true return true
} }
} }
@ -1012,7 +1061,6 @@ func (c *Command) HasHelpSubCommands() bool {
// HasAvailableSubCommands determines if a command has available sub commands that // HasAvailableSubCommands determines if a command has available sub commands that
// need to be shown in the usage/help default template under 'available commands'. // need to be shown in the usage/help default template under 'available commands'.
func (c *Command) HasAvailableSubCommands() bool { func (c *Command) HasAvailableSubCommands() bool {
// return true on the first found available (non deprecated/help/hidden) // return true on the first found available (non deprecated/help/hidden)
// sub command // sub command
for _, sub := range c.commands { for _, sub := range c.commands {
@ -1036,7 +1084,7 @@ func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) f
return c.globNormFunc return c.globNormFunc
} }
// Flage returns the complete FlagSet that applies // Flags returns the complete FlagSet that applies
// to this command (local and persistent declared here and by all parents). // to this command (local and persistent declared here and by all parents).
func (c *Command) Flags() *flag.FlagSet { func (c *Command) Flags() *flag.FlagSet {
if c.flags == nil { if c.flags == nil {
@ -1136,44 +1184,44 @@ func (c *Command) ResetFlags() {
c.pflags.SetOutput(c.flagErrorBuf) c.pflags.SetOutput(c.flagErrorBuf)
} }
// Does the command contain any flags (local plus persistent from the entire structure). // HasFlags checks if the command contains any flags (local plus persistent from the entire structure).
func (c *Command) HasFlags() bool { func (c *Command) HasFlags() bool {
return c.Flags().HasFlags() return c.Flags().HasFlags()
} }
// Does the command contain persistent flags. // HasPersistentFlags checks if the command contains persistent flags.
func (c *Command) HasPersistentFlags() bool { func (c *Command) HasPersistentFlags() bool {
return c.PersistentFlags().HasFlags() return c.PersistentFlags().HasFlags()
} }
// Does the command has flags specifically declared locally. // HasLocalFlags checks if the command has flags specifically declared locally.
func (c *Command) HasLocalFlags() bool { func (c *Command) HasLocalFlags() bool {
return c.LocalFlags().HasFlags() return c.LocalFlags().HasFlags()
} }
// Does the command have flags inherited from its parent command. // HasInheritedFlags checks if the command has 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 // HasAvailableFlags checks if the command contains any flags (local plus persistent from the entire
// structure) which are not hidden or deprecated. // structure) which are not hidden or deprecated.
func (c *Command) HasAvailableFlags() bool { func (c *Command) HasAvailableFlags() bool {
return c.Flags().HasAvailableFlags() return c.Flags().HasAvailableFlags()
} }
// Does the command contain persistent flags which are not hidden or deprecated. // HasAvailablePersistentFlags checks if the command contains persistent flags which are not hidden or deprecated.
func (c *Command) HasAvailablePersistentFlags() bool { func (c *Command) HasAvailablePersistentFlags() bool {
return c.PersistentFlags().HasAvailableFlags() return c.PersistentFlags().HasAvailableFlags()
} }
// Does the command has flags specifically declared locally which are not hidden // HasAvailableLocalFlags checks if the command has flags specifically declared locally which are not hidden
// or deprecated. // or deprecated.
func (c *Command) HasAvailableLocalFlags() bool { func (c *Command) HasAvailableLocalFlags() bool {
return c.LocalFlags().HasAvailableFlags() return c.LocalFlags().HasAvailableFlags()
} }
// Does the command have flags inherited from its parent command which are // HasAvailableInheritedFlags checks if the command has flags inherited from its parent command which are
// not hidden or deprecated. // not hidden or deprecated.
func (c *Command) HasAvailableInheritedFlags() bool { func (c *Command) HasAvailableInheritedFlags() bool {
return c.InheritedFlags().HasAvailableFlags() return c.InheritedFlags().HasAvailableFlags()

View File

@ -1,6 +1,8 @@
package cobra package cobra
import ( import (
"bytes"
"fmt"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@ -134,6 +136,20 @@ func Test_DisableFlagParsing(t *testing.T) {
} }
} }
func TestInitHelpFlagMergesFlags(t *testing.T) {
usage := "custom flag"
baseCmd := Command{Use: "testcmd"}
baseCmd.PersistentFlags().Bool("help", false, usage)
cmd := Command{Use: "do"}
baseCmd.AddCommand(&cmd)
cmd.initHelpFlag()
actual := cmd.Flags().Lookup("help").Usage
if actual != usage {
t.Fatalf("Expected the help flag from the base command with usage '%s', but got the default with usage '%s'", usage, actual)
}
}
func TestCommandsAreSorted(t *testing.T) { func TestCommandsAreSorted(t *testing.T) {
EnableCommandSorting = true EnableCommandSorting = true
@ -174,3 +190,35 @@ func TestEnableCommandSortingIsDisabled(t *testing.T) {
EnableCommandSorting = true EnableCommandSorting = true
} }
func TestSetOutput(t *testing.T) {
cmd := &Command{}
cmd.SetOutput(nil)
if out := cmd.OutOrStdout(); out != os.Stdout {
t.Fatalf("expected setting output to nil to revert back to stdout, got %v", out)
}
}
func TestFlagErrorFunc(t *testing.T) {
cmd := &Command{
Use: "print",
RunE: func(cmd *Command, args []string) error {
return nil
},
}
expectedFmt := "This is expected: %s"
cmd.SetFlagErrorFunc(func(c *Command, err error) error {
return fmt.Errorf(expectedFmt, err)
})
cmd.SetArgs([]string{"--bogus-flag"})
cmd.SetOutput(new(bytes.Buffer))
err := cmd.Execute()
expected := fmt.Sprintf(expectedFmt, "unknown flag: --bogus-flag")
if err.Error() != expected {
t.Errorf("expected %v, got %v", expected, err.Error())
}
}

View File

@ -37,7 +37,7 @@ func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error {
return GenManTreeFromOpts(cmd, GenManTreeOptions{ return GenManTreeFromOpts(cmd, GenManTreeOptions{
Header: header, Header: header,
Path: dir, Path: dir,
CommandSeparator: "_", CommandSeparator: "-",
}) })
} }
@ -49,7 +49,7 @@ func GenManTreeFromOpts(cmd *cobra.Command, opts GenManTreeOptions) error {
header = &GenManHeader{} header = &GenManHeader{}
} }
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
if err := GenManTreeFromOpts(c, opts); err != nil { if err := GenManTreeFromOpts(c, opts); err != nil {
@ -216,7 +216,7 @@ func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
children := cmd.Commands() children := cmd.Commands()
sort.Sort(byName(children)) sort.Sort(byName(children))
for _, c := range children { for _, c := range children {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
seealso := fmt.Sprintf("**%s-%s(%s)**", dashCommandName, c.Name(), header.Section) seealso := fmt.Sprintf("**%s-%s(%s)**", dashCommandName, c.Name(), header.Section)

View File

@ -15,7 +15,7 @@ func main() {
Use: "test", Use: "test",
Short: "my test program", Short: "my test program",
} }
header := &cobra.GenManHeader{ header := &doc.GenManHeader{
Title: "MINE", Title: "MINE",
Section: "3", Section: "3",
} }
@ -23,4 +23,4 @@ func main() {
} }
``` ```
That will get you a man page `/tmp/test.1` That will get you a man page `/tmp/test.3`

View File

@ -8,7 +8,7 @@ import (
"github.com/spf13/cobra/doc" "github.com/spf13/cobra/doc"
) )
func ExampleCommand_GenManTree() { func ExampleGenManTree() {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "test", Use: "test",
Short: "my test program", Short: "my test program",
@ -20,7 +20,7 @@ func ExampleCommand_GenManTree() {
doc.GenManTree(cmd, header, "/tmp") doc.GenManTree(cmd, header, "/tmp")
} }
func ExampleCommand_GenMan() { func ExampleGenMan() {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "test", Use: "test",
Short: "my test program", Short: "my test program",

View File

@ -119,7 +119,7 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
sort.Sort(byName(children)) sort.Sort(byName(children))
for _, child := range children { for _, child := range children {
if !child.IsAvailableCommand() || child.IsHelpCommand() { if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue continue
} }
cname := name + " " + child.Name() cname := name + " " + child.Name()
@ -149,7 +149,7 @@ func GenMarkdownTree(cmd *cobra.Command, dir string) error {
func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error { func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
if err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil { if err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil {

View File

@ -32,15 +32,15 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd" "k8s.io/kubernetes/pkg/kubectl/cmd"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra/doc" "github.com/spf13/cobra/doc"
) )
func main() { func main() {
cmd := kubectlcmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
doc.GenMarkdownTree(cmd, "./") doc.GenMarkdownTree(kubectl, "./")
} }
``` ```
@ -101,4 +101,3 @@ linkHandler := func(name string) string {
return "/commands/" + strings.ToLower(base) + "/" return "/commands/" + strings.ToLower(base) + "/"
} }
``` ```

View File

@ -13,7 +13,11 @@
package doc package doc
import "github.com/spf13/cobra" import (
"strings"
"github.com/spf13/cobra"
)
// Test to see if we have a reason to print See Also information in docs // Test to see if we have a reason to print See Also information in docs
// Basically this is a test for a parent commend or a subcommand which is // Basically this is a test for a parent commend or a subcommand which is
@ -23,7 +27,7 @@ func hasSeeAlso(cmd *cobra.Command) bool {
return true return true
} }
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsHelpCommand() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue continue
} }
return true return true
@ -31,6 +35,15 @@ func hasSeeAlso(cmd *cobra.Command) bool {
return false return false
} }
// Temporary workaround for yaml lib generating incorrect yaml with long strings
// that do not contain \n.
func forceMultiLine(s string) string {
if len(s) > 60 && !strings.Contains(s, "\n") {
s = s + "\n"
}
return s
}
type byName []*cobra.Command type byName []*cobra.Command
func (s byName) Len() int { return len(s) } func (s byName) Len() int { return len(s) }

View File

@ -0,0 +1,165 @@
// Copyright 2016 French Ben. All rights reserved.
//
// 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.
package doc
import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v2"
)
type cmdOption struct {
Name string
Shorthand string `yaml:",omitempty"`
DefaultValue string `yaml:"default_value,omitempty"`
Usage string `yaml:",omitempty"`
}
type cmdDoc struct {
Name string
Synopsis string `yaml:",omitempty"`
Description string `yaml:",omitempty"`
Options []cmdOption `yaml:",omitempty"`
InheritedOptions []cmdOption `yaml:"inherited_options,omitempty"`
Example string `yaml:",omitempty"`
SeeAlso []string `yaml:"see_also,omitempty"`
}
// GenYamlTree creates yaml structured ref files for this command and all descendants
// in the directory given. This function may not work
// 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`
// it is undefined which help output will be in the file `cmd-sub-third.1`.
func GenYamlTree(cmd *cobra.Command, dir string) error {
identity := func(s string) string { return s }
emptyStr := func(s string) string { return "" }
return GenYamlTreeCustom(cmd, dir, emptyStr, identity)
}
// GenYamlTreeCustom creates yaml structured ref files
func GenYamlTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error {
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue
}
if err := GenYamlTreeCustom(c, dir, filePrepender, linkHandler); err != nil {
return err
}
}
basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".yaml"
filename := filepath.Join(dir, basename)
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
if _, err := io.WriteString(f, filePrepender(filename)); err != nil {
return err
}
if err := GenYamlCustom(cmd, f, linkHandler); err != nil {
return err
}
return nil
}
// GenYaml creates yaml output
func GenYaml(cmd *cobra.Command, w io.Writer) error {
return GenYamlCustom(cmd, w, func(s string) string { return s })
}
// GenYamlCustom creates custom yaml output
func GenYamlCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error {
yamlDoc := cmdDoc{}
yamlDoc.Name = cmd.CommandPath()
yamlDoc.Synopsis = forceMultiLine(cmd.Short)
yamlDoc.Description = forceMultiLine(cmd.Long)
if len(cmd.Example) > 0 {
yamlDoc.Example = cmd.Example
}
flags := cmd.NonInheritedFlags()
if flags.HasFlags() {
yamlDoc.Options = genFlagResult(flags)
}
flags = cmd.InheritedFlags()
if flags.HasFlags() {
yamlDoc.InheritedOptions = genFlagResult(flags)
}
if hasSeeAlso(cmd) {
result := []string{}
if cmd.HasParent() {
parent := cmd.Parent()
result = append(result, parent.CommandPath()+" - "+parent.Short)
}
children := cmd.Commands()
sort.Sort(byName(children))
for _, child := range children {
if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue
}
result = append(result, child.Name()+" - "+child.Short)
}
yamlDoc.SeeAlso = result
}
final, err := yaml.Marshal(&yamlDoc)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if _, err := fmt.Fprintf(w, string(final)); err != nil {
return err
}
return nil
}
func genFlagResult(flags *pflag.FlagSet) []cmdOption {
var result []cmdOption
flags.VisitAll(func(flag *pflag.Flag) {
// Todo, when we mark a shorthand is deprecated, but specify an empty message.
// The flag.ShorthandDeprecated is empty as the shorthand is deprecated.
// Using len(flag.ShorthandDeprecated) > 0 can't handle this, others are ok.
if !(len(flag.ShorthandDeprecated) > 0) && len(flag.Shorthand) > 0 {
opt := cmdOption{
flag.Name,
flag.Shorthand,
flag.DefValue,
forceMultiLine(flag.Usage),
}
result = append(result, opt)
} else {
opt := cmdOption{
Name: flag.Name,
DefaultValue: forceMultiLine(flag.DefValue),
Usage: forceMultiLine(flag.Usage),
}
result = append(result, opt)
}
})
return result
}

View File

@ -0,0 +1,103 @@
# Generating Yaml Docs For Your Own cobra.Command
Generating yaml files from a cobra command is incredibly easy. An example is as follows:
```go
package main
import (
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
func main() {
cmd := &cobra.Command{
Use: "test",
Short: "my test program",
}
doc.GenYamlTree(cmd, "/tmp")
}
```
That will get you a Yaml document `/tmp/test.yaml`
## Generate yaml docs for the entire command tree
This program can actually generate docs for the kubectl command in the kubernetes project
```go
package main
import (
"io/ioutil"
"os"
"k8s.io/kubernetes/pkg/kubectl/cmd"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra/doc"
)
func main() {
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
doc.GenYamlTree(kubectl, "./")
}
```
This will generate a whole series of files, one for each command in the tree, in the directory specified (in this case "./")
## Generate yaml docs for a single command
You may wish to have more control over the output, or only generate for a single command, instead of the entire command tree. If this is the case you may prefer to `GenYaml` instead of `GenYamlTree`
```go
out := new(bytes.Buffer)
doc.GenYaml(cmd, out)
```
This will write the yaml doc for ONLY "cmd" into the out, buffer.
## Customize the output
Both `GenYaml` and `GenYamlTree` have alternate versions with callbacks to get some control of the output:
```go
func GenYamlTreeCustom(cmd *Command, dir string, filePrepender, linkHandler func(string) string) error {
//...
}
```
```go
func GenYamlCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string) string) error {
//...
}
```
The `filePrepender` will prepend the return value given the full filepath to the rendered Yaml file. A common use case is to add front matter to use the generated documentation with [Hugo](http://gohugo.io/):
```go
const fmTemplate = `---
date: %s
title: "%s"
slug: %s
url: %s
---
`
filePrepender := func(filename string) string {
now := time.Now().Format(time.RFC3339)
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
url := "/commands/" + strings.ToLower(base) + "/"
return fmt.Sprintf(fmTemplate, now, strings.Replace(base, "_", " ", -1), base, url)
}
```
The `linkHandler` can be used to customize the rendered internal links to the commands, given a filename:
```go
linkHandler := func(name string) string {
base := strings.TrimSuffix(name, path.Ext(name))
return "/commands/" + strings.ToLower(base) + "/"
}
```

View File

@ -0,0 +1,88 @@
package doc
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
)
var _ = fmt.Println
var _ = os.Stderr
func TestGenYamlDoc(t *testing.T) {
c := initializeWithRootCmd()
// Need two commands to run the command alphabetical sort
cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated)
c.AddCommand(cmdPrint, cmdEcho)
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
out := new(bytes.Buffer)
// We generate on s subcommand so we have both subcommands and parents
if err := GenYaml(cmdEcho, out); err != nil {
t.Fatal(err)
}
found := out.String()
// Our description
expected := cmdEcho.Long
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// Better have our example
expected = cmdEcho.Example
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// A local flag
expected = "boolone"
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// persistent flag on parent
expected = "rootflag"
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// We better output info about our parent
expected = cmdRootWithRun.Short
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
// And about subcommands
expected = cmdEchoSub.Short
if !strings.Contains(found, expected) {
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
}
unexpected := cmdDeprecated.Short
if strings.Contains(found, unexpected) {
t.Errorf("Unexpected response.\nFound: %v\nBut should not have!!\n", unexpected)
}
}
func TestGenYamlNoTag(t *testing.T) {
c := initializeWithRootCmd()
// Need two commands to run the command alphabetical sort
cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated)
c.AddCommand(cmdPrint, cmdEcho)
c.DisableAutoGenTag = true
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
out := new(bytes.Buffer)
if err := GenYaml(c, out); err != nil {
t.Fatal(err)
}
found := out.String()
unexpected := "Auto generated"
checkStringOmits(t, found, unexpected)
}