[chore]: Bump github.com/spf13/cobra from 1.8.1 to 1.9.1 (#3805)

Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.1 to 1.9.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2025-02-17 11:43:32 +00:00 committed by GitHub
parent f35c1cf67a
commit c73497b58c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 704 additions and 180 deletions

4
go.mod
View file

@ -52,7 +52,7 @@ require (
github.com/oklog/ulid v1.3.1 github.com/oklog/ulid v1.3.1
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.20.5
github.com/rivo/uniseg v0.4.7 github.com/rivo/uniseg v0.4.7
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/superseriousbusiness/activity v1.10.0-gts github.com/superseriousbusiness/activity v1.10.0-gts
@ -194,7 +194,7 @@ require (
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB // indirect github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB // indirect

10
go.sum generated
View file

@ -125,7 +125,7 @@ github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdk
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -497,10 +497,10 @@ github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNo
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

View file

@ -1,4 +1,5 @@
![cobra logo](assets/CobraMain.png)
![cobra logo](https://github.com/user-attachments/assets/cbc3adf8-0dff-46e9-a88d-5e2d971c169e)
Cobra is a library for creating powerful modern CLI applications. Cobra is a library for creating powerful modern CLI applications.
@ -105,7 +106,7 @@ go install github.com/spf13/cobra-cli@latest
For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)
For complete details on using the Cobra library, please read the [The Cobra User Guide](site/content/user_guide.md). For complete details on using the Cobra library, please read [The Cobra User Guide](site/content/user_guide.md).
# License # License

View file

@ -35,7 +35,7 @@
// This function can be called multiple times before and/or after completions are added to // This function can be called multiple times before and/or after completions are added to
// the array. Each time this function is called with the same array, the new // the array. Each time this function is called with the same array, the new
// ActiveHelp line will be shown below the previous ones when completion is triggered. // ActiveHelp line will be shown below the previous ones when completion is triggered.
func AppendActiveHelp(compArray []string, activeHelpStr string) []string { func AppendActiveHelp(compArray []Completion, activeHelpStr string) []Completion {
return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr)) return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr))
} }

View file

@ -146,7 +146,7 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
if (((directive & shellCompDirectiveFilterFileExt) != 0)); then if (((directive & shellCompDirectiveFilterFileExt) != 0)); then
# File extension filtering # File extension filtering
local fullFilter filter filteringCmd local fullFilter="" filter filteringCmd
# Do not use quotes around the $completions variable or else newline # Do not use quotes around the $completions variable or else newline
# characters will be kept. # characters will be kept.
@ -177,20 +177,71 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
__%[1]s_handle_special_char "$cur" = __%[1]s_handle_special_char "$cur" =
# Print the activeHelp statements before we finish # Print the activeHelp statements before we finish
if ((${#activeHelp[*]} != 0)); then __%[1]s_handle_activeHelp
printf "\n"; }
printf "%%s\n" "${activeHelp[@]}"
printf "\n"
# The prompt format is only available from bash 4.4. __%[1]s_handle_activeHelp() {
# We test if it is available before using it. # Print the activeHelp statements
if (x=${PS1@P}) 2> /dev/null; then if ((${#activeHelp[*]} != 0)); then
printf "%%s" "${PS1@P}${COMP_LINE[@]}" if [ -z $COMP_TYPE ]; then
else # Bash v3 does not set the COMP_TYPE variable.
# Can't print the prompt. Just print the printf "\n";
# text the user had typed, it is workable enough. printf "%%s\n" "${activeHelp[@]}"
printf "%%s" "${COMP_LINE[@]}" printf "\n"
__%[1]s_reprint_commandLine
return
fi fi
# Only print ActiveHelp on the second TAB press
if [ $COMP_TYPE -eq 63 ]; then
printf "\n"
printf "%%s\n" "${activeHelp[@]}"
if ((${#COMPREPLY[*]} == 0)); then
# When there are no completion choices from the program, file completion
# may kick in if the program has not disabled it; in such a case, we want
# to know if any files will match what the user typed, so that we know if
# there will be completions presented, so that we know how to handle ActiveHelp.
# To find out, we actually trigger the file completion ourselves;
# the call to _filedir will fill COMPREPLY if files match.
if (((directive & shellCompDirectiveNoFileComp) == 0)); then
__%[1]s_debug "Listing files"
_filedir
fi
fi
if ((${#COMPREPLY[*]} != 0)); then
# If there are completion choices to be shown, print a delimiter.
# Re-printing the command-line will automatically be done
# by the shell when it prints the completion choices.
printf -- "--"
else
# When there are no completion choices at all, we need
# to re-print the command-line since the shell will
# not be doing it itself.
__%[1]s_reprint_commandLine
fi
elif [ $COMP_TYPE -eq 37 ] || [ $COMP_TYPE -eq 42 ]; then
# For completion type: menu-complete/menu-complete-backward and insert-completions
# the completions are immediately inserted into the command-line, so we first
# print the activeHelp message and reprint the command-line since the shell won't.
printf "\n"
printf "%%s\n" "${activeHelp[@]}"
__%[1]s_reprint_commandLine
fi
fi
}
__%[1]s_reprint_commandLine() {
# The prompt format is only available from bash 4.4.
# We test if it is available before using it.
if (x=${PS1@P}) 2> /dev/null; then
printf "%%s" "${PS1@P}${COMP_LINE[@]}"
else
# Can't print the prompt. Just print the
# text the user had typed, it is workable enough.
printf "%%s" "${COMP_LINE[@]}"
fi fi
} }
@ -201,6 +252,8 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
local endIndex=${#activeHelpMarker} local endIndex=${#activeHelpMarker}
while IFS='' read -r comp; do while IFS='' read -r comp; do
[[ -z $comp ]] && continue
if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then
comp=${comp:endIndex} comp=${comp:endIndex}
__%[1]s_debug "ActiveHelp found: $comp" __%[1]s_debug "ActiveHelp found: $comp"
@ -223,16 +276,21 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
# If the user requested inserting one completion at a time, or all # If the user requested inserting one completion at a time, or all
# completions at once on the command-line we must remove the descriptions. # completions at once on the command-line we must remove the descriptions.
# https://github.com/spf13/cobra/issues/1508 # https://github.com/spf13/cobra/issues/1508
local tab=$'\t' comp
while IFS='' read -r comp; do # If there are no completions, we don't need to do anything
[[ -z $comp ]] && continue (( ${#completions[@]} == 0 )) && return 0
# Strip any description
comp=${comp%%%%$tab*} local tab=$'\t'
# Only consider the completions that match
if [[ $comp == "$cur"* ]]; then # Strip any description and escape the completion to handled special characters
COMPREPLY+=("$comp") IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]%%%%$tab*}")
fi
done < <(printf "%%s\n" "${completions[@]}") # Only consider the completions that match
IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}")
# compgen looses the escaping so we need to escape all completions again since they will
# all be inserted on the command-line.
IFS=$'\n' read -ra COMPREPLY -d '' < <(printf "%%q\n" "${COMPREPLY[@]}")
;; ;;
*) *)
@ -243,11 +301,25 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
} }
__%[1]s_handle_standard_completion_case() { __%[1]s_handle_standard_completion_case() {
local tab=$'\t' comp local tab=$'\t'
# If there are no completions, we don't need to do anything
(( ${#completions[@]} == 0 )) && return 0
# Short circuit to optimize if we don't have descriptions # Short circuit to optimize if we don't have descriptions
if [[ "${completions[*]}" != *$tab* ]]; then if [[ "${completions[*]}" != *$tab* ]]; then
IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur") # First, escape the completions to handle special characters
IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]}")
# Only consider the completions that match what the user typed
IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}")
# compgen looses the escaping so, if there is only a single completion, we need to
# escape it again because it will be inserted on the command-line. If there are multiple
# completions, we don't want to escape them because they will be printed in a list
# and we don't want to show escape characters in that list.
if (( ${#COMPREPLY[@]} == 1 )); then
COMPREPLY[0]=$(printf "%%q" "${COMPREPLY[0]}")
fi
return 0 return 0
fi fi
@ -256,23 +328,39 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
# Look for the longest completion so that we can format things nicely # Look for the longest completion so that we can format things nicely
while IFS='' read -r compline; do while IFS='' read -r compline; do
[[ -z $compline ]] && continue [[ -z $compline ]] && continue
# Strip any description before checking the length
comp=${compline%%%%$tab*} # Before checking if the completion matches what the user typed,
# we need to strip any description and escape the completion to handle special
# characters because those escape characters are part of what the user typed.
# Don't call "printf" in a sub-shell because it will be much slower
# since we are in a loop.
printf -v comp "%%q" "${compline%%%%$tab*}" &>/dev/null || comp=$(printf "%%q" "${compline%%%%$tab*}")
# Only consider the completions that match # Only consider the completions that match
[[ $comp == "$cur"* ]] || continue [[ $comp == "$cur"* ]] || continue
# The completions matches. Add it to the list of full completions including
# its description. We don't escape the completion because it may get printed
# in a list if there are more than one and we don't want show escape characters
# in that list.
COMPREPLY+=("$compline") COMPREPLY+=("$compline")
# Strip any description before checking the length, and again, don't escape
# the completion because this length is only used when printing the completions
# in a list and we don't want show escape characters in that list.
comp=${compline%%%%$tab*}
if ((${#comp}>longest)); then if ((${#comp}>longest)); then
longest=${#comp} longest=${#comp}
fi fi
done < <(printf "%%s\n" "${completions[@]}") done < <(printf "%%s\n" "${completions[@]}")
# If there is a single completion left, remove the description text # If there is a single completion left, remove the description text and escape any special characters
if ((${#COMPREPLY[*]} == 1)); then if ((${#COMPREPLY[*]} == 1)); then
__%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
comp="${COMPREPLY[0]%%%%$tab*}" COMPREPLY[0]=$(printf "%%q" "${COMPREPLY[0]%%%%$tab*}")
__%[1]s_debug "Removed description from single completion, which is now: ${comp}" __%[1]s_debug "Removed description from single completion, which is now: ${COMPREPLY[0]}"
COMPREPLY[0]=$comp else
else # Format the descriptions # Format the descriptions
__%[1]s_format_comp_descriptions $longest __%[1]s_format_comp_descriptions $longest
fi fi
} }

View file

@ -176,12 +176,16 @@ func rpad(s string, padding int) string {
return fmt.Sprintf(formattedString, s) return fmt.Sprintf(formattedString, s)
} }
// tmpl executes the given template text on data, writing the result to w. func tmpl(text string) *tmplFunc {
func tmpl(w io.Writer, text string, data interface{}) error { return &tmplFunc{
t := template.New("top") tmpl: text,
t.Funcs(templateFuncs) fn: func(w io.Writer, data interface{}) error {
template.Must(t.Parse(text)) t := template.New("top")
return t.Execute(w, data) t.Funcs(templateFuncs)
template.Must(t.Parse(text))
return t.Execute(w, data)
},
}
} }
// ld compares two strings and returns the levenshtein distance between them. // ld compares two strings and returns the levenshtein distance between them.

View file

@ -33,6 +33,9 @@
const ( const (
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name" CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
helpFlagName = "help"
helpCommandName = "help"
) )
// FParseErrWhitelist configures Flag parse errors to be ignored // FParseErrWhitelist configures Flag parse errors to be ignored
@ -80,11 +83,11 @@ type Command struct {
Example string Example string
// ValidArgs is list of all valid non-flag arguments that are accepted in shell completions // ValidArgs is list of all valid non-flag arguments that are accepted in shell completions
ValidArgs []string ValidArgs []Completion
// ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion. // ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion.
// It is a dynamic version of using ValidArgs. // It is a dynamic version of using ValidArgs.
// Only one of ValidArgs and ValidArgsFunction can be used for a command. // Only one of ValidArgs and ValidArgsFunction can be used for a command.
ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) ValidArgsFunction CompletionFunc
// Expected arguments // Expected arguments
Args PositionalArgs Args PositionalArgs
@ -168,12 +171,12 @@ type Command struct {
// usageFunc is usage func defined by user. // usageFunc is usage func defined by user.
usageFunc func(*Command) error usageFunc func(*Command) error
// usageTemplate is usage template defined by user. // usageTemplate is usage template defined by user.
usageTemplate string usageTemplate *tmplFunc
// flagErrorFunc is func defined by user and it's called when the parsing of // flagErrorFunc is func defined by user and it's called when the parsing of
// flags returns an error. // flags returns an error.
flagErrorFunc func(*Command, error) error flagErrorFunc func(*Command, error) error
// helpTemplate is help template defined by user. // helpTemplate is help template defined by user.
helpTemplate string helpTemplate *tmplFunc
// helpFunc is help func defined by user. // helpFunc is help func defined by user.
helpFunc func(*Command, []string) helpFunc func(*Command, []string)
// helpCommand is command with usage 'help'. If it's not defined by user, // helpCommand is command with usage 'help'. If it's not defined by user,
@ -186,7 +189,7 @@ type Command struct {
completionCommandGroupID string completionCommandGroupID string
// versionTemplate is the version template defined by user. // versionTemplate is the version template defined by user.
versionTemplate string versionTemplate *tmplFunc
// errPrefix is the error message prefix defined by user. // errPrefix is the error message prefix defined by user.
errPrefix string errPrefix string
@ -281,6 +284,7 @@ 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.
//
// Deprecated: Use SetOut and/or SetErr instead // Deprecated: Use SetOut and/or SetErr instead
func (c *Command) SetOutput(output io.Writer) { func (c *Command) SetOutput(output io.Writer) {
c.outWriter = output c.outWriter = output
@ -312,7 +316,11 @@ func (c *Command) SetUsageFunc(f func(*Command) error) {
// SetUsageTemplate sets usage template. 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 if s == "" {
c.usageTemplate = nil
return
}
c.usageTemplate = tmpl(s)
} }
// SetFlagErrorFunc sets a function to generate an error when flag parsing // SetFlagErrorFunc sets a function to generate an error when flag parsing
@ -348,12 +356,20 @@ func (c *Command) SetCompletionCommandGroupID(groupID string) {
// SetHelpTemplate sets help template to be used. Application can use it to set custom template. // 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 if s == "" {
c.helpTemplate = nil
return
}
c.helpTemplate = tmpl(s)
} }
// SetVersionTemplate sets version template to be used. Application can use it to set custom template. // SetVersionTemplate sets version template to be used. Application can use it to set custom template.
func (c *Command) SetVersionTemplate(s string) { func (c *Command) SetVersionTemplate(s string) {
c.versionTemplate = s if s == "" {
c.versionTemplate = nil
return
}
c.versionTemplate = tmpl(s)
} }
// SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix. // SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix.
@ -434,7 +450,8 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
} }
return func(c *Command) error { return func(c *Command) error {
c.mergePersistentFlags() c.mergePersistentFlags()
err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c) fn := c.getUsageTemplateFunc()
err := fn(c.OutOrStderr(), c)
if err != nil { if err != nil {
c.PrintErrln(err) c.PrintErrln(err)
} }
@ -442,6 +459,19 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
} }
} }
// getUsageTemplateFunc returns the usage template function for the command
// going up the command tree if necessary.
func (c *Command) getUsageTemplateFunc() func(w io.Writer, data interface{}) error {
if c.usageTemplate != nil {
return c.usageTemplate.fn
}
if c.HasParent() {
return c.parent.getUsageTemplateFunc()
}
return defaultUsageFunc
}
// Usage puts out the usage for the command. // Usage puts out the usage for the command.
// Used when a user provides invalid input. // Used when a user provides invalid input.
// Can be defined by user by overriding UsageFunc. // Can be defined by user by overriding UsageFunc.
@ -460,15 +490,30 @@ func (c *Command) HelpFunc() func(*Command, []string) {
} }
return func(c *Command, a []string) { return func(c *Command, a []string) {
c.mergePersistentFlags() c.mergePersistentFlags()
fn := c.getHelpTemplateFunc()
// The help should be sent to stdout // The help should be sent to stdout
// See https://github.com/spf13/cobra/issues/1002 // See https://github.com/spf13/cobra/issues/1002
err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c) err := fn(c.OutOrStdout(), c)
if err != nil { if err != nil {
c.PrintErrln(err) c.PrintErrln(err)
} }
} }
} }
// getHelpTemplateFunc returns the help template function for the command
// going up the command tree if necessary.
func (c *Command) getHelpTemplateFunc() func(w io.Writer, data interface{}) error {
if c.helpTemplate != nil {
return c.helpTemplate.fn
}
if c.HasParent() {
return c.parent.getHelpTemplateFunc()
}
return defaultHelpFunc
}
// 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.
@ -543,71 +588,55 @@ func (c *Command) NamePadding() int {
} }
// UsageTemplate returns usage template for the command. // UsageTemplate returns usage template for the command.
// This function is kept for backwards-compatibility reasons.
func (c *Command) UsageTemplate() string { func (c *Command) UsageTemplate() string {
if c.usageTemplate != "" { if c.usageTemplate != nil {
return c.usageTemplate return c.usageTemplate.tmpl
} }
if c.HasParent() { if c.HasParent() {
return c.parent.UsageTemplate() return c.parent.UsageTemplate()
} }
return `Usage:{{if .Runnable}} return defaultUsageTemplate
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
} }
// HelpTemplate return help template for the command. // HelpTemplate return help template for the command.
// This function is kept for backwards-compatibility reasons.
func (c *Command) HelpTemplate() string { func (c *Command) HelpTemplate() string {
if c.helpTemplate != "" { if c.helpTemplate != nil {
return c.helpTemplate return c.helpTemplate.tmpl
} }
if c.HasParent() { if c.HasParent() {
return c.parent.HelpTemplate() return c.parent.HelpTemplate()
} }
return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} return defaultHelpTemplate
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
} }
// VersionTemplate return version template for the command. // VersionTemplate return version template for the command.
// This function is kept for backwards-compatibility reasons.
func (c *Command) VersionTemplate() string { func (c *Command) VersionTemplate() string {
if c.versionTemplate != "" { if c.versionTemplate != nil {
return c.versionTemplate return c.versionTemplate.tmpl
} }
if c.HasParent() { if c.HasParent() {
return c.parent.VersionTemplate() return c.parent.VersionTemplate()
} }
return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}} return defaultVersionTemplate
` }
// getVersionTemplateFunc returns the version template function for the command
// going up the command tree if necessary.
func (c *Command) getVersionTemplateFunc() func(w io.Writer, data interface{}) error {
if c.versionTemplate != nil {
return c.versionTemplate.fn
}
if c.HasParent() {
return c.parent.getVersionTemplateFunc()
}
return defaultVersionFunc
} }
// ErrPrefix return error message prefix for the command // ErrPrefix return error message prefix for the command
@ -894,7 +923,7 @@ func (c *Command) execute(a []string) (err error) {
// 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.
helpVal, err := c.Flags().GetBool("help") helpVal, err := c.Flags().GetBool(helpFlagName)
if err != nil { if err != nil {
// should be impossible to get here as we always declare a help // should be impossible to get here as we always declare a help
// flag in InitDefaultHelpFlag() // flag in InitDefaultHelpFlag()
@ -914,7 +943,8 @@ func (c *Command) execute(a []string) (err error) {
return err return err
} }
if versionVal { if versionVal {
err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c) fn := c.getVersionTemplateFunc()
err := fn(c.OutOrStdout(), c)
if err != nil { if err != nil {
c.Println(err) c.Println(err)
} }
@ -1068,12 +1098,6 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
// initialize help at the last point to allow for user overriding // initialize help at the last point to allow for user overriding
c.InitDefaultHelpCmd() c.InitDefaultHelpCmd()
// initialize completion at the last point to allow for user overriding
c.InitDefaultCompletionCmd()
// Now that all commands have been created, let's make sure all groups
// are properly created also
c.checkCommandGroups()
args := c.args args := c.args
@ -1082,9 +1106,16 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
args = os.Args[1:] args = os.Args[1:]
} }
// initialize the hidden command to be used for shell completion // initialize the __complete command to be used for shell completion
c.initCompleteCmd(args) c.initCompleteCmd(args)
// initialize the default completion command
c.InitDefaultCompletionCmd(args...)
// Now that all commands have been created, let's make sure all groups
// are properly created also
c.checkCommandGroups()
var flags []string var flags []string
if c.TraverseChildren { if c.TraverseChildren {
cmd, flags, err = c.Traverse(args) cmd, flags, err = c.Traverse(args)
@ -1187,16 +1218,16 @@ func (c *Command) checkCommandGroups() {
// If c already has help flag, it will do nothing. // If c already has help flag, it will do nothing.
func (c *Command) InitDefaultHelpFlag() { func (c *Command) InitDefaultHelpFlag() {
c.mergePersistentFlags() c.mergePersistentFlags()
if c.Flags().Lookup("help") == nil { if c.Flags().Lookup(helpFlagName) == nil {
usage := "help for " usage := "help for "
name := c.displayName() name := c.DisplayName()
if name == "" { if name == "" {
usage += "this command" usage += "this command"
} else { } else {
usage += name usage += name
} }
c.Flags().BoolP("help", "h", false, usage) c.Flags().BoolP(helpFlagName, "h", false, usage)
_ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"}) _ = c.Flags().SetAnnotation(helpFlagName, FlagSetByCobraAnnotation, []string{"true"})
} }
} }
@ -1215,7 +1246,7 @@ func (c *Command) InitDefaultVersionFlag() {
if c.Name() == "" { if c.Name() == "" {
usage += "this command" usage += "this command"
} else { } else {
usage += c.Name() usage += c.DisplayName()
} }
if c.Flags().ShorthandLookup("v") == nil { if c.Flags().ShorthandLookup("v") == nil {
c.Flags().BoolP("version", "v", false, usage) c.Flags().BoolP("version", "v", false, usage)
@ -1239,9 +1270,9 @@ func (c *Command) InitDefaultHelpCmd() {
Use: "help [command]", Use: "help [command]",
Short: "Help about any command", Short: "Help about any command",
Long: `Help provides help for any command in the application. Long: `Help provides help for any command in the application.
Simply type ` + c.displayName() + ` help [path to command] for full details.`, Simply type ` + c.DisplayName() + ` help [path to command] for full details.`,
ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) { ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) {
var completions []string var completions []Completion
cmd, _, e := c.Root().Find(args) cmd, _, e := c.Root().Find(args)
if e != nil { if e != nil {
return nil, ShellCompDirectiveNoFileComp return nil, ShellCompDirectiveNoFileComp
@ -1253,7 +1284,7 @@ func (c *Command) InitDefaultHelpCmd() {
for _, subCmd := range cmd.Commands() { for _, subCmd := range cmd.Commands() {
if subCmd.IsAvailableCommand() || subCmd == cmd.helpCommand { if subCmd.IsAvailableCommand() || subCmd == cmd.helpCommand {
if strings.HasPrefix(subCmd.Name(), toComplete) { if strings.HasPrefix(subCmd.Name(), toComplete) {
completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short))
} }
} }
} }
@ -1430,10 +1461,12 @@ func (c *Command) CommandPath() string {
if c.HasParent() { if c.HasParent() {
return c.Parent().CommandPath() + " " + c.Name() return c.Parent().CommandPath() + " " + c.Name()
} }
return c.displayName() return c.DisplayName()
} }
func (c *Command) displayName() string { // DisplayName returns the name to display in help text. Returns command Name()
// If CommandDisplayNameAnnoation is not set
func (c *Command) DisplayName() string {
if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok { if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok {
return displayName return displayName
} }
@ -1443,7 +1476,7 @@ func (c *Command) displayName() string {
// UseLine puts out the full usage for a given command (including parents). // UseLine puts out the full usage for a given command (including parents).
func (c *Command) UseLine() string { func (c *Command) UseLine() string {
var useline string var useline string
use := strings.Replace(c.Use, c.Name(), c.displayName(), 1) use := strings.Replace(c.Use, c.Name(), c.DisplayName(), 1)
if c.HasParent() { if c.HasParent() {
useline = c.parent.CommandPath() + " " + use useline = c.parent.CommandPath() + " " + use
} else { } else {
@ -1649,7 +1682,7 @@ func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) f
// 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 {
c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
if c.flagErrorBuf == nil { if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf = new(bytes.Buffer)
} }
@ -1664,7 +1697,7 @@ func (c *Command) Flags() *flag.FlagSet {
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
persistentFlags := c.PersistentFlags() persistentFlags := c.PersistentFlags()
out := flag.NewFlagSet(c.displayName(), flag.ContinueOnError) out := flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
c.LocalFlags().VisitAll(func(f *flag.Flag) { c.LocalFlags().VisitAll(func(f *flag.Flag) {
if persistentFlags.Lookup(f.Name) == nil { if persistentFlags.Lookup(f.Name) == nil {
out.AddFlag(f) out.AddFlag(f)
@ -1679,7 +1712,7 @@ func (c *Command) LocalFlags() *flag.FlagSet {
c.mergePersistentFlags() c.mergePersistentFlags()
if c.lflags == nil { if c.lflags == nil {
c.lflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.lflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
if c.flagErrorBuf == nil { if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf = new(bytes.Buffer)
} }
@ -1707,7 +1740,7 @@ func (c *Command) InheritedFlags() *flag.FlagSet {
c.mergePersistentFlags() c.mergePersistentFlags()
if c.iflags == nil { if c.iflags == nil {
c.iflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.iflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
if c.flagErrorBuf == nil { if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf = new(bytes.Buffer)
} }
@ -1736,7 +1769,7 @@ func (c *Command) NonInheritedFlags() *flag.FlagSet {
// PersistentFlags returns the persistent FlagSet specifically set in the current command. // PersistentFlags returns the persistent FlagSet specifically set in the current command.
func (c *Command) PersistentFlags() *flag.FlagSet { func (c *Command) PersistentFlags() *flag.FlagSet {
if c.pflags == nil { if c.pflags == nil {
c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
if c.flagErrorBuf == nil { if c.flagErrorBuf == nil {
c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf = new(bytes.Buffer)
} }
@ -1749,9 +1782,9 @@ func (c *Command) PersistentFlags() *flag.FlagSet {
func (c *Command) ResetFlags() { func (c *Command) ResetFlags() {
c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf = new(bytes.Buffer)
c.flagErrorBuf.Reset() c.flagErrorBuf.Reset()
c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
c.flags.SetOutput(c.flagErrorBuf) c.flags.SetOutput(c.flagErrorBuf)
c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
c.pflags.SetOutput(c.flagErrorBuf) c.pflags.SetOutput(c.flagErrorBuf)
c.lflags = nil c.lflags = nil
@ -1868,7 +1901,7 @@ func (c *Command) mergePersistentFlags() {
// If c.parentsPflags == nil, it makes new. // If c.parentsPflags == nil, it makes new.
func (c *Command) updateParentsPflags() { func (c *Command) updateParentsPflags() {
if c.parentsPflags == nil { if c.parentsPflags == nil {
c.parentsPflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.parentsPflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
c.parentsPflags.SetOutput(c.flagErrorBuf) c.parentsPflags.SetOutput(c.flagErrorBuf)
c.parentsPflags.SortFlags = false c.parentsPflags.SortFlags = false
} }
@ -1894,3 +1927,141 @@ func commandNameMatches(s string, t string) bool {
return s == t return s == t
} }
// tmplFunc holds a template and a function that will execute said template.
type tmplFunc struct {
tmpl string
fn func(io.Writer, interface{}) error
}
var defaultUsageTemplate = `Usage:{{if .Runnable}}
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
// defaultUsageFunc is equivalent to executing defaultUsageTemplate. The two should be changed in sync.
func defaultUsageFunc(w io.Writer, in interface{}) error {
c := in.(*Command)
fmt.Fprint(w, "Usage:")
if c.Runnable() {
fmt.Fprintf(w, "\n %s", c.UseLine())
}
if c.HasAvailableSubCommands() {
fmt.Fprintf(w, "\n %s [command]", c.CommandPath())
}
if len(c.Aliases) > 0 {
fmt.Fprintf(w, "\n\nAliases:\n")
fmt.Fprintf(w, " %s", c.NameAndAliases())
}
if c.HasExample() {
fmt.Fprintf(w, "\n\nExamples:\n")
fmt.Fprintf(w, "%s", c.Example)
}
if c.HasAvailableSubCommands() {
cmds := c.Commands()
if len(c.Groups()) == 0 {
fmt.Fprintf(w, "\n\nAvailable Commands:")
for _, subcmd := range cmds {
if subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName {
fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short)
}
}
} else {
for _, group := range c.Groups() {
fmt.Fprintf(w, "\n\n%s", group.Title)
for _, subcmd := range cmds {
if subcmd.GroupID == group.ID && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) {
fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short)
}
}
}
if !c.AllChildCommandsHaveGroup() {
fmt.Fprintf(w, "\n\nAdditional Commands:")
for _, subcmd := range cmds {
if subcmd.GroupID == "" && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) {
fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short)
}
}
}
}
}
if c.HasAvailableLocalFlags() {
fmt.Fprintf(w, "\n\nFlags:\n")
fmt.Fprint(w, trimRightSpace(c.LocalFlags().FlagUsages()))
}
if c.HasAvailableInheritedFlags() {
fmt.Fprintf(w, "\n\nGlobal Flags:\n")
fmt.Fprint(w, trimRightSpace(c.InheritedFlags().FlagUsages()))
}
if c.HasHelpSubCommands() {
fmt.Fprintf(w, "\n\nAdditional help topcis:")
for _, subcmd := range c.Commands() {
if subcmd.IsAdditionalHelpTopicCommand() {
fmt.Fprintf(w, "\n %s %s", rpad(subcmd.CommandPath(), subcmd.CommandPathPadding()), subcmd.Short)
}
}
}
if c.HasAvailableSubCommands() {
fmt.Fprintf(w, "\n\nUse \"%s [command] --help\" for more information about a command.", c.CommandPath())
}
fmt.Fprintln(w)
return nil
}
var defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
// defaultHelpFunc is equivalent to executing defaultHelpTemplate. The two should be changed in sync.
func defaultHelpFunc(w io.Writer, in interface{}) error {
c := in.(*Command)
usage := c.Long
if usage == "" {
usage = c.Short
}
usage = trimRightSpace(usage)
if usage != "" {
fmt.Fprintln(w, usage)
fmt.Fprintln(w)
}
if c.Runnable() || c.HasSubCommands() {
fmt.Fprint(w, c.UsageString())
}
return nil
}
var defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
`
// defaultVersionFunc is equivalent to executing defaultVersionTemplate. The two should be changed in sync.
func defaultVersionFunc(w io.Writer, in interface{}) error {
c := in.(*Command)
_, err := fmt.Fprintf(w, "%s version %s\n", c.DisplayName(), c.Version)
return err
}

View file

@ -35,7 +35,7 @@
) )
// Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. // Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it.
var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} var flagCompletionFunctions = map[*pflag.Flag]CompletionFunc{}
// lock for reading and writing from flagCompletionFunctions // lock for reading and writing from flagCompletionFunctions
var flagCompletionMutex = &sync.RWMutex{} var flagCompletionMutex = &sync.RWMutex{}
@ -117,22 +117,50 @@ type CompletionOptions struct {
HiddenDefaultCmd bool HiddenDefaultCmd bool
} }
// Completion is a string that can be used for completions
//
// two formats are supported:
// - the completion choice
// - the completion choice with a textual description (separated by a TAB).
//
// [CompletionWithDesc] can be used to create a completion string with a textual description.
//
// Note: Go type alias is used to provide a more descriptive name in the documentation, but any string can be used.
type Completion = string
// CompletionFunc is a function that provides completion results.
type CompletionFunc = func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective)
// CompletionWithDesc returns a [Completion] with a description by using the TAB delimited format.
func CompletionWithDesc(choice string, description string) Completion {
return choice + "\t" + description
}
// NoFileCompletions can be used to disable file completion for commands that should // NoFileCompletions can be used to disable file completion for commands that should
// not trigger file completions. // not trigger file completions.
func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { //
// This method satisfies [CompletionFunc].
// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction].
func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) {
return nil, ShellCompDirectiveNoFileComp return nil, ShellCompDirectiveNoFileComp
} }
// FixedCompletions can be used to create a completion function which always // FixedCompletions can be used to create a completion function which always
// returns the same results. // returns the same results.
func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { //
return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { // This method returns a function that satisfies [CompletionFunc]
// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction].
func FixedCompletions(choices []Completion, directive ShellCompDirective) CompletionFunc {
return func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) {
return choices, directive return choices, directive
} }
} }
// RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag. // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error { //
// You can use pre-defined completion functions such as [FixedCompletions] or [NoFileCompletions],
// or you can define your own.
func (c *Command) RegisterFlagCompletionFunc(flagName string, f CompletionFunc) error {
flag := c.Flag(flagName) flag := c.Flag(flagName)
if flag == nil { if flag == nil {
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName) return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName)
@ -148,7 +176,7 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman
} }
// GetFlagCompletionFunc returns the completion function for the given flag of the command, if available. // GetFlagCompletionFunc returns the completion function for the given flag of the command, if available.
func (c *Command) GetFlagCompletionFunc(flagName string) (func(*Command, []string, string) ([]string, ShellCompDirective), bool) { func (c *Command) GetFlagCompletionFunc(flagName string) (CompletionFunc, bool) {
flag := c.Flag(flagName) flag := c.Flag(flagName)
if flag == nil { if flag == nil {
return nil, false return nil, false
@ -270,7 +298,15 @@ func (c *Command) initCompleteCmd(args []string) {
} }
} }
func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) { // SliceValue is a reduced version of [pflag.SliceValue]. It is used to detect
// flags that accept multiple values and therefore can provide completion
// multiple times.
type SliceValue interface {
// GetSlice returns the flag value list as an array of strings.
GetSlice() []string
}
func (c *Command) getCompletions(args []string) (*Command, []Completion, ShellCompDirective, error) {
// The last argument, which is not completely typed by the user, // The last argument, which is not completely typed by the user,
// should not be part of the list of arguments // should not be part of the list of arguments
toComplete := args[len(args)-1] toComplete := args[len(args)-1]
@ -298,7 +334,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
} }
if err != nil { if err != nil {
// Unable to find the real command. E.g., <program> someInvalidCmd <TAB> // Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs) return c, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs)
} }
finalCmd.ctx = c.ctx finalCmd.ctx = c.ctx
@ -328,7 +364,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
// Parse the flags early so we can check if required flags are set // Parse the flags early so we can check if required flags are set
if err = finalCmd.ParseFlags(finalArgs); err != nil { if err = finalCmd.ParseFlags(finalArgs); err != nil {
return finalCmd, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error()) return finalCmd, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error())
} }
realArgCount := finalCmd.Flags().NArg() realArgCount := finalCmd.Flags().NArg()
@ -340,14 +376,14 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
if flagErr != nil { if flagErr != nil {
// If error type is flagCompError and we don't want flagCompletion we should ignore the error // If error type is flagCompError and we don't want flagCompletion we should ignore the error
if _, ok := flagErr.(*flagCompError); !(ok && !flagCompletion) { if _, ok := flagErr.(*flagCompError); !(ok && !flagCompletion) {
return finalCmd, []string{}, ShellCompDirectiveDefault, flagErr return finalCmd, []Completion{}, ShellCompDirectiveDefault, flagErr
} }
} }
// Look for the --help or --version flags. If they are present, // Look for the --help or --version flags. If they are present,
// there should be no further completions. // there should be no further completions.
if helpOrVersionFlagPresent(finalCmd) { if helpOrVersionFlagPresent(finalCmd) {
return finalCmd, []string{}, ShellCompDirectiveNoFileComp, nil return finalCmd, []Completion{}, ShellCompDirectiveNoFileComp, nil
} }
// We only remove the flags from the arguments if DisableFlagParsing is not set. // We only remove the flags from the arguments if DisableFlagParsing is not set.
@ -376,11 +412,11 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
return finalCmd, subDir, ShellCompDirectiveFilterDirs, nil return finalCmd, subDir, ShellCompDirectiveFilterDirs, nil
} }
// Directory completion // Directory completion
return finalCmd, []string{}, ShellCompDirectiveFilterDirs, nil return finalCmd, []Completion{}, ShellCompDirectiveFilterDirs, nil
} }
} }
var completions []string var completions []Completion
var directive ShellCompDirective var directive ShellCompDirective
// Enforce flag groups before doing flag completions // Enforce flag groups before doing flag completions
@ -399,10 +435,14 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
// If we have not found any required flags, only then can we show regular flags // If we have not found any required flags, only then can we show regular flags
if len(completions) == 0 { if len(completions) == 0 {
doCompleteFlags := func(flag *pflag.Flag) { doCompleteFlags := func(flag *pflag.Flag) {
if !flag.Changed || _, acceptsMultiple := flag.Value.(SliceValue)
acceptsMultiple = acceptsMultiple ||
strings.Contains(flag.Value.Type(), "Slice") || strings.Contains(flag.Value.Type(), "Slice") ||
strings.Contains(flag.Value.Type(), "Array") { strings.Contains(flag.Value.Type(), "Array") ||
// If the flag is not already present, or if it can be specified multiple times (Array or Slice) strings.HasPrefix(flag.Value.Type(), "stringTo")
if !flag.Changed || acceptsMultiple {
// If the flag is not already present, or if it can be specified multiple times (Array, Slice, or stringTo)
// we suggest it as a completion // we suggest it as a completion
completions = append(completions, getFlagNameCompletions(flag, toComplete)...) completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
} }
@ -462,7 +502,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
for _, subCmd := range finalCmd.Commands() { for _, subCmd := range finalCmd.Commands() {
if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand { if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand {
if strings.HasPrefix(subCmd.Name(), toComplete) { if strings.HasPrefix(subCmd.Name(), toComplete) {
completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short))
} }
directive = ShellCompDirectiveNoFileComp directive = ShellCompDirectiveNoFileComp
} }
@ -507,7 +547,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
} }
// Find the completion function for the flag or command // Find the completion function for the flag or command
var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) var completionFn CompletionFunc
if flag != nil && flagCompletion { if flag != nil && flagCompletion {
flagCompletionMutex.RLock() flagCompletionMutex.RLock()
completionFn = flagCompletionFunctions[flag] completionFn = flagCompletionFunctions[flag]
@ -518,7 +558,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
if completionFn != nil { if completionFn != nil {
// Go custom completion defined for this flag or command. // Go custom completion defined for this flag or command.
// Call the registered completion function to get the completions. // Call the registered completion function to get the completions.
var comps []string var comps []Completion
comps, directive = completionFn(finalCmd, finalArgs, toComplete) comps, directive = completionFn(finalCmd, finalArgs, toComplete)
completions = append(completions, comps...) completions = append(completions, comps...)
} }
@ -531,23 +571,23 @@ func helpOrVersionFlagPresent(cmd *Command) bool {
len(versionFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && versionFlag.Changed { len(versionFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && versionFlag.Changed {
return true return true
} }
if helpFlag := cmd.Flags().Lookup("help"); helpFlag != nil && if helpFlag := cmd.Flags().Lookup(helpFlagName); helpFlag != nil &&
len(helpFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && helpFlag.Changed { len(helpFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && helpFlag.Changed {
return true return true
} }
return false return false
} }
func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []Completion {
if nonCompletableFlag(flag) { if nonCompletableFlag(flag) {
return []string{} return []Completion{}
} }
var completions []string var completions []Completion
flagName := "--" + flag.Name flagName := "--" + flag.Name
if strings.HasPrefix(flagName, toComplete) { if strings.HasPrefix(flagName, toComplete) {
// Flag without the = // Flag without the =
completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) completions = append(completions, CompletionWithDesc(flagName, flag.Usage))
// Why suggest both long forms: --flag and --flag= ? // Why suggest both long forms: --flag and --flag= ?
// This forces the user to *always* have to type either an = or a space after the flag name. // This forces the user to *always* have to type either an = or a space after the flag name.
@ -559,20 +599,20 @@ func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string {
// if len(flag.NoOptDefVal) == 0 { // if len(flag.NoOptDefVal) == 0 {
// // Flag requires a value, so it can be suffixed with = // // Flag requires a value, so it can be suffixed with =
// flagName += "=" // flagName += "="
// completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) // completions = append(completions, CompletionWithDesc(flagName, flag.Usage))
// } // }
} }
flagName = "-" + flag.Shorthand flagName = "-" + flag.Shorthand
if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) { if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) {
completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) completions = append(completions, CompletionWithDesc(flagName, flag.Usage))
} }
return completions return completions
} }
func completeRequireFlags(finalCmd *Command, toComplete string) []string { func completeRequireFlags(finalCmd *Command, toComplete string) []Completion {
var completions []string var completions []Completion
doCompleteRequiredFlags := func(flag *pflag.Flag) { doCompleteRequiredFlags := func(flag *pflag.Flag) {
if _, present := flag.Annotations[BashCompOneRequiredFlag]; present { if _, present := flag.Annotations[BashCompOneRequiredFlag]; present {
@ -687,8 +727,8 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p
// 1- the feature has been explicitly disabled by the program, // 1- the feature has been explicitly disabled by the program,
// 2- c has no subcommands (to avoid creating one), // 2- c has no subcommands (to avoid creating one),
// 3- c already has a 'completion' command provided by the program. // 3- c already has a 'completion' command provided by the program.
func (c *Command) InitDefaultCompletionCmd() { func (c *Command) InitDefaultCompletionCmd(args ...string) {
if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() { if c.CompletionOptions.DisableDefaultCmd {
return return
} }
@ -701,6 +741,16 @@ func (c *Command) InitDefaultCompletionCmd() {
haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions
// Special case to know if there are sub-commands or not.
hasSubCommands := false
for _, cmd := range c.commands {
if cmd.Name() != ShellCompRequestCmd && cmd.Name() != helpCommandName {
// We found a real sub-command (not 'help' or '__complete')
hasSubCommands = true
break
}
}
completionCmd := &Command{ completionCmd := &Command{
Use: compCmdName, Use: compCmdName,
Short: "Generate the autocompletion script for the specified shell", Short: "Generate the autocompletion script for the specified shell",
@ -714,6 +764,22 @@ func (c *Command) InitDefaultCompletionCmd() {
} }
c.AddCommand(completionCmd) c.AddCommand(completionCmd)
if !hasSubCommands {
// If the 'completion' command will be the only sub-command,
// we only create it if it is actually being called.
// This avoids breaking programs that would suddenly find themselves with
// a subcommand, which would prevent them from accepting arguments.
// We also create the 'completion' command if the user is triggering
// shell completion for it (prog __complete completion '')
subCmd, cmdArgs, err := c.Find(args)
if err != nil || subCmd.Name() != compCmdName &&
!(subCmd.Name() == ShellCompRequestCmd && len(cmdArgs) > 1 && cmdArgs[0] == compCmdName) {
// The completion command is not being called or being completed so we remove it.
c.RemoveCommand(completionCmd)
return
}
}
out := c.OutOrStdout() out := c.OutOrStdout()
noDesc := c.CompletionOptions.DisableDescriptions noDesc := c.CompletionOptions.DisableDescriptions
shortDesc := "Generate the autocompletion script for %s" shortDesc := "Generate the autocompletion script for %s"

View file

@ -162,7 +162,10 @@ function __%[1]s_debug {
if (-Not $Description) { if (-Not $Description) {
$Description = " " $Description = " "
} }
@{Name="$Name";Description="$Description"} New-Object -TypeName PSCustomObject -Property @{
Name = "$Name"
Description = "$Description"
}
} }
@ -240,7 +243,12 @@ function __%[1]s_debug {
__%[1]s_debug "Only one completion left" __%[1]s_debug "Only one completion left"
# insert space after value # insert space after value
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") $CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
} else {
$CompletionText
}
} else { } else {
# Add the proper number of spaces to align the descriptions # Add the proper number of spaces to align the descriptions
@ -255,7 +263,12 @@ function __%[1]s_debug {
$Description = " ($($comp.Description))" $Description = " ($($comp.Description))"
} }
[System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") $CompletionText = "$($comp.Name)$Description"
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
} else {
$CompletionText
}
} }
} }
@ -264,7 +277,13 @@ function __%[1]s_debug {
# insert space after value # insert space after value
# MenuComplete will automatically show the ToolTip of # MenuComplete will automatically show the ToolTip of
# the highlighted value at the bottom of the suggestions. # the highlighted value at the bottom of the suggestions.
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
} else {
$CompletionText
}
} }
# TabCompleteNext and in case we get something unknown # TabCompleteNext and in case we get something unknown
@ -272,7 +291,13 @@ function __%[1]s_debug {
# Like MenuComplete but we don't want to add a space here because # Like MenuComplete but we don't want to add a space here because
# the user need to press space anyway to get the completion. # the user need to press space anyway to get the completion.
# Description will not be shown because that's not possible with TabCompleteNext # Description will not be shown because that's not possible with TabCompleteNext
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
$CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars)
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){
[System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
} else {
$CompletionText
}
} }
} }

12
vendor/github.com/spf13/pflag/.editorconfig generated vendored Normal file
View file

@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.go]
indent_style = tab

4
vendor/github.com/spf13/pflag/.golangci.yaml generated vendored Normal file
View file

@ -0,0 +1,4 @@
linters:
disable-all: true
enable:
- nolintlint

View file

@ -160,7 +160,7 @@ type FlagSet struct {
args []string // arguments after flags args []string // arguments after flags
argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no -- argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no --
errorHandling ErrorHandling errorHandling ErrorHandling
output io.Writer // nil means stderr; use out() accessor output io.Writer // nil means stderr; use Output() accessor
interspersed bool // allow interspersed option/non-option args interspersed bool // allow interspersed option/non-option args
normalizeNameFunc func(f *FlagSet, name string) NormalizedName normalizeNameFunc func(f *FlagSet, name string) NormalizedName
@ -255,13 +255,20 @@ func (f *FlagSet) normalizeFlagName(name string) NormalizedName {
return n(f, name) return n(f, name)
} }
func (f *FlagSet) out() io.Writer { // Output returns the destination for usage and error messages. os.Stderr is returned if
// output was not set or was set to nil.
func (f *FlagSet) Output() io.Writer {
if f.output == nil { if f.output == nil {
return os.Stderr return os.Stderr
} }
return f.output return f.output
} }
// Name returns the name of the flag set.
func (f *FlagSet) Name() string {
return f.name
}
// 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 (f *FlagSet) SetOutput(output io.Writer) { func (f *FlagSet) SetOutput(output io.Writer) {
@ -358,7 +365,7 @@ func (f *FlagSet) ShorthandLookup(name string) *Flag {
} }
if len(name) > 1 { if len(name) > 1 {
msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name) msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name)
fmt.Fprintf(f.out(), msg) fmt.Fprintf(f.Output(), msg)
panic(msg) panic(msg)
} }
c := name[0] c := name[0]
@ -482,7 +489,7 @@ func (f *FlagSet) Set(name, value string) error {
} }
if flag.Deprecated != "" { if flag.Deprecated != "" {
fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
} }
return nil return nil
} }
@ -523,7 +530,7 @@ func Set(name, value string) error {
// otherwise, the default values of all defined flags in the set. // otherwise, the default values of all defined flags in the set.
func (f *FlagSet) PrintDefaults() { func (f *FlagSet) PrintDefaults() {
usages := f.FlagUsages() usages := f.FlagUsages()
fmt.Fprint(f.out(), usages) fmt.Fprint(f.Output(), usages)
} }
// defaultIsZeroValue returns true if the default value for this flag represents // defaultIsZeroValue returns true if the default value for this flag represents
@ -758,7 +765,7 @@ func PrintDefaults() {
// defaultUsage is the default function to print a usage message. // defaultUsage is the default function to print a usage message.
func defaultUsage(f *FlagSet) { func defaultUsage(f *FlagSet) {
fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name)
f.PrintDefaults() f.PrintDefaults()
} }
@ -844,7 +851,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
_, alreadyThere := f.formal[normalizedFlagName] _, alreadyThere := f.formal[normalizedFlagName]
if alreadyThere { if alreadyThere {
msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name)
fmt.Fprintln(f.out(), msg) fmt.Fprintln(f.Output(), msg)
panic(msg) // Happens only if flags are declared with identical names panic(msg) // Happens only if flags are declared with identical names
} }
if f.formal == nil { if f.formal == nil {
@ -860,7 +867,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
} }
if len(flag.Shorthand) > 1 { if len(flag.Shorthand) > 1 {
msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand) msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand)
fmt.Fprintf(f.out(), msg) fmt.Fprintf(f.Output(), msg)
panic(msg) panic(msg)
} }
if f.shorthands == nil { if f.shorthands == nil {
@ -870,7 +877,7 @@ func (f *FlagSet) AddFlag(flag *Flag) {
used, alreadyThere := f.shorthands[c] used, alreadyThere := f.shorthands[c]
if alreadyThere { if alreadyThere {
msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name) msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name)
fmt.Fprintf(f.out(), msg) fmt.Fprintf(f.Output(), msg)
panic(msg) panic(msg)
} }
f.shorthands[c] = flag f.shorthands[c] = flag
@ -909,7 +916,7 @@ func VarP(value Value, name, shorthand, usage string) {
func (f *FlagSet) failf(format string, a ...interface{}) error { func (f *FlagSet) failf(format string, a ...interface{}) error {
err := fmt.Errorf(format, a...) err := fmt.Errorf(format, a...)
if f.errorHandling != ContinueOnError { if f.errorHandling != ContinueOnError {
fmt.Fprintln(f.out(), err) fmt.Fprintln(f.Output(), err)
f.usage() f.usage()
} }
return err return err
@ -1060,7 +1067,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
} }
if flag.ShorthandDeprecated != "" { if flag.ShorthandDeprecated != "" {
fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
} }
err = fn(flag, value) err = fn(flag, value)

View file

@ -16,6 +16,9 @@ func newIPValue(val net.IP, p *net.IP) *ipValue {
func (i *ipValue) String() string { return net.IP(*i).String() } func (i *ipValue) String() string { return net.IP(*i).String() }
func (i *ipValue) Set(s string) error { func (i *ipValue) Set(s string) error {
if s == "" {
return nil
}
ip := net.ParseIP(strings.TrimSpace(s)) ip := net.ParseIP(strings.TrimSpace(s))
if ip == nil { if ip == nil {
return fmt.Errorf("failed to parse IP: %q", s) return fmt.Errorf("failed to parse IP: %q", s)

147
vendor/github.com/spf13/pflag/ipnet_slice.go generated vendored Normal file
View file

@ -0,0 +1,147 @@
package pflag
import (
"fmt"
"io"
"net"
"strings"
)
// -- ipNetSlice Value
type ipNetSliceValue struct {
value *[]net.IPNet
changed bool
}
func newIPNetSliceValue(val []net.IPNet, p *[]net.IPNet) *ipNetSliceValue {
ipnsv := new(ipNetSliceValue)
ipnsv.value = p
*ipnsv.value = val
return ipnsv
}
// Set converts, and assigns, the comma-separated IPNet argument string representation as the []net.IPNet value of this flag.
// If Set is called on a flag that already has a []net.IPNet assigned, the newly converted values will be appended.
func (s *ipNetSliceValue) Set(val string) error {
// remove all quote characters
rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
// read flag arguments with CSV parser
ipNetStrSlice, err := readAsCSV(rmQuote.Replace(val))
if err != nil && err != io.EOF {
return err
}
// parse ip values into slice
out := make([]net.IPNet, 0, len(ipNetStrSlice))
for _, ipNetStr := range ipNetStrSlice {
_, n, err := net.ParseCIDR(strings.TrimSpace(ipNetStr))
if err != nil {
return fmt.Errorf("invalid string being converted to CIDR: %s", ipNetStr)
}
out = append(out, *n)
}
if !s.changed {
*s.value = out
} else {
*s.value = append(*s.value, out...)
}
s.changed = true
return nil
}
// Type returns a string that uniquely represents this flag's type.
func (s *ipNetSliceValue) Type() string {
return "ipNetSlice"
}
// String defines a "native" format for this net.IPNet slice flag value.
func (s *ipNetSliceValue) String() string {
ipNetStrSlice := make([]string, len(*s.value))
for i, n := range *s.value {
ipNetStrSlice[i] = n.String()
}
out, _ := writeAsCSV(ipNetStrSlice)
return "[" + out + "]"
}
func ipNetSliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]")
// Emtpy string would cause a slice with one (empty) entry
if len(val) == 0 {
return []net.IPNet{}, nil
}
ss := strings.Split(val, ",")
out := make([]net.IPNet, len(ss))
for i, sval := range ss {
_, n, err := net.ParseCIDR(strings.TrimSpace(sval))
if err != nil {
return nil, fmt.Errorf("invalid string being converted to CIDR: %s", sval)
}
out[i] = *n
}
return out, nil
}
// GetIPNetSlice returns the []net.IPNet value of a flag with the given name
func (f *FlagSet) GetIPNetSlice(name string) ([]net.IPNet, error) {
val, err := f.getFlagType(name, "ipNetSlice", ipNetSliceConv)
if err != nil {
return []net.IPNet{}, err
}
return val.([]net.IPNet), nil
}
// IPNetSliceVar defines a ipNetSlice flag with specified name, default value, and usage string.
// The argument p points to a []net.IPNet variable in which to store the value of the flag.
func (f *FlagSet) IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
f.VarP(newIPNetSliceValue(value, p), name, "", usage)
}
// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
f.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
}
// IPNetSliceVar defines a []net.IPNet flag with specified name, default value, and usage string.
// The argument p points to a []net.IPNet variable in which to store the value of the flag.
func IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
CommandLine.VarP(newIPNetSliceValue(value, p), name, "", usage)
}
// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
func IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
CommandLine.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
}
// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
// The return value is the address of a []net.IPNet variable that stores the value of that flag.
func (f *FlagSet) IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
p := []net.IPNet{}
f.IPNetSliceVarP(&p, name, "", value, usage)
return &p
}
// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
func (f *FlagSet) IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
p := []net.IPNet{}
f.IPNetSliceVarP(&p, name, shorthand, value, usage)
return &p
}
// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
// The return value is the address of a []net.IP variable that stores the value of the flag.
func IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
return CommandLine.IPNetSliceP(name, "", value, usage)
}
// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
func IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
return CommandLine.IPNetSliceP(name, shorthand, value, usage)
}

View file

@ -31,11 +31,7 @@ func (s *stringArrayValue) Append(val string) error {
func (s *stringArrayValue) Replace(val []string) error { func (s *stringArrayValue) Replace(val []string) error {
out := make([]string, len(val)) out := make([]string, len(val))
for i, d := range val { for i, d := range val {
var err error
out[i] = d out[i] = d
if err != nil {
return err
}
} }
*s.value = out *s.value = out
return nil return nil

4
vendor/modules.txt vendored
View file

@ -601,10 +601,10 @@ github.com/spf13/afero/mem
# github.com/spf13/cast v1.6.0 # github.com/spf13/cast v1.6.0
## explicit; go 1.19 ## explicit; go 1.19
github.com/spf13/cast github.com/spf13/cast
# github.com/spf13/cobra v1.8.1 # github.com/spf13/cobra v1.9.1
## explicit; go 1.15 ## explicit; go 1.15
github.com/spf13/cobra github.com/spf13/cobra
# github.com/spf13/pflag v1.0.5 # github.com/spf13/pflag v1.0.6
## explicit; go 1.12 ## explicit; go 1.12
github.com/spf13/pflag github.com/spf13/pflag
# github.com/spf13/viper v1.19.0 # github.com/spf13/viper v1.19.0