From c643c61d551dfe74ef440d120a22dbf3bc878a25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 16:05:12 +0000 Subject: [PATCH] Bump github.com/urfave/cli/v3 from 3.7.0 to 3.10.0 Bumps [github.com/urfave/cli/v3](https://github.com/urfave/cli) from 3.7.0 to 3.10.0. - [Release notes](https://github.com/urfave/cli/releases) - [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md) - [Commits](https://github.com/urfave/cli/compare/v3.7.0...v3.10.0) --- updated-dependencies: - dependency-name: github.com/urfave/cli/v3 dependency-version: 3.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../cli/v3/autocomplete/bash_autocomplete | 113 ++++++++++++++--- .../cli/v3/autocomplete/fish_autocomplete | 20 +-- .../autocomplete/powershell_autocomplete.ps1 | 17 ++- vendor/github.com/urfave/cli/v3/command.go | 100 ++++++++++----- .../github.com/urfave/cli/v3/command_parse.go | 24 +++- .../github.com/urfave/cli/v3/command_run.go | 117 ++++++++++++------ .../github.com/urfave/cli/v3/command_setup.go | 60 +++++++-- vendor/github.com/urfave/cli/v3/completion.go | 60 ++++----- vendor/github.com/urfave/cli/v3/docs.go | 10 +- vendor/github.com/urfave/cli/v3/errors.go | 10 +- vendor/github.com/urfave/cli/v3/flag.go | 39 +++--- vendor/github.com/urfave/cli/v3/flag_bool.go | 2 +- .../urfave/cli/v3/flag_bool_with_inverse.go | 39 +++++- vendor/github.com/urfave/cli/v3/flag_ext.go | 28 ++++- vendor/github.com/urfave/cli/v3/flag_impl.go | 52 +++++++- .../github.com/urfave/cli/v3/flag_map_impl.go | 2 +- .../urfave/cli/v3/flag_slice_base.go | 2 +- .../urfave/cli/v3/godoc-current.txt | 60 +++++++-- vendor/github.com/urfave/cli/v3/help.go | 94 ++++++-------- .../urfave/cli/v3/mkdocs-requirements.txt | 8 +- vendor/github.com/urfave/cli/v3/mkdocs.yml | 2 + .../github.com/urfave/cli/v3/suggestions.go | 2 +- vendor/github.com/urfave/cli/v3/template.go | 4 +- vendor/modules.txt | 2 +- 26 files changed, 620 insertions(+), 253 deletions(-) diff --git a/go.mod b/go.mod index d80193f897..6ab1b4c1bf 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/regclient/regclient v0.11.2 github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 - github.com/urfave/cli/v3 v3.7.0 + github.com/urfave/cli/v3 v3.10.0 go.uber.org/zap v1.27.1 golang.org/x/mod v0.33.0 k8s.io/api v0.35.2 diff --git a/go.sum b/go.sum index ccd8363245..0a670755e1 100644 --- a/go.sum +++ b/go.sum @@ -230,8 +230,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v3 v3.7.0 h1:AGSnbUyjtLiM+WJUb4dzXKldl/gL+F8OwmRDtVr6g2U= -github.com/urfave/cli/v3 v3.7.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= +github.com/urfave/cli/v3 v3.10.0 h1:0aU8yOObVDMkM13Cj4G+zb4P0PdeJMec65f81Ak1ioM= +github.com/urfave/cli/v3 v3.10.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= diff --git a/vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete b/vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete index d63937d971..42eb17b8b2 100644 --- a/vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete +++ b/vendor/github.com/urfave/cli/v3/autocomplete/bash_autocomplete @@ -1,34 +1,107 @@ -#!/bin/bash - # This is a shell completion script auto-generated by https://github.com/urfave/cli for bash. -# Macs have bash3 for which the bash-completion package doesn't include -# _init_completion. This is a minimal version of that function. +# macOS still ships Bash 3, where bash-completion may not provide +# _init_completion. This is a minimal compatible fallback. __%[1]s_init_completion() { COMPREPLY=() - _get_comp_words_by_ref "$@" cur prev words cword + if declare -F _comp_initialize >/dev/null 2>&1; then + _comp_initialize "$@" + else + _get_comp_words_by_ref "$@" cur prev words cword + fi +} + +__%[1]s_build_completion_request() { + local -a words_before_cursor=("${COMP_WORDS[@]:0:${COMP_CWORD}}") + local current_word="${COMP_WORDS[COMP_CWORD]}" + + if [[ "${current_word}" == "-"* ]]; then + printf '%%s %%s --generate-shell-completion' "${words_before_cursor[*]}" "${current_word}" + else + printf '%%s --generate-shell-completion' "${words_before_cursor[*]}" + fi +} + +# Keep Bash 3 compatibility: associative arrays require Bash 4+, so +# descriptions are looked up via parallel indexed arrays. +__%[1]s_find_description_for() { + local candidate="$1" + local i + + for i in "${!__cli_completion_tokens[@]}"; do + if [[ "${__cli_completion_tokens[i]}" == "${candidate}" ]]; then + printf '%%s' "${__cli_completion_descriptions[i]}" + return 0 + fi + done + + return 1 } __%[1]s_bash_autocomplete() { - if [[ "${COMP_WORDS[0]}" != "source" ]]; then - local cur opts base words + local words=("${COMP_WORDS[@]}") + + if [[ "${words[0]}" != "source" ]]; then + local cur opts + local cword="${COMP_CWORD}" + local request_comp + COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - if declare -F _init_completion >/dev/null 2>&1; then - _init_completion -n "=:" || return - else - __%[1]s_init_completion -n "=:" || return - fi - words=("${words[@]:0:$cword}") - if [[ "$cur" == "-"* ]]; then - requestComp="${words[*]} ${cur} --generate-shell-completion" + cur="${words[$cword]}" + + __%[1]s_init_completion -n "=:" || return + + request_comp="$(__%[1]s_build_completion_request)" + opts=$(eval "${request_comp}" 2>/dev/null) + + # Completion output lines use "token:description" format. + # Keep token/description in parallel arrays for Bash 3 compatibility. + __cli_completion_tokens=() + __cli_completion_descriptions=() + + local line + local longest=0 + while IFS=$'\n' read -r line; do + local token="${line}" + local description="" + + if [[ "${line}" == *:* ]]; then + token="${line%%%%:*}" + description="${line#*:}" + fi + + if [[ -z "${token}" ]]; then + continue + fi + + __cli_completion_tokens+=("${token}") + __cli_completion_descriptions+=("${description}") + (( ${#token} > longest )) && longest=${#token} + done <<< "${opts}" + + local matches=( $(compgen -W "${__cli_completion_tokens[*]}" -- "${cur}") ) + + # COMP_TYPE=63 means Bash is listing matches (usually on second TAB). + if [[ "${COMP_TYPE:-}" == "63" && ${#matches[@]} -gt 0 ]]; then + local listed=() + local candidate + for candidate in "${matches[@]}"; do + local desc="$(__%[1]s_find_description_for "${candidate}")" + + if [[ -n "${desc}" ]]; then + local padded="$(printf '%%-*s' "${longest}" "${candidate}")" + listed+=("${padded} -- ${desc}") + else + listed+=("${candidate}") + fi + done + COMPREPLY=("${listed[@]}") else - requestComp="${words[*]} --generate-shell-completion" + COMPREPLY=("${matches[@]}") fi - opts=$(eval "${requestComp}" 2>/dev/null) - COMPREPLY=($(compgen -W "${opts}" -- ${cur})) + return 0 fi } -complete -o bashdefault -o default -o nospace -F __%[1]s_bash_autocomplete %[1]s +complete -o bashdefault -o default -F __%[1]s_bash_autocomplete %[1]s diff --git a/vendor/github.com/urfave/cli/v3/autocomplete/fish_autocomplete b/vendor/github.com/urfave/cli/v3/autocomplete/fish_autocomplete index 7aa7d0a8ba..5f2fcd7f6b 100644 --- a/vendor/github.com/urfave/cli/v3/autocomplete/fish_autocomplete +++ b/vendor/github.com/urfave/cli/v3/autocomplete/fish_autocomplete @@ -1,12 +1,16 @@ # This is a shell completion script auto-generated by https://github.com/urfave/cli for fish. -function __%[1]_perform_completion +function __%[1]s_perform_completion # Extract all args except the last one set -l args (commandline -opc) # Extract the last arg (partial input) set -l lastArg (commandline -ct) - set -l results ($args[1] $args[2..-1] $lastArg --generate-shell-completion 2> /dev/null) + if string match -q -- "-*" $lastArg + set results ($args[1] $args[2..-1] $lastArg --generate-shell-completion 2> /dev/null) + else + set results ($args[1] $args[2..-1] --generate-shell-completion 2> /dev/null) + end # Remove trailing empty lines for line in $results[-1..1] @@ -18,18 +22,18 @@ function __%[1]_perform_completion end for line in $results - if not string match -q -- "%[1]*" $line + if not string match -q -- "%[1]s*" $line set -l parts (string split -m 1 ":" -- "$line") if test (count $parts) -eq 2 - printf "%s\t%s\n" "$parts[1]" "$parts[2]" + printf "%%s\t%%s\n" "$parts[1]" "$parts[2]" else - printf "%s\n" "$line" + printf "%%s\n" "$line" end end end end -# Clear existing completions for %[1] -complete -c %[1] -e +# Clear existing completions for %[1]s +complete -c %[1]s -e # Register completion function -complete -c %[1] -f -a '(__%[1]_perform_completion)' \ No newline at end of file +complete -c %[1]s -f -a '(__%[1]s_perform_completion)' diff --git a/vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1 b/vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1 index 6e0c422e25..fee6d0c7d2 100644 --- a/vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1 +++ b/vendor/github.com/urfave/cli/v3/autocomplete/powershell_autocomplete.ps1 @@ -1,9 +1,16 @@ $fn = $($MyInvocation.MyCommand.Name) $name = $fn -replace "(.*)\.ps1$", '$1' Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock { - param($commandName, $wordToComplete, $cursorPosition) - $other = "$wordToComplete --generate-shell-completion" - Invoke-Expression $other | ForEach-Object { + param($commandName, $wordToComplete, $cursorPosition) + $other = "$wordToComplete --generate-shell-completion" + Invoke-Expression $other | ForEach-Object { + $parts = $_.Split(':', 2) + if ($parts.Count -eq 2) { + $completion = $parts[0].Trim() + $description = $parts[1].Trim() + [System.Management.Automation.CompletionResult]::new($completion, $completion, 'ParameterValue', $description) + } else { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) - } - } + } + } +} \ No newline at end of file diff --git a/vendor/github.com/urfave/cli/v3/command.go b/vendor/github.com/urfave/cli/v3/command.go index 8f73fd28d7..4cd907a558 100644 --- a/vendor/github.com/urfave/cli/v3/command.go +++ b/vendor/github.com/urfave/cli/v3/command.go @@ -90,7 +90,7 @@ type Command struct { // default behavior. ExitErrHandler ExitErrHandlerFunc `json:"-"` // Other custom info - Metadata map[string]interface{} `json:"metadata"` + Metadata map[string]any `json:"metadata"` // Carries a function which returns app specific info. ExtraInfo func() map[string]string `json:"-"` // CustomRootCommandHelpTemplate the text template for app help topic. @@ -161,21 +161,12 @@ type Command struct { globaHelpFlagAdded bool // whether global version flag was added globaVersionFlagAdded bool + // generated root version flag + versionFlag Flag // whether this is a completion command isCompletionCommand bool -} - -// FullName returns the full name of the command. -// For commands with parents this ensures that the parent commands -// are part of the command path. -func (cmd *Command) FullName() string { - namePath := []string{} - - if cmd.parent != nil { - namePath = append(namePath, cmd.parent.FullName()) - } - - return strings.Join(append(namePath, cmd.Name), " ") + // whether this is the built-in help command + builtInHelp bool } func (cmd *Command) Command(name string) *Command { @@ -303,6 +294,9 @@ func (cmd *Command) appendFlag(fl Flag) { // VisiblePersistentFlags returns a slice of [LocalFlag] with Persistent=true and Hidden=false. func (cmd *Command) VisiblePersistentFlags() []Flag { + if cmd.isCompletionCommand { + return nil + } var flags []Flag for _, fl := range cmd.Root().Flags { pfl, ok := fl.(LocalFlag) @@ -370,6 +364,22 @@ func (cmd *Command) lFlag(name string) Flag { return nil } +func (cmd *Command) hasPersistentFlagOnAncestor(fl Flag) bool { + for pCmd := cmd.parent; pCmd != nil; pCmd = pCmd.parent { + for _, pFl := range pCmd.allFlags() { + if pFl != fl { + continue + } + + pfl, ok := pFl.(LocalFlag) + if ok && !pfl.IsLocal() { + return true + } + } + } + return false +} + func (cmd *Command) lookupFlag(name string) Flag { for _, pCmd := range cmd.Lineage() { if f := pCmd.lFlag(name); f != nil { @@ -408,6 +418,12 @@ func (cmd *Command) checkRequiredFlag(f Flag) (bool, string) { } func (cmd *Command) checkAllRequiredFlags() requiredFlagsErr { + // The help and completion commands are allowed to run without + // enforcement of required flags, since they do not invoke user + // actions that depend on those flag values. + if cmd.builtInHelp || cmd.isCompletionCommand { + return nil + } for pCmd := cmd; pCmd != nil; pCmd = pCmd.parent { if err := pCmd.checkRequiredFlags(); err != nil { return err @@ -550,6 +566,39 @@ func (cmd *Command) Lineage() []*Command { return lineage } +// FullName returns the full name of the command. +// Includes parent commands separated by space. +func (cmd *Command) FullName() string { + return strings.Join(cmd.Path(), " ") +} + +// Path returns the path of command names from the root to cmd, inclusive. +// Each element is a Command.Name. Path traverses upward via parent pointers +// similar to Lineage. FullName() is equivalent to strings.Join(cmd.Path(), " "). +func (cmd *Command) Path() []string { + if cmd.parent != nil { + return append(cmd.parent.Path(), cmd.Name) + } + return []string{cmd.Name} +} + +// Walk visits cmd and every descendant. If fn returns a non-nil error, the +// walk terminates and the error is returned to the caller. +func (cmd *Command) Walk(fn func(*Command) error) error { + if fn == nil { + return nil + } + if err := fn(cmd); err != nil { + return err + } + for _, sub := range cmd.Commands { + if err := sub.Walk(fn); err != nil { + return err + } + } + return nil +} + // Count returns the num of occurrences of this flag func (cmd *Command) Count(name string) int { if cf, ok := cmd.lookupFlag(name).(Countable); ok { @@ -559,7 +608,7 @@ func (cmd *Command) Count(name string) int { } // Value returns the value of the flag corresponding to `name` -func (cmd *Command) Value(name string) interface{} { +func (cmd *Command) Value(name string) any { if fs := cmd.lookupFlag(name); fs != nil { tracef("value found for name %[1]q (cmd=%[2]q)", name, cmd.Name) return fs.Get() @@ -582,19 +631,14 @@ func (cmd *Command) NArg() int { func (cmd *Command) runFlagActions(ctx context.Context) error { tracef("runFlagActions") - for fl := range cmd.setFlags { - /*tracef("checking %v:%v", fl.Names(), fl.IsSet()) - if !fl.IsSet() { - continue - }*/ - - //if pf, ok := fl.(LocalFlag); ok && !pf.IsLocal() { - // continue - //} - - if af, ok := fl.(ActionableFlag); ok { - if err := af.RunAction(ctx, cmd); err != nil { - return err + // run the flag actions in the same order that they are defined + // to maintain consistency. + for _, fl := range cmd.appliedFlags { + if _, inSet := cmd.setFlags[fl]; inSet { + if af, ok := fl.(ActionableFlag); ok { + if err := af.RunAction(ctx, cmd); err != nil { + return err + } } } } diff --git a/vendor/github.com/urfave/cli/v3/command_parse.go b/vendor/github.com/urfave/cli/v3/command_parse.go index f8103d12f2..2b9a481df2 100644 --- a/vendor/github.com/urfave/cli/v3/command_parse.go +++ b/vendor/github.com/urfave/cli/v3/command_parse.go @@ -80,11 +80,17 @@ func (cmd *Command) parseFlags(args Args) (Args, error) { firstArg := strings.TrimSpace(rargs[0]) if len(firstArg) == 0 { - break + posArgs = append(posArgs, rargs[0]) + continue } // stop parsing once we see a "--" if firstArg == "--" { + // In shell completion mode, preserve "--" so that completion can detect + // when the user is completing "--" itself vs. completing after "--" + if cmd.Root().shellCompletion { + posArgs = append(posArgs, firstArg) + } posArgs = append(posArgs, rargs[1:]...) return &stringSliceArgs{posArgs}, nil } @@ -163,8 +169,14 @@ func (cmd *Command) parseFlags(args Args) (Args, error) { tracef("processing non bool flag (fName=%[1]q)", flagName) // not a bool flag so need to get the next arg - if flagVal == "" { - if len(rargs) == 1 || valFromEqual { + if flagVal == "" && !valFromEqual { + if len(rargs) == 1 { + // In shell completion mode, preserve the flag so that DefaultCompleteWithFlags can use it + // as lastArg and offer suggestions for it. + if cmd.Root().shellCompletion { + posArgs = append(posArgs, rargs...) + return &stringSliceArgs{posArgs}, nil + } return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, firstArg) } flagVal = rargs[1] @@ -181,6 +193,12 @@ func (cmd *Command) parseFlags(args Args) (Args, error) { // no flag lookup found and short handling is disabled if !shortOptionHandling { + // In shell completion mode, preserve the partial flag so that DefaultCompleteWithFlags can use it + // as lastArg and offer suggestions that match the prefix. + if cmd.Root().shellCompletion { + posArgs = append(posArgs, rargs...) + return &stringSliceArgs{posArgs}, nil + } return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", providedButNotDefinedErrMsg, flagName) } diff --git a/vendor/github.com/urfave/cli/v3/command_run.go b/vendor/github.com/urfave/cli/v3/command_run.go index e5cfff8a57..8d5907151e 100644 --- a/vendor/github.com/urfave/cli/v3/command_run.go +++ b/vendor/github.com/urfave/cli/v3/command_run.go @@ -9,6 +9,8 @@ import ( "unicode" ) +type helpShownKey struct{} + func (cmd *Command) parseArgsFromStdin() ([]string, error) { type state int const ( @@ -141,13 +143,10 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context var rargs Args = &stringSliceArgs{v: osArgs} var args Args = &stringSliceArgs{rargs.Tail()} - if cmd.isCompletionCommand || cmd.Name == helpName { - tracef("special command detected, skipping pre-parse (cmd=%[1]q)", cmd.Name) - cmd.parsedArgs = args - return ctx, cmd.Action(ctx, cmd) - } - for _, f := range cmd.allFlags() { + if cmd.hasPersistentFlagOnAncestor(f) { + continue + } if err := f.PreParse(); err != nil { return ctx, err } @@ -164,7 +163,12 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context tracef("using post-parse arguments %[1]q (cmd=%[2]q)", args, cmd.Name) - if checkCompletions(ctx, cmd) { + if shouldRunCompletion(cmd) { + var beforeErr error + if ctx, beforeErr = runBefore(ctx, commandChain(cmd)); beforeErr != nil { + return ctx, beforeErr + } + runCompletion(ctx, cmd) return ctx, nil } @@ -173,6 +177,15 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context deferErr = err cmd.isInError = true + if cmd.checkHelp() { + ctx = context.WithValue(ctx, helpShownKey{}, true) + if cmd.parent == nil { + _ = ShowRootCommandHelp(cmd) + } else { + _ = ShowSubcommandHelp(cmd) + } + return ctx, nil + } if cmd.OnUsageError != nil { err = cmd.OnUsageError(ctx, cmd, err, cmd.parent != nil) err = cmd.handleExitCoder(ctx, err) @@ -191,10 +204,8 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context tracef("SILENTLY IGNORING ERROR running ShowRootCommandHelp %[1]v (cmd=%[2]q)", err, cmd.Name) } } else { - tracef("running ShowCommandHelp with %[1]q", cmd.Name) - if err := ShowCommandHelp(ctx, cmd, cmd.Name); err != nil { - tracef("SILENTLY IGNORING ERROR running ShowCommandHelp with %[1]q %[2]v", cmd.Name, err) - } + tracef("running ShowSubcommandHelp for %[1]q", cmd.Name) + _ = ShowSubcommandHelp(cmd) } } @@ -202,6 +213,7 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context } if cmd.checkHelp() { + ctx = context.WithValue(ctx, helpShownKey{}, true) return ctx, helpCommandAction(ctx, cmd) } else { tracef("no help is wanted (cmd=%[1]q)", cmd.Name) @@ -213,6 +225,7 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context } for _, flag := range cmd.allFlags() { + cmd.setMultiValueParsingConfig(flag) isSet := flag.IsSet() if err := flag.PostParse(); err != nil { return ctx, err @@ -225,6 +238,9 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context if cmd.After != nil && !cmd.Root().shellCompletion { defer func() { + if ctx.Value(helpShownKey{}) != nil { + return + } if err := cmd.After(ctx, cmd); err != nil { err = cmd.handleExitCoder(ctx, err) @@ -237,14 +253,25 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context }() } - for _, grp := range cmd.MutuallyExclusiveFlags { - if err := grp.check(cmd); err != nil { - if cmd.OnUsageError != nil { - err = cmd.OnUsageError(ctx, cmd, err, cmd.parent != nil) - } else { - _ = ShowSubcommandHelp(cmd) + // Walk the parent chain to check mutually exclusive flag groups + // defined on ancestor commands, since persistent flags are inherited. + for pCmd := cmd; pCmd != nil; pCmd = pCmd.parent { + for _, grp := range pCmd.MutuallyExclusiveFlags { + if err := grp.check(cmd); err != nil { + if cmd.OnUsageError != nil { + err = cmd.OnUsageError(ctx, cmd, err, cmd.parent != nil) + } else { + fmt.Fprintf(cmd.Root().ErrWriter, "Incorrect Usage: %s\n\n", err.Error()) + if cmd.parent == nil { + _ = ShowRootCommandHelp(cmd) + } else { + if err := ShowCommandHelp(ctx, cmd.parent, cmd.Name); err != nil { + _ = ShowSubcommandHelp(cmd) + } + } + } + return ctx, err } - return ctx, err } } @@ -263,13 +290,12 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context subCmd = cmd.Command(name) if subCmd == nil { hasDefault := cmd.DefaultCommand != "" - isFlagName := slices.Contains(cmd.FlagNames(), name) if hasDefault { tracef("using default command=%[1]q (cmd=%[2]q)", cmd.DefaultCommand, cmd.Name) } - if isFlagName || hasDefault { + if hasDefault { argsWithDefault := cmd.argsWithDefaultCommand(cmd.parsedArgs) tracef("using default command args=%[1]q (cmd=%[2]q)", argsWithDefault, cmd.Name) subCmd = cmd.Command(argsWithDefault.First()) @@ -299,23 +325,12 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context // perform the command action. // // First, resolve the chain of nested commands up to the parent. - var cmdChain []*Command - for p := cmd; p != nil; p = p.parent { - cmdChain = append(cmdChain, p) - } - slices.Reverse(cmdChain) + cmdChain := commandChain(cmd) // Run Before actions in order. - for _, cmd := range cmdChain { - if cmd.Before == nil { - continue - } - if bctx, err := cmd.Before(ctx, cmd); err != nil { - deferErr = cmd.handleExitCoder(ctx, err) - return ctx, deferErr - } else if bctx != nil { - ctx = bctx - } + if ctx, err = runBefore(ctx, cmdChain); err != nil { + deferErr = err + return ctx, deferErr } // Run flag actions in order. @@ -333,7 +348,14 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context if cmd.OnUsageError != nil { err = cmd.OnUsageError(ctx, cmd, err, cmd.parent != nil) } else { - _ = ShowSubcommandHelp(cmd) + fmt.Fprintf(cmd.Root().ErrWriter, "Incorrect Usage: %s\n\n", err.Error()) + if cmd.parent == nil { + _ = ShowRootCommandHelp(cmd) + } else { + if err := ShowCommandHelp(ctx, cmd.parent, cmd.Name); err != nil { + _ = ShowSubcommandHelp(cmd) + } + } } return ctx, err } @@ -365,3 +387,26 @@ func (cmd *Command) run(ctx context.Context, osArgs []string) (_ context.Context tracef("returning deferErr (cmd=%[1]q) %[2]q", cmd.Name, deferErr) return ctx, deferErr } + +func commandChain(cmd *Command) []*Command { + var cmdChain []*Command + for p := cmd; p != nil; p = p.parent { + cmdChain = append(cmdChain, p) + } + slices.Reverse(cmdChain) + return cmdChain +} + +func runBefore(ctx context.Context, cmdChain []*Command) (context.Context, error) { + for _, cmd := range cmdChain { + if cmd.Before == nil { + continue + } + if bctx, err := cmd.Before(ctx, cmd); err != nil { + return ctx, cmd.handleExitCoder(ctx, err) + } else if bctx != nil { + ctx = bctx + } + } + return ctx, nil +} diff --git a/vendor/github.com/urfave/cli/v3/command_setup.go b/vendor/github.com/urfave/cli/v3/command_setup.go index cac4a30314..8cc73a02fc 100644 --- a/vendor/github.com/urfave/cli/v3/command_setup.go +++ b/vendor/github.com/urfave/cli/v3/command_setup.go @@ -46,18 +46,33 @@ func (cmd *Command) setupDefaults(osArgs []string) { } if cmd.Reader == nil { - tracef("setting default Reader as os.Stdin (cmd=%[1]q)", cmd.Name) - cmd.Reader = os.Stdin + if cmd.parent != nil && cmd.parent.Reader != nil { + tracef("inheriting Reader from parent (cmd=%[1]q)", cmd.Name) + cmd.Reader = cmd.parent.Reader + } else { + tracef("setting default Reader as os.Stdin (cmd=%[1]q)", cmd.Name) + cmd.Reader = os.Stdin + } } if cmd.Writer == nil { - tracef("setting default Writer as os.Stdout (cmd=%[1]q)", cmd.Name) - cmd.Writer = os.Stdout + if cmd.parent != nil && cmd.parent.Writer != nil { + tracef("inheriting Writer from parent (cmd=%[1]q)", cmd.Name) + cmd.Writer = cmd.parent.Writer + } else { + tracef("setting default Writer as os.Stdout (cmd=%[1]q)", cmd.Name) + cmd.Writer = os.Stdout + } } if cmd.ErrWriter == nil { - tracef("setting default ErrWriter as os.Stderr (cmd=%[1]q)", cmd.Name) - cmd.ErrWriter = os.Stderr + if cmd.parent != nil && cmd.parent.ErrWriter != nil { + tracef("inheriting ErrWriter from parent (cmd=%[1]q)", cmd.Name) + cmd.ErrWriter = cmd.parent.ErrWriter + } else { + tracef("setting default ErrWriter as os.Stderr (cmd=%[1]q)", cmd.Name) + cmd.ErrWriter = os.Stderr + } } if cmd.AllowExtFlags { @@ -89,8 +104,11 @@ func (cmd *Command) setupDefaults(osArgs []string) { localVersionFlag = VersionFlag } - cmd.appendFlag(localVersionFlag) - cmd.globaVersionFlagAdded = true + if !flagNamesInUse(cmd.allFlags(), localVersionFlag.Names()) { + cmd.appendFlag(localVersionFlag) + cmd.versionFlag = localVersionFlag + cmd.globaVersionFlagAdded = true + } } } @@ -147,11 +165,13 @@ func (cmd *Command) setupDefaults(osArgs []string) { func (cmd *Command) setupCommandGraph() { tracef("setting up command graph (cmd=%[1]q)", cmd.Name) - for _, subCmd := range cmd.Commands { - subCmd.parent = cmd - subCmd.setupSubcommand() - subCmd.setupCommandGraph() - } + _ = cmd.Walk(func(sub *Command) error { + for _, subCmd := range sub.Commands { + subCmd.parent = sub + subCmd.setupSubcommand() + } + return nil + }) } func (cmd *Command) setupSubcommand() { @@ -178,6 +198,20 @@ func (cmd *Command) setupSubcommand() { cmd.flagCategories = newFlagCategoriesFromFlags(cmd.allFlags()) } +func flagNamesInUse(flags []Flag, names []string) bool { + for _, name := range names { + for _, fl := range flags { + for _, flagName := range fl.Names() { + if flagName == name { + return true + } + } + } + } + + return false +} + func (cmd *Command) hideHelp() bool { tracef("hide help (cmd=%[1]q)", cmd.Name) for c := cmd; c != nil; c = c.parent { diff --git a/vendor/github.com/urfave/cli/v3/completion.go b/vendor/github.com/urfave/cli/v3/completion.go index de11edb406..167b0c0123 100644 --- a/vendor/github.com/urfave/cli/v3/completion.go +++ b/vendor/github.com/urfave/cli/v3/completion.go @@ -4,7 +4,6 @@ import ( "context" "embed" "fmt" - "sort" "strings" ) @@ -58,45 +57,36 @@ Output the script to path/to/autocomplete/$COMMAND.ps1 an run it. ` func buildCompletionCommand(appName string) *Command { - return &Command{ - Name: completionCommandName, - Hidden: true, - Usage: "Output shell completion script for bash, zsh, fish, or Powershell", - Description: strings.ReplaceAll(completionDescription, "$COMMAND", appName), - Action: func(ctx context.Context, cmd *Command) error { - return printShellCompletion(ctx, cmd, appName) - }, + cmd := &Command{ + Name: completionCommandName, + Hidden: true, + Usage: "Output shell completion script for bash, zsh, fish, or Powershell", + Description: strings.ReplaceAll(completionDescription, "$COMMAND", appName), isCompletionCommand: true, } -} - -func printShellCompletion(_ context.Context, cmd *Command, appName string) error { - var shells []string - for k := range shellCompletions { - shells = append(shells, k) - } - - sort.Strings(shells) - - if cmd.Args().Len() == 0 { - return Exit(fmt.Sprintf("no shell provided for completion command. available shells are %+v", shells), 1) - } - s := cmd.Args().First() - renderCompletion, ok := shellCompletions[s] - if !ok { - return Exit(fmt.Sprintf("unknown shell %s, available shells are %+v", s, shells), 1) + for shell, render := range shellCompletions { + cmd.Commands = append(cmd.Commands, buildShellCompletionSubcommand(shell, render, appName)) } - completionScript, err := renderCompletion(cmd, appName) - if err != nil { - return Exit(err, 1) - } + return cmd +} - _, err = cmd.Writer.Write([]byte(completionScript)) - if err != nil { - return Exit(err, 1) +func buildShellCompletionSubcommand(shell string, render renderCompletion, appName string) *Command { + return &Command{ + Name: shell, + Usage: fmt.Sprintf("Output %s completion script", shell), + isCompletionCommand: true, + Action: func(ctx context.Context, cmd *Command) error { + completionScript, err := render(cmd, appName) + if err != nil { + return Exit(err, 1) + } + _, err = cmd.Root().Writer.Write([]byte(completionScript)) + if err != nil { + return Exit(err, 1) + } + return nil + }, } - - return nil } diff --git a/vendor/github.com/urfave/cli/v3/docs.go b/vendor/github.com/urfave/cli/v3/docs.go index 8ecdfc8359..88461386b8 100644 --- a/vendor/github.com/urfave/cli/v3/docs.go +++ b/vendor/github.com/urfave/cli/v3/docs.go @@ -35,21 +35,21 @@ func unquoteUsage(usage string) (string, string) { } func prefixedNames(names []string, placeholder string) string { - var prefixed string + var prefixed strings.Builder for i, name := range names { if name == "" { continue } - prefixed += prefixFor(name) + name + prefixed.WriteString(prefixFor(name) + name) if placeholder != "" { - prefixed += " " + placeholder + prefixed.WriteString(" " + placeholder) } if i < len(names)-1 { - prefixed += ", " + prefixed.WriteString(", ") } } - return prefixed + return prefixed.String() } func envFormat(envVars []string, prefix, sep, suffix string) string { diff --git a/vendor/github.com/urfave/cli/v3/errors.go b/vendor/github.com/urfave/cli/v3/errors.go index f365a57990..ffd6471f23 100644 --- a/vendor/github.com/urfave/cli/v3/errors.go +++ b/vendor/github.com/urfave/cli/v3/errors.go @@ -150,10 +150,12 @@ func HandleExitCoder(err error) { } if exitErr, ok := err.(ExitCoder); ok { - if _, ok := exitErr.(ErrorFormatter); ok { - _, _ = fmt.Fprintf(ErrWriter, "%+v\n", err) - } else { - _, _ = fmt.Fprintln(ErrWriter, err) + if msg := err.Error(); msg != "" { + if _, ok := exitErr.(ErrorFormatter); ok { + _, _ = fmt.Fprintf(ErrWriter, "%+v\n", err) + } else { + _, _ = fmt.Fprintln(ErrWriter, err) + } } OsExiter(exitErr.ExitCode()) return diff --git a/vendor/github.com/urfave/cli/v3/flag.go b/vendor/github.com/urfave/cli/v3/flag.go index bfac8faaf6..3cb9ab9608 100644 --- a/vendor/github.com/urfave/cli/v3/flag.go +++ b/vendor/github.com/urfave/cli/v3/flag.go @@ -3,7 +3,7 @@ package cli import ( "context" "fmt" - "regexp" + "slices" "strings" "time" ) @@ -16,11 +16,7 @@ const ( disableSliceFlagSeparator = false ) -var ( - slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano()) - - commaWhitespace = regexp.MustCompile("[, ]+.*") -) +var slPfx = fmt.Sprintf("sl:::%d:::", time.Now().UTC().UnixNano()) // GenerateShellCompletionFlag enables shell completion var GenerateShellCompletionFlag Flag = &BoolFlag{ @@ -155,6 +151,23 @@ type DocGenerationMultiValueFlag interface { IsMultiValueFlag() bool } +// SchemaTyper is an optional interface for flags that can report their +// JSON Schema type for programmatic introspection. +type SchemaTyper interface { + // SchemaType returns the JSON Schema type name for the value this + // flag accepts: "boolean", "integer", "number", "string", "array", + // "object". Returns "" if the flag does not map cleanly. + SchemaType() string +} + +// SchemaItemsTyper is an optional interface for multi-value flags that +// can report the JSON Schema type of their elements. +type SchemaItemsTyper interface { + // SchemaItemsType returns the JSON Schema type of elements for + // array-type flags. Returns "" for single-value or object flags. + SchemaItemsType() string +} + // Countable is an interface to enable detection of flag values which support // repetitive flags type Countable interface { @@ -201,20 +214,18 @@ func FlagNames(name string, aliases []string) []string { // Strip off anything after the first found comma or space, which // *hopefully* makes it a tiny bit more obvious that unexpected behavior is // caused by using the v1 form of stringly typed "Name". - ret = append(ret, commaWhitespace.ReplaceAllString(part, "")) + if i := strings.IndexAny(part, ", "); i >= 0 { + ret = append(ret, part[:i]) + } else { + ret = append(ret, part) + } } return ret } func hasFlag(flags []Flag, fl Flag) bool { - for _, existing := range flags { - if fl == existing { - return true - } - } - - return false + return slices.Contains(flags, fl) } func flagSplitMultiValues(val string, sliceSeparator string, disableSliceSeparator bool) []string { diff --git a/vendor/github.com/urfave/cli/v3/flag_bool.go b/vendor/github.com/urfave/cli/v3/flag_bool.go index 0f2af27bf2..f0ec22fafa 100644 --- a/vendor/github.com/urfave/cli/v3/flag_bool.go +++ b/vendor/github.com/urfave/cli/v3/flag_bool.go @@ -69,7 +69,7 @@ func (b *boolValue) Set(s string) error { return err } -func (b *boolValue) Get() interface{} { return *b.destination } +func (b *boolValue) Get() any { return *b.destination } func (b *boolValue) String() string { return strconv.FormatBool(*b.destination) diff --git a/vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go b/vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go index 272dd98fec..199d22c7d9 100644 --- a/vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go +++ b/vendor/github.com/urfave/cli/v3/flag_bool_with_inverse.go @@ -116,7 +116,7 @@ func (bif *BoolWithInverseFlag) PostParse() error { func (bif *BoolWithInverseFlag) Set(name, val string) error { if bif.count > 0 && bif.OnlyOnce { - return fmt.Errorf("cant duplicate this flag") + return fmt.Errorf("can't duplicate this flag") } bif.hasBeenSet = true @@ -167,6 +167,9 @@ func (bif *BoolWithInverseFlag) IsVisible() bool { // // Example for BoolFlag{Name: "env"} // --[no-]env (default: false) +// +// Example for BoolFlag{Name: "env", Aliases: []string{"e"}} +// --[no-]env, -e (default: false) func (bif *BoolWithInverseFlag) String() string { out := FlagStringer(bif) @@ -179,10 +182,32 @@ func (bif *BoolWithInverseFlag) String() string { prefix = "-" } - return fmt.Sprintf("%s[%s]%s%s", prefix, bif.inversePrefix(), bif.Name, out[i:]) + // Guard against a FlagStringer that returns a string without a tab (e.g. + // a custom stringer or the default stringer when the flag does not + // implement DocGenerationFlag). In that case treat the entire output as + // the tab-delimited suffix so the slice never goes out of bounds. + if i < 0 { + i = 0 + } + + var aliasParts []string + for _, alias := range bif.Aliases { + aPrefix := "--" + if len(alias) == 1 { + aPrefix = "-" + } + aliasParts = append(aliasParts, aPrefix+alias) + } + + names := fmt.Sprintf("%s[%s]%s", prefix, bif.inversePrefix(), bif.Name) + if len(aliasParts) > 0 { + names = names + ", " + strings.Join(aliasParts, ", ") + } + + return fmt.Sprintf("%s%s", names, out[i:]) } -// IsBoolFlag returns whether the flag doesnt need to accept args +// IsBoolFlag returns whether the flag doesn't need to accept args func (bif *BoolWithInverseFlag) IsBoolFlag() bool { return true } @@ -238,3 +263,11 @@ func (bif *BoolWithInverseFlag) IsDefaultVisible() bool { func (bif *BoolWithInverseFlag) TypeName() string { return "bool" } + +func (bif *BoolWithInverseFlag) SchemaType() string { + return "boolean" +} + +func (bif *BoolWithInverseFlag) SchemaItemsType() string { + return "" +} diff --git a/vendor/github.com/urfave/cli/v3/flag_ext.go b/vendor/github.com/urfave/cli/v3/flag_ext.go index 9972af7c56..3fb01f2597 100644 --- a/vendor/github.com/urfave/cli/v3/flag_ext.go +++ b/vendor/github.com/urfave/cli/v3/flag_ext.go @@ -1,6 +1,9 @@ package cli -import "flag" +import ( + "flag" + "time" +) type extFlag struct { f *flag.Flag @@ -61,3 +64,26 @@ func (e *extFlag) GetDefaultText() string { func (e *extFlag) GetEnvVars() []string { return nil } + +func (e *extFlag) SchemaType() string { + switch e.Get().(type) { + case bool: + return "boolean" + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return "integer" + case float32, float64: + return "number" + case string: + return "string" + case time.Duration: + return "duration" + case time.Time: + return "date-time" + default: + return "" + } +} + +func (e *extFlag) SchemaItemsType() string { + return "" +} diff --git a/vendor/github.com/urfave/cli/v3/flag_impl.go b/vendor/github.com/urfave/cli/v3/flag_impl.go index c7cc8dffc5..a67208c91c 100644 --- a/vendor/github.com/urfave/cli/v3/flag_impl.go +++ b/vendor/github.com/urfave/cli/v3/flag_impl.go @@ -6,6 +6,7 @@ import ( "fmt" "reflect" "strings" + "time" ) // Value represents a value as used by cli. @@ -185,7 +186,7 @@ func (f *FlagBase[T, C, V]) Set(_ string, val string) error { // lots of units tests prior to persistent flags assumed that the // flag can be applied to different flag sets multiple times while still // keeping the env set. - if !f.applied || f.Local { + if !f.applied { if err := f.PreParse(); err != nil { return err } @@ -193,7 +194,7 @@ func (f *FlagBase[T, C, V]) Set(_ string, val string) error { } if f.count == 1 && f.OnlyOnce { - return fmt.Errorf("cant duplicate this flag") + return fmt.Errorf("can't duplicate this flag") } f.count++ @@ -285,6 +286,51 @@ func (f *FlagBase[T, C, V]) RunAction(ctx context.Context, cmd *Command) error { return nil } +// SchemaType returns the JSON Schema type for the flag's value type. +func (f *FlagBase[T, C, V]) SchemaType() string { + var zero T + switch any(zero).(type) { + case bool: + return "boolean" + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return "integer" + case float32, float64: + return "number" + case string: + return "string" + case time.Duration: + return "duration" + case time.Time: + return "date-time" + case []string, []int, []int8, []int16, []int32, []int64, + []uint, []uint8, []uint16, []uint32, []uint64, + []float32, []float64: + return "array" + case map[string]string: + return "object" + default: + return "" + } +} + +// SchemaItemsType returns the JSON Schema element type for slice flags. +func (f *FlagBase[T, C, V]) SchemaItemsType() string { + var zero T + t := reflect.TypeOf(zero) + if t.Kind() == reflect.Slice { + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return "integer" + case reflect.Float32, reflect.Float64: + return "number" + case reflect.String: + return "string" + } + } + return "" +} + // IsMultiValueFlag returns true if the value type T can take multiple // values from cmd line. This is true for slice and map type flags func (f *FlagBase[T, C, VC]) IsMultiValueFlag() bool { @@ -301,7 +347,7 @@ func (f *FlagBase[T, C, VC]) IsLocal() bool { return f.Local } -// IsBoolFlag returns whether the flag doesnt need to accept args +// IsBoolFlag returns whether the flag doesn't need to accept args func (f *FlagBase[T, C, VC]) IsBoolFlag() bool { bf, ok := f.value.(boolFlag) return ok && bf.IsBoolFlag() diff --git a/vendor/github.com/urfave/cli/v3/flag_map_impl.go b/vendor/github.com/urfave/cli/v3/flag_map_impl.go index b56d0a9733..cb65903b92 100644 --- a/vendor/github.com/urfave/cli/v3/flag_map_impl.go +++ b/vendor/github.com/urfave/cli/v3/flag_map_impl.go @@ -115,7 +115,7 @@ func (i *MapBase[T, C, VC]) Value() map[string]T { } // Get returns the mapping of values set by this flag -func (i *MapBase[T, C, VC]) Get() interface{} { +func (i *MapBase[T, C, VC]) Get() any { return *i.dict } diff --git a/vendor/github.com/urfave/cli/v3/flag_slice_base.go b/vendor/github.com/urfave/cli/v3/flag_slice_base.go index 0248d8f1dc..1f9561ae21 100644 --- a/vendor/github.com/urfave/cli/v3/flag_slice_base.go +++ b/vendor/github.com/urfave/cli/v3/flag_slice_base.go @@ -105,7 +105,7 @@ func (i *SliceBase[T, C, VC]) Value() []T { } // Get returns the slice of values set by this flag -func (i *SliceBase[T, C, VC]) Get() interface{} { +func (i *SliceBase[T, C, VC]) Get() any { return *i.slice } diff --git a/vendor/github.com/urfave/cli/v3/godoc-current.txt b/vendor/github.com/urfave/cli/v3/godoc-current.txt index 9784c4e2b3..d680c5f544 100644 --- a/vendor/github.com/urfave/cli/v3/godoc-current.txt +++ b/vendor/github.com/urfave/cli/v3/godoc-current.txt @@ -170,7 +170,9 @@ COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategori OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .VisiblePersistentFlags}} + +GLOBAL OPTIONS:{{template "visiblePersistentFlagTemplate" .}}{{end}} ` SubcommandHelpTemplate is the text template for the subcommand help topic. cli.go uses text/template to render templates. You can render custom help @@ -384,7 +386,7 @@ func (bif *BoolWithInverseFlag) GetValue() string string if the flag takes no value at all. func (bif *BoolWithInverseFlag) IsBoolFlag() bool - IsBoolFlag returns whether the flag doesnt need to accept args + IsBoolFlag returns whether the flag doesn't need to accept args func (bif *BoolWithInverseFlag) IsDefaultVisible() bool IsDefaultVisible returns true if the flag is not hidden, otherwise false @@ -405,6 +407,10 @@ func (bif *BoolWithInverseFlag) PreParse() error func (bif *BoolWithInverseFlag) RunAction(ctx context.Context, cmd *Command) error +func (bif *BoolWithInverseFlag) SchemaItemsType() string + +func (bif *BoolWithInverseFlag) SchemaType() string + func (bif *BoolWithInverseFlag) Set(name, val string) error func (bif *BoolWithInverseFlag) SetCategory(c string) @@ -414,6 +420,9 @@ func (bif *BoolWithInverseFlag) String() string Example for BoolFlag{Name: "env"} --[no-]env (default: false) + Example for BoolFlag{Name: "env", Aliases: []string{"e"}} --[no-]env, + -e (default: false) + func (bif *BoolWithInverseFlag) TakesValue() bool func (bif *BoolWithInverseFlag) TypeName() string @@ -499,7 +508,7 @@ type Command struct { // default behavior. ExitErrHandler ExitErrHandlerFunc `json:"-"` // Other custom info - Metadata map[string]interface{} `json:"metadata"` + Metadata map[string]any `json:"metadata"` // Carries a function which returns app specific info. ExtraInfo func() map[string]string `json:"-"` // CustomRootCommandHelpTemplate the text template for app help topic. @@ -603,8 +612,8 @@ func (cmd *Command) FloatSlice(name string) []float64 found func (cmd *Command) FullName() string - FullName returns the full name of the command. For commands with parents - this ensures that the parent commands are part of the command path. + FullName returns the full name of the command. Includes parent commands + separated by space. func (cmd *Command) Generic(name string) Value Generic looks up the value of a local GenericFlag, returns nil if not found @@ -686,6 +695,12 @@ func (cmd *Command) Names() []string func (cmd *Command) NumFlags() int NumFlags returns the number of flags set +func (cmd *Command) Path() []string + Path returns the path of command names from the root to cmd, inclusive. + Each element is a Command.Name. Path traverses upward via parent pointers + similar to Lineage. FullName() is equivalent to strings.Join(cmd.Path(), + " "). + func (cmd *Command) Root() *Command Root returns the Command at the root of the graph @@ -777,7 +792,7 @@ func (cmd *Command) UintSlice(name string) []uint UintSlice looks up the value of a local UintSliceFlag, returns nil if not found -func (cmd *Command) Value(name string) interface{} +func (cmd *Command) Value(name string) any Value returns the value of the flag corresponding to `name` func (cmd *Command) VisibleCategories() []CommandCategory @@ -798,6 +813,10 @@ func (cmd *Command) VisiblePersistentFlags() []Flag VisiblePersistentFlags returns a slice of LocalFlag with Persistent=true and Hidden=false. +func (cmd *Command) Walk(fn func(*Command) error) error + Walk visits cmd and every descendant. If fn returns a non-nil error, + the walk terminates and the error is returned to the caller. + type CommandCategories interface { // AddCommand adds a command to a category, creating a new category if necessary. AddCommand(category string, command *Command) @@ -997,7 +1016,7 @@ func (f *FlagBase[T, C, V]) GetValue() string string if the flag takes no value at all. func (f *FlagBase[T, C, VC]) IsBoolFlag() bool - IsBoolFlag returns whether the flag doesnt need to accept args + IsBoolFlag returns whether the flag doesn't need to accept args func (f *FlagBase[T, C, V]) IsDefaultVisible() bool IsDefaultVisible returns true if the flag is not hidden, otherwise false @@ -1029,6 +1048,12 @@ func (f *FlagBase[T, C, V]) PreParse() error func (f *FlagBase[T, C, V]) RunAction(ctx context.Context, cmd *Command) error RunAction executes flag action if set +func (f *FlagBase[T, C, V]) SchemaItemsType() string + SchemaItemsType returns the JSON Schema element type for slice flags. + +func (f *FlagBase[T, C, V]) SchemaType() string + SchemaType returns the JSON Schema type for the flag's value type. + func (f *FlagBase[T, C, V]) Set(_ string, val string) error Set applies given value from string @@ -1225,7 +1250,7 @@ func NewMapBase[T any, C any, VC ValueCreator[T, C]](defaults map[string]T) *Map func (i MapBase[T, C, VC]) Create(val map[string]T, p *map[string]T, c C) Value -func (i *MapBase[T, C, VC]) Get() interface{} +func (i *MapBase[T, C, VC]) Get() any Get returns the mapping of values set by this flag func (i *MapBase[T, C, VC]) Serialize() string @@ -1293,6 +1318,23 @@ type RequiredFlag interface { it allows flags required flags to be backwards compatible with the Flag interface +type SchemaItemsTyper interface { + // SchemaItemsType returns the JSON Schema type of elements for + // array-type flags. Returns "" for single-value or object flags. + SchemaItemsType() string +} + SchemaItemsTyper is an optional interface for multi-value flags that can + report the JSON Schema type of their elements. + +type SchemaTyper interface { + // SchemaType returns the JSON Schema type name for the value this + // flag accepts: "boolean", "integer", "number", "string", "array", + // "object". Returns "" if the flag does not map cleanly. + SchemaType() string +} + SchemaTyper is an optional interface for flags that can report their JSON + Schema type for programmatic introspection. + type Serializer interface { Serialize() string } @@ -1312,7 +1354,7 @@ func NewSliceBase[T any, C any, VC ValueCreator[T, C]](defaults ...T) *SliceBase func (i SliceBase[T, C, VC]) Create(val []T, p *[]T, c C) Value -func (i *SliceBase[T, C, VC]) Get() interface{} +func (i *SliceBase[T, C, VC]) Get() any Get returns the slice of values set by this flag func (i *SliceBase[T, C, VC]) Serialize() string diff --git a/vendor/github.com/urfave/cli/v3/help.go b/vendor/github.com/urfave/cli/v3/help.go index 9db9df510c..4bedf87d5d 100644 --- a/vendor/github.com/urfave/cli/v3/help.go +++ b/vendor/github.com/urfave/cli/v3/help.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "slices" "strings" "text/tabwriter" "text/template" @@ -64,11 +65,12 @@ var ArgsUsageCommandHelp = "[command]" func buildHelpCommand(withAction bool) *Command { cmd := &Command{ - Name: helpName, - Aliases: []string{helpAlias}, - Usage: UsageCommandHelp, - ArgsUsage: ArgsUsageCommandHelp, - HideHelp: true, + Name: helpName, + Aliases: []string{helpAlias}, + Usage: UsageCommandHelp, + ArgsUsage: ArgsUsageCommandHelp, + HideHelp: true, + builtInHelp: true, } if withAction { @@ -84,14 +86,22 @@ func helpCommandAction(ctx context.Context, cmd *Command) error { tracef("doing help for cmd %[1]q with args %[2]q", cmd, args) - // This action can be triggered by a "default" action of a command - // or via cmd.Run when cmd == helpCmd. So we have following possibilities + // helpCommandAction is triggered in several ways: // - // 1 $ app - // 2 $ app help - // 3 $ app foo - // 4 $ app help foo - // 5 $ app foo help + // * the command has no user-defined Action (default action fallback) + // * the --help / -h flag was parsed (via cmd.checkHelp()) + // * the "help" subcommand (or "h" alias) was dispatched + // + // Possible invocations: + // + // $ app # default action; show root help + // $ app --help / -h # flag; show root help (ignores subsequent args) + // $ app help / h # subcommand; show root help + // $ app help / h foo # subcommand; show help for subcommand "foo" + // $ app --help / -h foo # flag; show help for subcommand "foo" + // $ app foo --help / -h # flag on subcommand; show help for "foo" + // $ app foo help / h # subcommand on subcommand; show help for "foo" + // $ app foo (no action) # default action on subcommand; show help for "foo" // Case 4. when executing a help command set the context to parent // to allow resolution of subsequent args. This will transform @@ -99,7 +109,7 @@ func helpCommandAction(ctx context.Context, cmd *Command) error { // to // $ app foo // which will then be handled as case 3 - if cmd.parent != nil && (cmd.HasName(helpName) || cmd.HasName(helpAlias)) { + if cmd.parent != nil && cmd.builtInHelp { tracef("setting cmd to cmd.parent") cmd = cmd.parent } @@ -125,16 +135,8 @@ func helpCommandAction(ctx context.Context, cmd *Command) error { // Case 3, 5 if len(cmd.VisibleCommands()) == 0 { - - tmpl := cmd.CustomHelpTemplate - if tmpl == "" { - tmpl = CommandHelpTemplate - } - tracef("running HelpPrinter with command %[1]q", cmd.Name) - HelpPrinter(cmd.Root().Writer, tmpl, cmd) - - return nil + return ShowCommandHelp(ctx, cmd.parent, cmd.Name) } tracef("running ShowSubcommandHelp") @@ -183,12 +185,11 @@ func DefaultRootCommandComplete(ctx context.Context, cmd *Command) { var DefaultAppComplete = DefaultRootCommandComplete func printCommandSuggestions(commands []*Command, writer io.Writer) { - shell := os.Getenv("SHELL") for _, command := range commands { if command.Hidden { continue } - if (strings.HasSuffix(shell, "zsh") || strings.HasSuffix(shell, "fish")) && len(command.Usage) > 0 { + if len(command.Usage) > 0 { _, _ = fmt.Fprintf(writer, "%s:%s\n", command.Name, command.Usage) } else { _, _ = fmt.Fprintf(writer, "%s\n", command.Name) @@ -204,10 +205,8 @@ func cliArgContains(flagName string, args []string) bool { count = 2 } flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - for _, a := range args { - if a == flag { - return true - } + if slices.Contains(args, flag) { + return true } } return false @@ -240,8 +239,7 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { // match if last argument matches this flag and it is not repeated if strings.HasPrefix(name, cur) && cur != name /* && !cliArgContains(name, os.Args)*/ { flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - shell := os.Getenv("SHELL") - if usage != "" && (strings.HasSuffix(shell, "zsh") || strings.HasSuffix(shell, "fish")) { + if usage != "" { flagCompletion = fmt.Sprintf("%s:%s", flagCompletion, usage) } fmt.Fprintln(writer, flagCompletion) @@ -267,11 +265,6 @@ func DefaultCompleteWithFlags(ctx context.Context, cmd *Command) { lastArg = args[argsLen-1] } - if lastArg == "--" { - tracef("No completions due to termination") - return - } - if lastArg == completionFlag { lastArg = "" } @@ -304,7 +297,7 @@ func DefaultShowCommandHelp(ctx context.Context, cmd *Command, commandName strin tmpl := subCmd.CustomHelpTemplate if tmpl == "" { - if len(subCmd.Commands) == 0 { + if len(subCmd.VisibleCommands()) == 0 { tracef("using CommandHelpTemplate") tmpl = CommandHelpTemplate } else { @@ -475,13 +468,7 @@ func DefaultPrintHelp(out io.Writer, templ string, data any) { } func checkVersion(cmd *Command) bool { - found := false - for _, name := range VersionFlag.Names() { - if cmd.Bool(name) { - found = true - } - } - return found + return cmd.versionFlag != nil && cmd.versionFlag.IsSet() } func checkShellCompleteFlag(c *Command, arguments []string) (bool, []string) { @@ -496,19 +483,19 @@ func checkShellCompleteFlag(c *Command, arguments []string) (bool, []string) { return false, arguments } - for _, arg := range arguments { - // If arguments include "--", shell completion is disabled - // because after "--" only positional arguments are accepted. - // https://unix.stackexchange.com/a/11382 - if arg == "--" { - return false, arguments[:pos] - } + // If arguments include "--" before the token being completed, shell completion + // is disabled because after "--" only positional arguments are accepted. + // https://unix.stackexchange.com/a/11382 + // Note: The token being completed is at position pos-1 (immediately before completionFlag). + // We only check arguments before that position, so completing "--" itself still works. + if pos >= 1 && slices.Contains(arguments[:pos-1], "--") { + return false, arguments[:pos] } return true, arguments[:pos] } -func checkCompletions(ctx context.Context, cmd *Command) bool { +func shouldRunCompletion(cmd *Command) bool { tracef("checking completions on command %[1]q", cmd.Name) if !cmd.Root().shellCompletion { @@ -525,13 +512,14 @@ func checkCompletions(ctx context.Context, cmd *Command) bool { } tracef("no subcommand found for completion %[1]q", cmd.Name) + return true +} +func runCompletion(ctx context.Context, cmd *Command) { if cmd.ShellComplete != nil { tracef("running shell completion func for command %[1]q", cmd.Name) cmd.ShellComplete(ctx, cmd) } - - return true } func subtract(a, b int) int { diff --git a/vendor/github.com/urfave/cli/v3/mkdocs-requirements.txt b/vendor/github.com/urfave/cli/v3/mkdocs-requirements.txt index e41a984dbf..701403ae56 100644 --- a/vendor/github.com/urfave/cli/v3/mkdocs-requirements.txt +++ b/vendor/github.com/urfave/cli/v3/mkdocs-requirements.txt @@ -1,5 +1,5 @@ -mkdocs-git-revision-date-localized-plugin==1.5.1 -mkdocs-material==9.7.1 +mkdocs-git-revision-date-localized-plugin==1.5.3 +mkdocs-material==9.7.6 mkdocs==1.6.1 -mkdocs-redirects==1.2.2 -pygments==2.19.2 +mkdocs-redirects==1.2.3 +pygments==2.20.0 diff --git a/vendor/github.com/urfave/cli/v3/mkdocs.yml b/vendor/github.com/urfave/cli/v3/mkdocs.yml index e1eac95871..4539a73a4e 100644 --- a/vendor/github.com/urfave/cli/v3/mkdocs.yml +++ b/vendor/github.com/urfave/cli/v3/mkdocs.yml @@ -20,6 +20,8 @@ nav: - v3 Manual: - Getting Started: v3/getting-started.md - Migrating From Older Releases: v3/migrating-from-older-releases.md + - Path and Walk: v3/path-and-walk.md + - Binary Size: v3/binary-size.md - Examples: - Greet: v3/examples/greet.md - Flags: diff --git a/vendor/github.com/urfave/cli/v3/suggestions.go b/vendor/github.com/urfave/cli/v3/suggestions.go index 6f29f12213..401dcef253 100644 --- a/vendor/github.com/urfave/cli/v3/suggestions.go +++ b/vendor/github.com/urfave/cli/v3/suggestions.go @@ -92,7 +92,7 @@ func jaroWinkler(a, b string) float64 { prefix := int(math.Min(float64(len(a)), math.Min(float64(prefixSize), float64(len(b))))) var prefixMatch float64 - for i := 0; i < prefix; i++ { + for i := range prefix { if a[i] == b[i] { prefixMatch++ } else { diff --git a/vendor/github.com/urfave/cli/v3/template.go b/vendor/github.com/urfave/cli/v3/template.go index dd144e77dd..92030cc8fd 100644 --- a/vendor/github.com/urfave/cli/v3/template.go +++ b/vendor/github.com/urfave/cli/v3/template.go @@ -107,7 +107,9 @@ COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategori OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} -OPTIONS:{{template "visibleFlagTemplate" .}}{{end}} +OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}{{if .VisiblePersistentFlags}} + +GLOBAL OPTIONS:{{template "visiblePersistentFlagTemplate" .}}{{end}} ` var FishCompletionTemplate = `# {{ .Command.Name }} fish shell completion diff --git a/vendor/modules.txt b/vendor/modules.txt index 3ae4834625..ab669ae287 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -397,7 +397,7 @@ github.com/ulikunitz/xz github.com/ulikunitz/xz/internal/hash github.com/ulikunitz/xz/internal/xlog github.com/ulikunitz/xz/lzma -# github.com/urfave/cli/v3 v3.7.0 +# github.com/urfave/cli/v3 v3.10.0 ## explicit; go 1.22 github.com/urfave/cli/v3 # github.com/x448/float16 v0.8.4