From 3daa4b9c36617509a756832a3d0ee2b6f4b8c129 Mon Sep 17 00:00:00 2001 From: Gyanendra Mishra Date: Sat, 25 Feb 2023 20:57:12 +0000 Subject: [PATCH 01/26] Add keeporder to shell completion (#1903) This allows programs to request the shell to maintain the order of completions that was returned by the program --- bash_completionsV2.go | 18 ++++++++-- completions.go | 7 ++++ fish_completions.go | 76 ++++++++++++++++++++++++++++++++++----- powershell_completions.go | 10 ++++-- shell_completions.md | 4 +++ zsh_completions.go | 14 +++++--- 6 files changed, 112 insertions(+), 17 deletions(-) diff --git a/bash_completionsV2.go b/bash_completionsV2.go index 78f43b91..5a38e38d 100644 --- a/bash_completionsV2.go +++ b/bash_completionsV2.go @@ -101,6 +101,7 @@ __%[1]s_process_completion_results() { local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d + local shellCompDirectiveKeepOrder=%[8]d if (((directive & shellCompDirectiveError) != 0)); then # Error code. No completion. @@ -115,6 +116,19 @@ __%[1]s_process_completion_results() { __%[1]s_debug "No space directive not supported in this version of bash" fi fi + if (((directive & shellCompDirectiveKeepOrder) != 0)); then + if [[ $(type -t compopt) == builtin ]]; then + # no sort isn't supported for bash less than < 4.4 + if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then + __%[1]s_debug "No sort directive not supported in this version of bash" + else + __%[1]s_debug "Activating keep order" + compopt -o nosort + fi + else + __%[1]s_debug "No sort directive not supported in this version of bash" + fi + fi if (((directive & shellCompDirectiveNoFileComp) != 0)); then if [[ $(type -t compopt) == builtin ]]; then __%[1]s_debug "Activating no file completion" @@ -183,7 +197,7 @@ __%[1]s_process_completion_results() { # Separate activeHelp lines from real completions. # Fills the $activeHelp and $completions arrays. __%[1]s_extract_activeHelp() { - local activeHelpMarker="%[8]s" + local activeHelpMarker="%[9]s" local endIndex=${#activeHelpMarker} while IFS='' read -r comp; do @@ -360,7 +374,7 @@ fi # ex: ts=4 sw=4 et filetype=sh `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpMarker)) } diff --git a/completions.go b/completions.go index 8341959a..c7b457eb 100644 --- a/completions.go +++ b/completions.go @@ -77,6 +77,10 @@ const ( // obtain the same behavior but only for flags. ShellCompDirectiveFilterDirs + // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order + // in which the completions are provided + ShellCompDirectiveKeepOrder + // =========================================================================== // All directives using iota should be above this one. @@ -159,6 +163,9 @@ func (d ShellCompDirective) string() string { if d&ShellCompDirectiveFilterDirs != 0 { directives = append(directives, "ShellCompDirectiveFilterDirs") } + if d&ShellCompDirectiveKeepOrder != 0 { + directives = append(directives, "ShellCompDirectiveKeepOrder") + } if len(directives) == 0 { directives = append(directives, "ShellCompDirectiveDefault") } diff --git a/fish_completions.go b/fish_completions.go index 97112a17..32c4bea0 100644 --- a/fish_completions.go +++ b/fish_completions.go @@ -53,7 +53,7 @@ function __%[1]s_perform_completion __%[1]s_debug "last arg: $lastArg" # Disable ActiveHelp which is not supported for fish shell - set -l requestComp "%[9]s=0 $args[1] %[3]s $args[2..-1] $lastArg" + set -l requestComp "%[10]s=0 $args[1] %[3]s $args[2..-1] $lastArg" __%[1]s_debug "Calling $requestComp" set -l results (eval $requestComp 2> /dev/null) @@ -89,6 +89,60 @@ function __%[1]s_perform_completion printf "%%s\n" "$directiveLine" end +# this function limits calls to __%[1]s_perform_completion, by caching the result behind $__%[1]s_perform_completion_once_result +function __%[1]s_perform_completion_once + __%[1]s_debug "Starting __%[1]s_perform_completion_once" + + if test -n "$__%[1]s_perform_completion_once_result" + __%[1]s_debug "Seems like a valid result already exists, skipping __%[1]s_perform_completion" + return 0 + end + + set --global __%[1]s_perform_completion_once_result (__%[1]s_perform_completion) + if test -z "$__%[1]s_perform_completion_once_result" + __%[1]s_debug "No completions, probably due to a failure" + return 1 + end + + __%[1]s_debug "Performed completions and set __%[1]s_perform_completion_once_result" + return 0 +end + +# this function is used to clear the $__%[1]s_perform_completion_once_result variable after completions are run +function __%[1]s_clear_perform_completion_once_result + __%[1]s_debug "" + __%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable ==========" + set --erase __%[1]s_perform_completion_once_result + __%[1]s_debug "Succesfully erased the variable __%[1]s_perform_completion_once_result" +end + +function __%[1]s_requires_order_preservation + __%[1]s_debug "" + __%[1]s_debug "========= checking if order preservation is required ==========" + + __%[1]s_perform_completion_once + if test -z "$__%[1]s_perform_completion_once_result" + __%[1]s_debug "Error determining if order preservation is required" + return 1 + end + + set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1]) + __%[1]s_debug "Directive is: $directive" + + set -l shellCompDirectiveKeepOrder %[9]d + set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) %% 2) + __%[1]s_debug "Keeporder is: $keeporder" + + if test $keeporder -ne 0 + __%[1]s_debug "This does require order preservation" + return 0 + end + + __%[1]s_debug "This doesn't require order preservation" + return 1 +end + + # This function does two things: # - Obtain the completions and store them in the global __%[1]s_comp_results # - Return false if file completion should be performed @@ -99,17 +153,17 @@ function __%[1]s_prepare_completions # Start fresh set --erase __%[1]s_comp_results - set -l results (__%[1]s_perform_completion) - __%[1]s_debug "Completion results: $results" + __%[1]s_perform_completion_once + __%[1]s_debug "Completion results: $__%[1]s_perform_completion_once_result" - if test -z "$results" + if test -z "$__%[1]s_perform_completion_once_result" __%[1]s_debug "No completion, probably due to a failure" # Might as well do file completion, in case it helps return 1 end - set -l directive (string sub --start 2 $results[-1]) - set --global __%[1]s_comp_results $results[1..-2] + set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1]) + set --global __%[1]s_comp_results $__%[1]s_perform_completion_once_result[1..-2] __%[1]s_debug "Completions are: $__%[1]s_comp_results" __%[1]s_debug "Directive is: $directive" @@ -205,13 +259,17 @@ end # Remove any pre-existing completions for the program since we will be handling all of them. complete -c %[2]s -e +# this will get called after the two calls below and clear the $__%[1]s_perform_completion_once_result global +complete -c %[2]s -n '__%[1]s_clear_perform_completion_once_result' # The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results # which provides the program's completion choices. -complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' - +# If this doesn't require order preservation, we don't use the -k flag +complete -c %[2]s -n 'not __%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' +# otherwise we use the -k flag +complete -k -c %[2]s -n '__%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' `, nameForVar, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name))) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) } // GenFishCompletion generates fish completion file and writes to the passed writer. diff --git a/powershell_completions.go b/powershell_completions.go index c9d47e61..c766fc78 100644 --- a/powershell_completions.go +++ b/powershell_completions.go @@ -77,6 +77,7 @@ filter __%[1]s_escapeStringWithSpecialChars { $ShellCompDirectiveNoFileComp=%[6]d $ShellCompDirectiveFilterFileExt=%[7]d $ShellCompDirectiveFilterDirs=%[8]d + $ShellCompDirectiveKeepOrder=%[9]d # Prepare the command to request completions for the program. # Split the command at the first space to separate the program and arguments. @@ -112,7 +113,7 @@ filter __%[1]s_escapeStringWithSpecialChars { __%[1]s_debug "Calling $RequestComp" # First disable ActiveHelp which is not supported for Powershell - $env:%[9]s=0 + $env:%[10]s=0 #call the command store the output in $out and redirect stderr and stdout to null # $Out is an array contains each line per element @@ -182,6 +183,11 @@ filter __%[1]s_escapeStringWithSpecialChars { } } + # we sort the values in ascending order by name if keep order isn't passed + if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) { + $Values = $Values | Sort-Object -Property Name + } + if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { __%[1]s_debug "ShellCompDirectiveNoFileComp is called" @@ -267,7 +273,7 @@ filter __%[1]s_escapeStringWithSpecialChars { Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock $__%[2]sCompleterBlock `, name, nameForVar, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name))) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) } func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error { diff --git a/shell_completions.md b/shell_completions.md index 6c48f3e4..065c0621 100644 --- a/shell_completions.md +++ b/shell_completions.md @@ -228,6 +228,10 @@ ShellCompDirectiveFilterFileExt // return []string{"themes"}, ShellCompDirectiveFilterDirs // ShellCompDirectiveFilterDirs + +// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order +// in which the completions are provided +ShellCompDirectiveKeepOrder ``` ***Note***: When using the `ValidArgsFunction`, Cobra will call your registered function after having parsed all flags and arguments provided in the command-line. You therefore don't need to do this parsing yourself. For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function. diff --git a/zsh_completions.go b/zsh_completions.go index 84cec76f..29bcb570 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -108,8 +108,9 @@ _%[1]s() local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d + local shellCompDirectiveKeepOrder=%[8]d - local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace + local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder local -a completions __%[1]s_debug "\n========= starting completion logic ==========" @@ -177,7 +178,7 @@ _%[1]s() return fi - local activeHelpMarker="%[8]s" + local activeHelpMarker="%[9]s" local endIndex=${#activeHelpMarker} local startIndex=$((${#activeHelpMarker}+1)) local hasActiveHelp=0 @@ -227,6 +228,11 @@ _%[1]s() noSpace="-S ''" fi + if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then + __%[1]s_debug "Activating keep order." + keepOrder="-V" + fi + if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local filteringCmd @@ -262,7 +268,7 @@ _%[1]s() return $result else __%[1]s_debug "Calling _describe" - if eval _describe "completions" completions $flagPrefix $noSpace; then + if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then __%[1]s_debug "_describe found some completions" # Return the success of having called _describe @@ -296,6 +302,6 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then fi `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpMarker)) } From 567ea8ebc9b4385ae671486415cd203fc2a3bab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Sat, 25 Feb 2023 22:30:37 +0100 Subject: [PATCH 02/26] Add support for PowerShell 7.2+ (#1916) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PowerShell 7.2 has changed the way arguments are passed to executables. This was originally an experimental feature in 7.2, but as of 7.3 it is built-in. A simple "" is now sufficient for passing empty arguments, no back-tick escaping is required. Fixes #1849 Signed-off-by: Oldřich Jedlička Co-authored-by: Oldřich Jedlička --- powershell_completions.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/powershell_completions.go b/powershell_completions.go index c766fc78..927733b3 100644 --- a/powershell_completions.go +++ b/powershell_completions.go @@ -107,8 +107,17 @@ filter __%[1]s_escapeStringWithSpecialChars { # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __%[1]s_debug "Adding extra empty parameter" -`+" # We need to use `\"`\" to pass an empty argument a \"\" or '' does not work!!!"+` -`+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+` + # PowerShell 7.2+ changed the way how the arguments are passed to executables, + # so for pre-7.2 or when Legacy argument passing is enabled we need to use +`+" # `\"`\" to pass an empty argument, a \"\" or '' does not work!!!"+` + if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or + ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or + (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and + $PSNativeCommandArgumentPassing -eq 'Legacy')) { +`+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+` + } else { + $RequestComp="$RequestComp" + ' ""' + } } __%[1]s_debug "Calling $RequestComp" From c7300f0bdd22311c9a823ea0ea773bf968100eac Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral <38422348+umarcor@users.noreply.github.com> Date: Mon, 6 Mar 2023 02:07:19 +0000 Subject: [PATCH 03/26] ci: deprecate go 1.16 (#1926) --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b4307724..5ffcebaf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,7 +63,6 @@ jobs: - ubuntu - macOS go: - - 16 - 17 - 18 - 19 From fb3652402b30b582975880f652e7ed81343e8312 Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral <38422348+umarcor@users.noreply.github.com> Date: Mon, 6 Mar 2023 02:11:33 +0000 Subject: [PATCH 04/26] ci: test Golang 1.20 (#1925) --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5ffcebaf..f3076b79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,7 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: '^1.19' + go-version: '^1.20' check-latest: true cache: true @@ -66,6 +66,7 @@ jobs: - 17 - 18 - 19 + - 20 name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x' runs-on: ${{ matrix.platform }}-latest steps: From 9e6b58afc70c60a6b3c8a0138fb25acc734d47e3 Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral <38422348+umarcor@users.noreply.github.com> Date: Mon, 6 Mar 2023 02:28:31 +0000 Subject: [PATCH 05/26] update copyright year (#1927) --- .github/workflows/test.yml | 2 +- .golangci.yml | 2 +- active_help.go | 2 +- active_help_test.go | 2 +- args.go | 2 +- args_test.go | 2 +- bash_completions.go | 2 +- bash_completionsV2.go | 2 +- bash_completionsV2_test.go | 2 +- bash_completions_test.go | 2 +- cobra.go | 2 +- cobra_test.go | 2 +- command.go | 2 +- command_notwin.go | 2 +- command_test.go | 2 +- command_win.go | 2 +- completions.go | 2 +- completions_test.go | 2 +- doc/cmd_test.go | 2 +- doc/man_docs.go | 2 +- doc/man_docs_test.go | 2 +- doc/man_examples_test.go | 2 +- doc/md_docs.go | 2 +- doc/md_docs_test.go | 2 +- doc/rest_docs.go | 2 +- doc/rest_docs_test.go | 2 +- doc/util.go | 2 +- doc/yaml_docs.go | 2 +- doc/yaml_docs_test.go | 2 +- fish_completions.go | 2 +- fish_completions_test.go | 2 +- flag_groups.go | 2 +- flag_groups_test.go | 2 +- powershell_completions.go | 2 +- powershell_completions_test.go | 2 +- shell_completions.go | 2 +- zsh_completions.go | 2 +- zsh_completions_test.go | 2 +- 38 files changed, 38 insertions(+), 38 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f3076b79..6a0c7bb8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: -v $(pwd):/wrk -w /wrk ghcr.io/google/addlicense -c 'The Cobra Authors' - -y '2013-2022' + -y '2013-2023' -l apache -ignore '.github/**' -check diff --git a/.golangci.yml b/.golangci.yml index 439d3e1d..2578d94b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,4 +1,4 @@ -# Copyright 2013-2022 The Cobra Authors +# Copyright 2013-2023 The Cobra Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/active_help.go b/active_help.go index 95e03aec..2d023943 100644 --- a/active_help.go +++ b/active_help.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/active_help_test.go b/active_help_test.go index 3653dd87..2d624794 100644 --- a/active_help_test.go +++ b/active_help_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/args.go b/args.go index 2f4056c8..e79ec33a 100644 --- a/args.go +++ b/args.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/args_test.go b/args_test.go index fd2c3b41..90d174cc 100644 --- a/args_test.go +++ b/args_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bash_completions.go b/bash_completions.go index a9f4e4f2..10c78847 100644 --- a/bash_completions.go +++ b/bash_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bash_completionsV2.go b/bash_completionsV2.go index 5a38e38d..19b09560 100644 --- a/bash_completionsV2.go +++ b/bash_completionsV2.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bash_completionsV2_test.go b/bash_completionsV2_test.go index 9302baf0..88587e29 100644 --- a/bash_completionsV2_test.go +++ b/bash_completionsV2_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bash_completions_test.go b/bash_completions_test.go index 5c32306d..44412577 100644 --- a/bash_completions_test.go +++ b/bash_completions_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cobra.go b/cobra.go index 841cda0c..b07b44a0 100644 --- a/cobra.go +++ b/cobra.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cobra_test.go b/cobra_test.go index 71353c9d..fbb07f9b 100644 --- a/cobra_test.go +++ b/cobra_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/command.go b/command.go index b6e3f4a1..01f7c6f1 100644 --- a/command.go +++ b/command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/command_notwin.go b/command_notwin.go index 2b77f8f0..307f0c12 100644 --- a/command_notwin.go +++ b/command_notwin.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/command_test.go b/command_test.go index 18011325..0212f5ae 100644 --- a/command_test.go +++ b/command_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/command_win.go b/command_win.go index 520f23ab..adbef395 100644 --- a/command_win.go +++ b/command_win.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/completions.go b/completions.go index c7b457eb..8a42f889 100644 --- a/completions.go +++ b/completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/completions_test.go b/completions_test.go index 2364ed2e..0588da0f 100644 --- a/completions_test.go +++ b/completions_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/cmd_test.go b/doc/cmd_test.go index 4f2a0f58..0d022c77 100644 --- a/doc/cmd_test.go +++ b/doc/cmd_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/man_docs.go b/doc/man_docs.go index b5a2c596..b8c15ce8 100644 --- a/doc/man_docs.go +++ b/doc/man_docs.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go index d296eebe..c111d455 100644 --- a/doc/man_docs_test.go +++ b/doc/man_docs_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/man_examples_test.go b/doc/man_examples_test.go index b995bf6d..873b2b6d 100644 --- a/doc/man_examples_test.go +++ b/doc/man_examples_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/md_docs.go b/doc/md_docs.go index bab4b496..c4a27c00 100644 --- a/doc/md_docs.go +++ b/doc/md_docs.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/md_docs_test.go b/doc/md_docs_test.go index b1632e9c..e70cad82 100644 --- a/doc/md_docs_test.go +++ b/doc/md_docs_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/rest_docs.go b/doc/rest_docs.go index bb0f8ec4..2cca6fd7 100644 --- a/doc/rest_docs.go +++ b/doc/rest_docs.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/rest_docs_test.go b/doc/rest_docs_test.go index 0c197f8d..1a3ea9dd 100644 --- a/doc/rest_docs_test.go +++ b/doc/rest_docs_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/util.go b/doc/util.go index 499ecdd9..0aaa07a1 100644 --- a/doc/util.go +++ b/doc/util.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/yaml_docs.go b/doc/yaml_docs.go index 12f8cf33..2b26d6ec 100644 --- a/doc/yaml_docs.go +++ b/doc/yaml_docs.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/doc/yaml_docs_test.go b/doc/yaml_docs_test.go index c552077d..1a6fa7c3 100644 --- a/doc/yaml_docs_test.go +++ b/doc/yaml_docs_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/fish_completions.go b/fish_completions.go index 32c4bea0..12ca0d2b 100644 --- a/fish_completions.go +++ b/fish_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/fish_completions_test.go b/fish_completions_test.go index 935f5540..10d97d85 100644 --- a/fish_completions_test.go +++ b/fish_completions_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/flag_groups.go b/flag_groups.go index 9c377aaf..b35fde15 100644 --- a/flag_groups.go +++ b/flag_groups.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/flag_groups_test.go b/flag_groups_test.go index b4b65ac0..bf988d73 100644 --- a/flag_groups_test.go +++ b/flag_groups_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/powershell_completions.go b/powershell_completions.go index 927733b3..177d2755 100644 --- a/powershell_completions.go +++ b/powershell_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/powershell_completions_test.go b/powershell_completions_test.go index 3d3d1033..b4092134 100644 --- a/powershell_completions_test.go +++ b/powershell_completions_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/shell_completions.go b/shell_completions.go index 126e83c3..b035742d 100644 --- a/shell_completions.go +++ b/shell_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/zsh_completions.go b/zsh_completions.go index 29bcb570..9afdf3c0 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/zsh_completions_test.go b/zsh_completions_test.go index 258b1f71..fe898b3d 100644 --- a/zsh_completions_test.go +++ b/zsh_completions_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The Cobra Authors +// Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From b197a24504fb0c99318ad0f07cb899e6074d315a Mon Sep 17 00:00:00 2001 From: Florent Poinsard <35779988+frouioui@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:28:50 +0200 Subject: [PATCH 06/26] Update projects_using_cobra.md (#1932) Signed-off-by: Florent Poinsard --- projects_using_cobra.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects_using_cobra.md b/projects_using_cobra.md index 4a61f107..8a291eb2 100644 --- a/projects_using_cobra.md +++ b/projects_using_cobra.md @@ -1,6 +1,7 @@ ## Projects using Cobra - [Allero](https://github.com/allero-io/allero) +- [Arewefastyet](https://benchmark.vitess.io) - [Arduino CLI](https://github.com/arduino/arduino-cli) - [Bleve](https://blevesearch.com/) - [Cilium](https://cilium.io/) @@ -57,6 +58,7 @@ - [Tendermint](https://github.com/tendermint/tendermint) - [Twitch CLI](https://github.com/twitchdev/twitch-cli) - [UpCloud CLI (`upctl`)](https://github.com/UpCloudLtd/upcloud-cli) +- [Vitess](https://vitess.io) - VMware's [Tanzu Community Edition](https://github.com/vmware-tanzu/community-edition) & [Tanzu Framework](https://github.com/vmware-tanzu/tanzu-framework) - [Werf](https://werf.io/) - [ZITADEL](https://github.com/zitadel/zitadel) From c8a20a16bafa2dc85d304b59b2e677c7f75a73a6 Mon Sep 17 00:00:00 2001 From: Luiz Carvalho Date: Thu, 16 Mar 2023 21:03:29 -0400 Subject: [PATCH 07/26] Document suggested layout for subcommands (#1930) Signed-off-by: Luiz Carvalho --- user_guide.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/user_guide.md b/user_guide.md index 00b53d03..85201d84 100644 --- a/user_guide.md +++ b/user_guide.md @@ -188,6 +188,37 @@ var versionCmd = &cobra.Command{ } ``` +### Organizing subcommands + +A command may have subcommands which in turn may have other subcommands. This is achieved by using +`AddCommand`. In some cases, especially in larger applications, each subcommand may be defined in +its own go package. + +The suggested approach is for the parent command to use `AddCommand` to add its most immediate +subcommands. For example, consider the following directory structure: + +```text +├── cmd +│   ├── root.go +│   └── sub1 +│   ├── sub1.go +│   └── sub2 +│   ├── leafA.go +│   ├── leafB.go +│   └── sub2.go +└── main.go +``` + +In this case: + +* The `init` function of `root.go` adds the command defined in `sub1.go` to the root command. +* The `init` function of `sub1.go` adds the command defined in `sub2.go` to the sub1 command. +* The `init` function of `sub2.go` adds the commands defined in `leafA.go` and `leafB.go` to the + sub2 command. + +This approach ensures the subcommands are always included at compile time while avoiding cyclic +references. + ### Returning and handling errors If you wish to return an error to the caller of a command, `RunE` can be used. From 45360a55ccccf909b35f4290242bfb8a6bfbef66 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Wed, 22 Mar 2023 18:41:05 -0400 Subject: [PATCH 08/26] Allow sourcing zsh completion script (#1917) Although it is not the recommended approach, sourcing a completion script is the simplest way to get people to try using shell completion. Not allowing it for zsh has turned out to complicate shell completion adoption. Further, many tools modify the zsh script to allow sourcing. This commit allows sourcing of the zsh completion script. Signed-off-by: Marc Khouzam --- completions.go | 2 +- zsh_completions.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/completions.go b/completions.go index 8a42f889..ee38c4d0 100644 --- a/completions.go +++ b/completions.go @@ -734,7 +734,7 @@ to enable it. You can execute the following once: To load completions in your current shell session: - source <(%[1]s completion zsh); compdef _%[1]s %[1]s + source <(%[1]s completion zsh) To load completions for every new session, execute once: diff --git a/zsh_completions.go b/zsh_completions.go index 9afdf3c0..1856e4c7 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -90,6 +90,7 @@ func genZshComp(buf io.StringWriter, name string, includeDesc bool) { compCmd = ShellCompNoDescRequestCmd } WriteStringAndCheck(buf, fmt.Sprintf(`#compdef %[1]s +compdef _%[1]s %[1]s # zsh completion for %-36[1]s -*- shell-script -*- From 4dd4b25de38418174a6e859e8a32eaccca32dccc Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Wed, 22 Mar 2023 18:41:51 -0400 Subject: [PATCH 09/26] Update main image to better handle dark background (#1883) Fixes #1880 Signed-off-by: Marc Khouzam Co-authored-by: Deleplace --- README.md | 2 +- assets/CobraMain.png | Bin 0 -> 73479 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 assets/CobraMain.png diff --git a/README.md b/README.md index 063f31a4..592c0b8a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![cobra logo](https://cloud.githubusercontent.com/assets/173412/10886352/ad566232-814f-11e5-9cd0-aa101788c117.png) +![cobra logo](assets/CobraMain.png) Cobra is a library for creating powerful modern CLI applications. diff --git a/assets/CobraMain.png b/assets/CobraMain.png new file mode 100644 index 0000000000000000000000000000000000000000..6f1b68a756ad3f615cafdffcd195a75bb2aaa489 GIT binary patch literal 73479 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 kJ0K+vg#82rgLwbg`aUHx3vIVCg!0N Date: Sat, 8 Apr 2023 06:47:33 +0200 Subject: [PATCH 10/26] Fix typo in fish completions (#1945) --- fish_completions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fish_completions.go b/fish_completions.go index 12ca0d2b..12d61b69 100644 --- a/fish_completions.go +++ b/fish_completions.go @@ -113,7 +113,7 @@ function __%[1]s_clear_perform_completion_once_result __%[1]s_debug "" __%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable ==========" set --erase __%[1]s_perform_completion_once_result - __%[1]s_debug "Succesfully erased the variable __%[1]s_perform_completion_once_result" + __%[1]s_debug "Successfully erased the variable __%[1]s_perform_completion_once_result" end function __%[1]s_requires_order_preservation From 0e3a0bfe91a52670ba78ec020f3657d48954a4f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 02:39:30 +0000 Subject: [PATCH 11/26] build(deps): bump golangci/golangci-lint-action from 3.4.0 to 3.5.0 (#1971) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a0c7bb8..2fd68aa8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v3 - - uses: golangci/golangci-lint-action@v3.4.0 + - uses: golangci/golangci-lint-action@v3.5.0 with: version: latest args: --verbose From 2246fa82e91dda4486b9466fab691b41b516f747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taavi=20V=C3=A4=C3=A4n=C3=A4nen?= Date: Tue, 13 Jun 2023 18:12:49 +0300 Subject: [PATCH 12/26] Fix grammar: 'allows to' (#1978) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use in generated bash completion files is getting flagged by Lintian (the Debian package linting tool). Signed-off-by: Taavi Väänänen --- bash_completions.go | 2 +- bash_completionsV2.go | 2 +- cobra.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bash_completions.go b/bash_completions.go index 10c78847..8a531518 100644 --- a/bash_completions.go +++ b/bash_completions.go @@ -85,7 +85,7 @@ __%[1]s_handle_go_custom_completion() local out requestComp lastParam lastChar comp directive args # Prepare the command to request completions for the program. - # Calling ${words[0]} instead of directly %[1]s allows to handle aliases + # Calling ${words[0]} instead of directly %[1]s allows handling aliases args=("${words[@]:1}") # Disable ActiveHelp which is not supported for bash completion v1 requestComp="%[8]s=0 ${words[0]} %[2]s ${args[*]}" diff --git a/bash_completionsV2.go b/bash_completionsV2.go index 19b09560..1cce5c32 100644 --- a/bash_completionsV2.go +++ b/bash_completionsV2.go @@ -57,7 +57,7 @@ __%[1]s_get_completion_results() { local requestComp lastParam lastChar args # Prepare the command to request completions for the program. - # Calling ${words[0]} instead of directly %[1]s allows to handle aliases + # Calling ${words[0]} instead of directly %[1]s allows handling aliases args=("${words[@]:1}") requestComp="${words[0]} %[2]s ${args[*]}" diff --git a/cobra.go b/cobra.go index b07b44a0..f23f5092 100644 --- a/cobra.go +++ b/cobra.go @@ -48,7 +48,7 @@ const ( defaultCaseInsensitive = false ) -// EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing +// EnablePrefixMatching allows setting automatic prefix matching. Automatic prefix matching can be a dangerous thing // to automatically enable in CLI tools. // Set this to true to enable it. var EnablePrefixMatching = defaultPrefixMatching From 988bd76139210bc1d686c7112b4eb5e784365f1f Mon Sep 17 00:00:00 2001 From: Branch Vincent Date: Fri, 16 Jun 2023 07:25:30 -0700 Subject: [PATCH 13/26] test: make fish_completions_test more robust (#1980) Use temporary files instead of assuming the current directory is writable. Also, if creating a temporary file still returns an error, prevent the test from failing silently by replacing `log.Fatal` with `t.Fatal`. --- fish_completions_test.go | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/fish_completions_test.go b/fish_completions_test.go index 10d97d85..ce2a531d 100644 --- a/fish_completions_test.go +++ b/fish_completions_test.go @@ -16,9 +16,10 @@ package cobra import ( "bytes" + "errors" "fmt" - "log" "os" + "path/filepath" "testing" ) @@ -98,12 +99,12 @@ func TestFishCompletionNoActiveHelp(t *testing.T) { } func TestGenFishCompletionFile(t *testing.T) { - err := os.Mkdir("./tmp", 0755) + tmpFile, err := os.CreateTemp("", "cobra-test") if err != nil { - log.Fatal(err.Error()) + t.Fatal(err.Error()) } - defer os.RemoveAll("./tmp") + defer os.Remove(tmpFile.Name()) rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} child := &Command{ @@ -113,18 +114,18 @@ func TestGenFishCompletionFile(t *testing.T) { } rootCmd.AddCommand(child) - assertNoErr(t, rootCmd.GenFishCompletionFile("./tmp/test", false)) + assertNoErr(t, rootCmd.GenFishCompletionFile(tmpFile.Name(), false)) } func TestFailGenFishCompletionFile(t *testing.T) { - err := os.Mkdir("./tmp", 0755) + tmpDir, err := os.MkdirTemp("", "cobra-test") if err != nil { - log.Fatal(err.Error()) + t.Fatal(err.Error()) } - defer os.RemoveAll("./tmp") + defer os.RemoveAll(tmpDir) - f, _ := os.OpenFile("./tmp/test", os.O_CREATE, 0400) + f, _ := os.OpenFile(filepath.Join(tmpDir, "test"), os.O_CREATE, 0400) defer f.Close() rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} @@ -135,18 +136,8 @@ func TestFailGenFishCompletionFile(t *testing.T) { } rootCmd.AddCommand(child) - got := rootCmd.GenFishCompletionFile("./tmp/test", false) - if got == nil { - t.Error("should raise permission denied error") - } - - if os.Getenv("MSYSTEM") == "MINGW64" { - if got.Error() != "open ./tmp/test: Access is denied." { - t.Errorf("got: %s, want: %s", got.Error(), "open ./tmp/test: Access is denied.") - } - } else { - if got.Error() != "open ./tmp/test: permission denied" { - t.Errorf("got: %s, want: %s", got.Error(), "open ./tmp/test: permission denied") - } + got := rootCmd.GenFishCompletionFile(f.Name(), false) + if !errors.Is(got, os.ErrPermission) { + t.Errorf("got: %s, want: %s", got.Error(), os.ErrPermission.Error()) } } From fdee73b4a0d0be3e233b968ab038866b7cd99772 Mon Sep 17 00:00:00 2001 From: Paul Holzinger <45212748+Luap99@users.noreply.github.com> Date: Mon, 19 Jun 2023 18:16:18 +0200 Subject: [PATCH 14/26] powershell: escape variable with curly brackets (#1960) This fixes an issue with program names that include a dot, in our case `podman.exe`. This was caused by the change in commit 6ba7ebbc. Fixes #1853 Signed-off-by: Paul Holzinger --- powershell_completions.go | 6 +++--- powershell_completions_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/powershell_completions.go b/powershell_completions.go index 177d2755..55195193 100644 --- a/powershell_completions.go +++ b/powershell_completions.go @@ -47,7 +47,7 @@ filter __%[1]s_escapeStringWithSpecialChars { `+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+` } -[scriptblock]$__%[2]sCompleterBlock = { +[scriptblock]${__%[2]sCompleterBlock} = { param( $WordToComplete, $CommandAst, @@ -122,7 +122,7 @@ filter __%[1]s_escapeStringWithSpecialChars { __%[1]s_debug "Calling $RequestComp" # First disable ActiveHelp which is not supported for Powershell - $env:%[10]s=0 + ${env:%[10]s}=0 #call the command store the output in $out and redirect stderr and stdout to null # $Out is an array contains each line per element @@ -279,7 +279,7 @@ filter __%[1]s_escapeStringWithSpecialChars { } } -Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock $__%[2]sCompleterBlock +Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock ${__%[2]sCompleterBlock} `, name, nameForVar, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) diff --git a/powershell_completions_test.go b/powershell_completions_test.go index b4092134..603b50c9 100644 --- a/powershell_completions_test.go +++ b/powershell_completions_test.go @@ -29,5 +29,5 @@ func TestPwshCompletionNoActiveHelp(t *testing.T) { // check that active help is being disabled activeHelpVar := activeHelpEnvVar(c.Name()) - check(t, output, fmt.Sprintf("%s=0", activeHelpVar)) + check(t, output, fmt.Sprintf("${env:%s}=0", activeHelpVar)) } From cbe4865373c671ff257810b08e9d855aca922cee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 20:46:35 +0000 Subject: [PATCH 15/26] build(deps): bump golangci/golangci-lint-action from 3.5.0 to 3.6.0 (#1976) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2fd68aa8..498e3bec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v3 - - uses: golangci/golangci-lint-action@v3.5.0 + - uses: golangci/golangci-lint-action@v3.6.0 with: version: latest args: --verbose From dcb405a9399ed307eaa0e50f5cb1c290c8979dd4 Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral <38422348+umarcor@users.noreply.github.com> Date: Tue, 20 Jun 2023 22:15:36 +0100 Subject: [PATCH 16/26] Move documentation sources to site/content (#1428) --- README.md | 8 ++++---- fish_completions.md | 4 ---- powershell_completions.md | 3 --- active_help.md => site/content/active_help.md | 0 .../content/completions/_index.md | 12 ++++++------ .../content/completions/bash.md | 2 +- site/content/completions/fish.md | 4 ++++ site/content/completions/powershell.md | 3 +++ .../content/completions/zsh.md | 2 +- doc/README.md => site/content/docgen/_index.md | 8 ++++---- doc/man_docs.md => site/content/docgen/man.md | 0 doc/md_docs.md => site/content/docgen/md.md | 0 doc/rest_docs.md => site/content/docgen/rest.md | 0 doc/yaml_docs.md => site/content/docgen/yaml.md | 0 .../content/projects_using_cobra.md | 0 user_guide.md => site/content/user_guide.md | 15 ++++++++++----- 16 files changed, 33 insertions(+), 28 deletions(-) delete mode 100644 fish_completions.md delete mode 100644 powershell_completions.md rename active_help.md => site/content/active_help.md (100%) rename shell_completions.md => site/content/completions/_index.md (98%) rename bash_completions.md => site/content/completions/bash.md (98%) create mode 100644 site/content/completions/fish.md create mode 100644 site/content/completions/powershell.md rename zsh_completions.md => site/content/completions/zsh.md (98%) rename doc/README.md => site/content/docgen/_index.md (73%) rename doc/man_docs.md => site/content/docgen/man.md (100%) rename doc/md_docs.md => site/content/docgen/md.md (100%) rename doc/rest_docs.md => site/content/docgen/rest.md (100%) rename doc/yaml_docs.md => site/content/docgen/yaml.md (100%) rename projects_using_cobra.md => site/content/projects_using_cobra.md (100%) rename user_guide.md => site/content/user_guide.md (97%) diff --git a/README.md b/README.md index 592c0b8a..6444f4b7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Cobra is a library for creating powerful modern CLI applications. Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/), [Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to -name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra. +name a few. [This list](site/content/projects_using_cobra.md) contains a more extensive list of projects using Cobra. [![](https://img.shields.io/github/actions/workflow/status/spf13/cobra/test.yml?branch=main&longCache=true&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest) [![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/spf13/cobra) @@ -80,7 +80,7 @@ which maintains the same interface while adding POSIX compliance. # Installing Using Cobra is easy. First, use `go get` to install the latest version -of the library. +of the library. ``` go get -u github.com/spf13/cobra@latest @@ -105,8 +105,8 @@ 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 library, please read the [The Cobra User Guide](user_guide.md). +For complete details on using the Cobra library, please read the [The Cobra User Guide](site/content/user_guide.md). # License -Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt) +Cobra is released under the Apache 2.0 license. See [LICENSE.txt](LICENSE.txt) diff --git a/fish_completions.md b/fish_completions.md deleted file mode 100644 index 19b2ed12..00000000 --- a/fish_completions.md +++ /dev/null @@ -1,4 +0,0 @@ -## Generating Fish Completions For Your cobra.Command - -Please refer to [Shell Completions](shell_completions.md) for details. - diff --git a/powershell_completions.md b/powershell_completions.md deleted file mode 100644 index c449f1e5..00000000 --- a/powershell_completions.md +++ /dev/null @@ -1,3 +0,0 @@ -# Generating PowerShell Completions For Your Own cobra.Command - -Please refer to [Shell Completions](shell_completions.md#powershell-completions) for details. diff --git a/active_help.md b/site/content/active_help.md similarity index 100% rename from active_help.md rename to site/content/active_help.md diff --git a/shell_completions.md b/site/content/completions/_index.md similarity index 98% rename from shell_completions.md rename to site/content/completions/_index.md index 065c0621..4efad290 100644 --- a/shell_completions.md +++ b/site/content/completions/_index.md @@ -416,7 +416,7 @@ completion firstcommand secondcommand ### Bash legacy dynamic completions For backward compatibility, Cobra still supports its bash legacy dynamic completion solution. -Please refer to [Bash Completions](bash_completions.md) for details. +Please refer to [Bash Completions](bash.md) for details. ### Bash completion V2 @@ -425,13 +425,13 @@ Cobra provides two versions for bash completion. The original bash completion ( A new V2 bash completion version is also available. This version can be used by calling `GenBashCompletionV2()` or `GenBashCompletionFileV2()`. The V2 version does **not** support the legacy dynamic completion -(see [Bash Completions](bash_completions.md)) but instead works only with the Go dynamic completion +(see [Bash Completions](bash.md)) but instead works only with the Go dynamic completion solution described in this document. Unless your program already uses the legacy dynamic completion solution, it is recommended that you use the bash completion V2 solution which provides the following extra features: - Supports completion descriptions (like the other shells) - Small completion script of less than 300 lines (v1 generates scripts of thousands of lines; `kubectl` for example has a bash v1 completion script of over 13K lines) -- Streamlined user experience thanks to a completion behavior aligned with the other shells +- Streamlined user experience thanks to a completion behavior aligned with the other shells `Bash` completion V2 supports descriptions for completions. When calling `GenBashCompletionV2()` or `GenBashCompletionFileV2()` you must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra @@ -448,7 +448,7 @@ show (show information of a chart) $ helm s[tab][tab] search show status ``` -**Note**: Cobra's default `completion` command uses bash completion V2. If for some reason you need to use bash completion V1, you will need to implement your own `completion` command. +**Note**: Cobra's default `completion` command uses bash completion V2. If for some reason you need to use bash completion V1, you will need to implement your own `completion` command. ## Zsh completions Cobra supports native zsh completion generated from the root `cobra.Command`. @@ -482,7 +482,7 @@ search show status ### Zsh completions standardization Cobra 1.1 standardized its zsh completion support to align it with its other shell completions. Although the API was kept backward-compatible, some small changes in behavior were introduced. -Please refer to [Zsh Completions](zsh_completions.md) for details. +Please refer to [Zsh Completions](zsh.md) for details. ## fish completions @@ -535,7 +535,7 @@ search (search for a keyword in charts) show (show information of a chart) s # With descriptions and Mode 'MenuComplete' The description of the current selected value will be displayed below the suggestions. $ helm s[tab] -search show status +search show status search for a keyword in charts diff --git a/bash_completions.md b/site/content/completions/bash.md similarity index 98% rename from bash_completions.md rename to site/content/completions/bash.md index 52919b2f..6838a3a6 100644 --- a/bash_completions.md +++ b/site/content/completions/bash.md @@ -1,6 +1,6 @@ # Generating Bash Completions For Your cobra.Command -Please refer to [Shell Completions](shell_completions.md) for details. +Please refer to [Shell Completions](_index.md) for details. ## Bash legacy dynamic completions diff --git a/site/content/completions/fish.md b/site/content/completions/fish.md new file mode 100644 index 00000000..5253f7d4 --- /dev/null +++ b/site/content/completions/fish.md @@ -0,0 +1,4 @@ +## Generating Fish Completions For Your cobra.Command + +Please refer to [Shell Completions](_index.md) for details. + diff --git a/site/content/completions/powershell.md b/site/content/completions/powershell.md new file mode 100644 index 00000000..024b119a --- /dev/null +++ b/site/content/completions/powershell.md @@ -0,0 +1,3 @@ +# Generating PowerShell Completions For Your Own cobra.Command + +Please refer to [Shell Completions](_index.md#powershell-completions) for details. diff --git a/zsh_completions.md b/site/content/completions/zsh.md similarity index 98% rename from zsh_completions.md rename to site/content/completions/zsh.md index 7cff6178..3e27208b 100644 --- a/zsh_completions.md +++ b/site/content/completions/zsh.md @@ -1,6 +1,6 @@ ## Generating Zsh Completion For Your cobra.Command -Please refer to [Shell Completions](shell_completions.md) for details. +Please refer to [Shell Completions](_index.md) for details. ## Zsh completions standardization diff --git a/doc/README.md b/site/content/docgen/_index.md similarity index 73% rename from doc/README.md rename to site/content/docgen/_index.md index 8e07baae..eba2a5fc 100644 --- a/doc/README.md +++ b/site/content/docgen/_index.md @@ -1,9 +1,9 @@ # Documentation generation -- [Man page docs](./man_docs.md) -- [Markdown docs](./md_docs.md) -- [Rest docs](./rest_docs.md) -- [Yaml docs](./yaml_docs.md) +- [Man page docs](man.md) +- [Markdown docs](md.md) +- [Rest docs](rest.md) +- [Yaml docs](yaml.md) ## Options ### `DisableAutoGenTag` diff --git a/doc/man_docs.md b/site/content/docgen/man.md similarity index 100% rename from doc/man_docs.md rename to site/content/docgen/man.md diff --git a/doc/md_docs.md b/site/content/docgen/md.md similarity index 100% rename from doc/md_docs.md rename to site/content/docgen/md.md diff --git a/doc/rest_docs.md b/site/content/docgen/rest.md similarity index 100% rename from doc/rest_docs.md rename to site/content/docgen/rest.md diff --git a/doc/yaml_docs.md b/site/content/docgen/yaml.md similarity index 100% rename from doc/yaml_docs.md rename to site/content/docgen/yaml.md diff --git a/projects_using_cobra.md b/site/content/projects_using_cobra.md similarity index 100% rename from projects_using_cobra.md rename to site/content/projects_using_cobra.md diff --git a/user_guide.md b/site/content/user_guide.md similarity index 97% rename from user_guide.md rename to site/content/user_guide.md index 85201d84..04971431 100644 --- a/user_guide.md +++ b/site/content/user_guide.md @@ -29,8 +29,8 @@ func main() { ## Using the Cobra Generator -Cobra-CLI is its own program that will create your application and add any -commands you want. It's the easiest way to incorporate Cobra into your application. +Cobra-CLI is its own program that will create your application and add any commands you want. +It's the easiest way to incorporate Cobra into your application. For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) @@ -715,12 +715,17 @@ Run 'kubectl help' for usage. ## Generating documentation for your command -Cobra can generate documentation based on subcommands, flags, etc. Read more about it in the [docs generation documentation](doc/README.md). +Cobra can generate documentation based on subcommands, flags, etc. +Read more about it in the [docs generation documentation](docgen/_index.md). ## Generating shell completions -Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible. Read more about it in [Shell Completions](shell_completions.md). +Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. +If you add more information to your commands, these completions can be amazingly powerful and flexible. +Read more about it in [Shell Completions](completions/_index.md). ## Providing Active Help -Cobra makes use of the shell-completion system to define a framework allowing you to provide Active Help to your users. Active Help are messages (hints, warnings, etc) printed as the program is being used. Read more about it in [Active Help](active_help.md). +Cobra makes use of the shell-completion system to define a framework allowing you to provide Active Help to your users. +Active Help are messages (hints, warnings, etc) printed as the program is being used. +Read more about it in [Active Help](active_help.md). From c81c46a015b48d9f79c73cc6f528f90eaabb0c7e Mon Sep 17 00:00:00 2001 From: Martijn Evers <94963229+marevers@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:38:22 +0200 Subject: [PATCH 17/26] Add 'one required flag' group (#1952) --- completions_test.go | 98 ++++++++++++++++++++++++++++++++++++++ flag_groups.go | 68 +++++++++++++++++++++++++- flag_groups_test.go | 63 ++++++++++++++++++++---- site/content/user_guide.md | 11 ++++- 4 files changed, 228 insertions(+), 12 deletions(-) diff --git a/completions_test.go b/completions_test.go index 0588da0f..01724660 100644 --- a/completions_test.go +++ b/completions_test.go @@ -2830,6 +2830,104 @@ func TestCompletionForGroupedFlags(t *testing.T) { } } +func TestCompletionForOneRequiredGroupFlags(t *testing.T) { + getCmd := func() *Command { + rootCmd := &Command{ + Use: "root", + Run: emptyRun, + } + childCmd := &Command{ + Use: "child", + ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { + return []string{"subArg"}, ShellCompDirectiveNoFileComp + }, + Run: emptyRun, + } + rootCmd.AddCommand(childCmd) + + rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1") + rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2") + + childCmd.Flags().Bool("ingroup3", false, "ingroup3") + childCmd.Flags().Bool("nogroup", false, "nogroup") + + // Add flags to a group + childCmd.MarkFlagsOneRequired("ingroup1", "ingroup2", "ingroup3") + + return rootCmd + } + + // Each test case uses a unique command from the function above. + testcases := []struct { + desc string + args []string + expectedOutput string + }{ + { + desc: "flags in group suggested without - prefix", + args: []string{"child", ""}, + expectedOutput: strings.Join([]string{ + "--ingroup1", + "--ingroup2", + "--ingroup3", + "subArg", + ":4", + "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + }, + { + desc: "flags in group suggested with - prefix", + args: []string{"child", "-"}, + expectedOutput: strings.Join([]string{ + "--ingroup1", + "--ingroup2", + "--ingroup3", + ":4", + "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + }, + { + desc: "when any flag in group present, other flags in group not suggested without - prefix", + args: []string{"child", "--ingroup2", "value", ""}, + expectedOutput: strings.Join([]string{ + "subArg", + ":4", + "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + }, + { + desc: "when all flags in group present, flags not suggested without - prefix", + args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""}, + expectedOutput: strings.Join([]string{ + "subArg", + ":4", + "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + }, + { + desc: "group ignored if some flags not applicable", + args: []string{"--ingroup2", "value", ""}, + expectedOutput: strings.Join([]string{ + "child", + "completion", + "help", + ":4", + "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), + }, + } + + for _, tc := range testcases { + t.Run(tc.desc, func(t *testing.T) { + c := getCmd() + args := []string{ShellCompNoDescRequestCmd} + args = append(args, tc.args...) + output, err := executeCommand(c, args...) + switch { + case err == nil && output != tc.expectedOutput: + t.Errorf("expected: %q, got: %q", tc.expectedOutput, output) + case err != nil: + t.Errorf("Unexpected error %q", err) + } + }) + } +} + func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { getCmd := func() *Command { rootCmd := &Command{ diff --git a/flag_groups.go b/flag_groups.go index b35fde15..0671ec5f 100644 --- a/flag_groups.go +++ b/flag_groups.go @@ -24,6 +24,7 @@ import ( const ( requiredAsGroup = "cobra_annotation_required_if_others_set" + oneRequired = "cobra_annotation_one_required" mutuallyExclusive = "cobra_annotation_mutually_exclusive" ) @@ -43,6 +44,22 @@ func (c *Command) MarkFlagsRequiredTogether(flagNames ...string) { } } +// MarkFlagsOneRequired marks the given flags with annotations so that Cobra errors +// if the command is invoked without at least one flag from the given set of flags. +func (c *Command) MarkFlagsOneRequired(flagNames ...string) { + c.mergePersistentFlags() + for _, v := range flagNames { + f := c.Flags().Lookup(v) + if f == nil { + panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a one-required flag group", v)) + } + if err := c.Flags().SetAnnotation(v, oneRequired, append(f.Annotations[oneRequired], strings.Join(flagNames, " "))); err != nil { + // Only errs if the flag isn't found. + panic(err) + } + } +} + // MarkFlagsMutuallyExclusive marks the given flags with annotations so that Cobra errors // if the command is invoked with more than one flag from the given set of flags. func (c *Command) MarkFlagsMutuallyExclusive(flagNames ...string) { @@ -59,7 +76,7 @@ func (c *Command) MarkFlagsMutuallyExclusive(flagNames ...string) { } } -// ValidateFlagGroups validates the mutuallyExclusive/requiredAsGroup logic and returns the +// ValidateFlagGroups validates the mutuallyExclusive/oneRequired/requiredAsGroup logic and returns the // first error encountered. func (c *Command) ValidateFlagGroups() error { if c.DisableFlagParsing { @@ -71,15 +88,20 @@ func (c *Command) ValidateFlagGroups() error { // groupStatus format is the list of flags as a unique ID, // then a map of each flag name and whether it is set or not. groupStatus := map[string]map[string]bool{} + oneRequiredGroupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{} flags.VisitAll(func(pflag *flag.Flag) { processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus) + processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus) processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus) }) if err := validateRequiredFlagGroups(groupStatus); err != nil { return err } + if err := validateOneRequiredFlagGroups(oneRequiredGroupStatus); err != nil { + return err + } if err := validateExclusiveFlagGroups(mutuallyExclusiveGroupStatus); err != nil { return err } @@ -142,6 +164,27 @@ func validateRequiredFlagGroups(data map[string]map[string]bool) error { return nil } +func validateOneRequiredFlagGroups(data map[string]map[string]bool) error { + keys := sortedKeys(data) + for _, flagList := range keys { + flagnameAndStatus := data[flagList] + var set []string + for flagname, isSet := range flagnameAndStatus { + if isSet { + set = append(set, flagname) + } + } + if len(set) >= 1 { + continue + } + + // Sort values, so they can be tested/scripted against consistently. + sort.Strings(set) + return fmt.Errorf("at least one of the flags in the group [%v] is required", flagList) + } + return nil +} + func validateExclusiveFlagGroups(data map[string]map[string]bool) error { keys := sortedKeys(data) for _, flagList := range keys { @@ -176,6 +219,7 @@ func sortedKeys(m map[string]map[string]bool) []string { // enforceFlagGroupsForCompletion will do the following: // - when a flag in a group is present, other flags in the group will be marked required +// - when none of the flags in a one-required group are present, all flags in the group will be marked required // - when a flag in a mutually exclusive group is present, other flags in the group will be marked as hidden // This allows the standard completion logic to behave appropriately for flag groups func (c *Command) enforceFlagGroupsForCompletion() { @@ -185,9 +229,11 @@ func (c *Command) enforceFlagGroupsForCompletion() { flags := c.Flags() groupStatus := map[string]map[string]bool{} + oneRequiredGroupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{} c.Flags().VisitAll(func(pflag *flag.Flag) { processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus) + processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus) processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus) }) @@ -204,6 +250,26 @@ func (c *Command) enforceFlagGroupsForCompletion() { } } + // If none of the flags of a one-required group are present, we make all the flags + // of that group required so that the shell completion suggests them automatically + for flagList, flagnameAndStatus := range oneRequiredGroupStatus { + set := 0 + + for _, isSet := range flagnameAndStatus { + if isSet { + set++ + } + } + + // None of the flags of the group are set, mark all flags in the group + // as required + if set == 0 { + for _, fName := range strings.Split(flagList, " ") { + _ = c.MarkFlagRequired(fName) + } + } + } + // If a flag that is mutually exclusive to others is present, we hide the other // flags of that group so the shell completion does not suggest them for flagList, flagnameAndStatus := range mutuallyExclusiveGroupStatus { diff --git a/flag_groups_test.go b/flag_groups_test.go index bf988d73..cffa8552 100644 --- a/flag_groups_test.go +++ b/flag_groups_test.go @@ -43,13 +43,15 @@ func TestValidateFlagGroups(t *testing.T) { // Each test case uses a unique command from the function above. testcases := []struct { - desc string - flagGroupsRequired []string - flagGroupsExclusive []string - subCmdFlagGroupsRequired []string - subCmdFlagGroupsExclusive []string - args []string - expectErr string + desc string + flagGroupsRequired []string + flagGroupsOneRequired []string + flagGroupsExclusive []string + subCmdFlagGroupsRequired []string + subCmdFlagGroupsOneRequired []string + subCmdFlagGroupsExclusive []string + args []string + expectErr string }{ { desc: "No flags no problem", @@ -62,6 +64,11 @@ func TestValidateFlagGroups(t *testing.T) { flagGroupsRequired: []string{"a b c"}, args: []string{"--a=foo"}, expectErr: "if any flags in the group [a b c] are set they must all be set; missing [b c]", + }, { + desc: "One-required flag group not satisfied", + flagGroupsOneRequired: []string{"a b"}, + args: []string{"--c=foo"}, + expectErr: "at least one of the flags in the group [a b] is required", }, { desc: "Exclusive flag group not satisfied", flagGroupsExclusive: []string{"a b c"}, @@ -72,6 +79,11 @@ func TestValidateFlagGroups(t *testing.T) { flagGroupsRequired: []string{"a b c", "a d"}, args: []string{"--c=foo", "--d=foo"}, expectErr: `if any flags in the group [a b c] are set they must all be set; missing [a b]`, + }, { + desc: "Multiple one-required flag group not satisfied returns first error", + flagGroupsOneRequired: []string{"a b", "d e"}, + args: []string{"--c=foo", "--f=foo"}, + expectErr: `at least one of the flags in the group [a b] is required`, }, { desc: "Multiple exclusive flag group not satisfied returns first error", flagGroupsExclusive: []string{"a b c", "a d"}, @@ -82,32 +94,57 @@ func TestValidateFlagGroups(t *testing.T) { flagGroupsRequired: []string{"a d", "a b", "a c"}, args: []string{"--a=foo"}, expectErr: `if any flags in the group [a b] are set they must all be set; missing [b]`, + }, { + desc: "Validation of one-required groups occurs on groups in sorted order", + flagGroupsOneRequired: []string{"d e", "a b", "f g"}, + args: []string{"--c=foo"}, + expectErr: `at least one of the flags in the group [a b] is required`, }, { desc: "Validation of exclusive groups occurs on groups in sorted order", flagGroupsExclusive: []string{"a d", "a b", "a c"}, args: []string{"--a=foo", "--b=foo", "--c=foo"}, expectErr: `if any flags in the group [a b] are set none of the others can be; [a b] were all set`, }, { - desc: "Persistent flags utilize both features and can fail required groups", + desc: "Persistent flags utilize required and exclusive groups and can fail required groups", flagGroupsRequired: []string{"a e", "e f"}, flagGroupsExclusive: []string{"f g"}, args: []string{"--a=foo", "--f=foo", "--g=foo"}, expectErr: `if any flags in the group [a e] are set they must all be set; missing [e]`, }, { - desc: "Persistent flags utilize both features and can fail mutually exclusive groups", + desc: "Persistent flags utilize one-required and exclusive groups and can fail one-required groups", + flagGroupsOneRequired: []string{"a b", "e f"}, + flagGroupsExclusive: []string{"e f"}, + args: []string{"--e=foo"}, + expectErr: `at least one of the flags in the group [a b] is required`, + }, { + desc: "Persistent flags utilize required and exclusive groups and can fail mutually exclusive groups", flagGroupsRequired: []string{"a e", "e f"}, flagGroupsExclusive: []string{"f g"}, args: []string{"--a=foo", "--e=foo", "--f=foo", "--g=foo"}, expectErr: `if any flags in the group [f g] are set none of the others can be; [f g] were all set`, }, { - desc: "Persistent flags utilize both features and can pass", + desc: "Persistent flags utilize required and exclusive groups and can pass", flagGroupsRequired: []string{"a e", "e f"}, flagGroupsExclusive: []string{"f g"}, args: []string{"--a=foo", "--e=foo", "--f=foo"}, + }, { + desc: "Persistent flags utilize one-required and exclusive groups and can pass", + flagGroupsOneRequired: []string{"a e", "e f"}, + flagGroupsExclusive: []string{"f g"}, + args: []string{"--a=foo", "--e=foo", "--f=foo"}, }, { desc: "Subcmds can use required groups using inherited flags", subCmdFlagGroupsRequired: []string{"e subonly"}, args: []string{"subcmd", "--e=foo", "--subonly=foo"}, + }, { + desc: "Subcmds can use one-required groups using inherited flags", + subCmdFlagGroupsOneRequired: []string{"e subonly"}, + args: []string{"subcmd", "--e=foo", "--subonly=foo"}, + }, { + desc: "Subcmds can use one-required groups using inherited flags and fail one-required groups", + subCmdFlagGroupsOneRequired: []string{"e subonly"}, + args: []string{"subcmd"}, + expectErr: "at least one of the flags in the group [e subonly] is required", }, { desc: "Subcmds can use exclusive groups using inherited flags", subCmdFlagGroupsExclusive: []string{"e subonly"}, @@ -130,12 +167,18 @@ func TestValidateFlagGroups(t *testing.T) { for _, flagGroup := range tc.flagGroupsRequired { c.MarkFlagsRequiredTogether(strings.Split(flagGroup, " ")...) } + for _, flagGroup := range tc.flagGroupsOneRequired { + c.MarkFlagsOneRequired(strings.Split(flagGroup, " ")...) + } for _, flagGroup := range tc.flagGroupsExclusive { c.MarkFlagsMutuallyExclusive(strings.Split(flagGroup, " ")...) } for _, flagGroup := range tc.subCmdFlagGroupsRequired { sub.MarkFlagsRequiredTogether(strings.Split(flagGroup, " ")...) } + for _, flagGroup := range tc.subCmdFlagGroupsOneRequired { + sub.MarkFlagsOneRequired(strings.Split(flagGroup, " ")...) + } for _, flagGroup := range tc.subCmdFlagGroupsExclusive { sub.MarkFlagsMutuallyExclusive(strings.Split(flagGroup, " ")...) } diff --git a/site/content/user_guide.md b/site/content/user_guide.md index 04971431..56e8e44a 100644 --- a/site/content/user_guide.md +++ b/site/content/user_guide.md @@ -349,7 +349,16 @@ rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML") rootCmd.MarkFlagsMutuallyExclusive("json", "yaml") ``` -In both of these cases: +If you want to require at least one flag from a group to be present, you can use `MarkFlagsOneRequired`. +This can be combined with `MarkFlagsMutuallyExclusive` to enforce exactly one flag from a given group: +```go +rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON") +rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML") +rootCmd.MarkFlagsOneRequired("json", "yaml") +rootCmd.MarkFlagsMutuallyExclusive("json", "yaml") +``` + +In these cases: - both local and persistent flags can be used - **NOTE:** the group is only enforced on commands where every flag is defined - a flag may appear in multiple groups From 66b215ba186e6e28ccab74b9022149cc2236d863 Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral <38422348+umarcor@users.noreply.github.com> Date: Tue, 18 Jul 2023 15:51:36 +0200 Subject: [PATCH 18/26] golangci: enable 'unused' and disable deprecated replaced by it (#1983) --- .golangci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2578d94b..a618ec24 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,7 +19,7 @@ linters: disable-all: true enable: #- bodyclose - - deadcode + # - deadcode ! deprecated since v1.49.0; replaced by 'unused' #- depguard #- dogsled #- dupl @@ -51,12 +51,12 @@ linters: #- rowserrcheck #- scopelint #- staticcheck - - structcheck + #- structcheck ! deprecated since v1.49.0; replaced by 'unused' #- stylecheck #- typecheck - unconvert #- unparam - #- unused - - varcheck + - unused + # - varcheck ! deprecated since v1.49.0; replaced by 'unused' #- whitespace fast: false From 60d056d1574d04c859638354bc9cacab70bab296 Mon Sep 17 00:00:00 2001 From: gocurr Date: Thu, 20 Jul 2023 23:27:44 +0800 Subject: [PATCH 19/26] doc: fix typo, Deperecated -> Deprecated (#2000) --- doc/man_docs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go index c111d455..a4435e6e 100644 --- a/doc/man_docs_test.go +++ b/doc/man_docs_test.go @@ -150,7 +150,7 @@ func TestGenManSeeAlso(t *testing.T) { } } -func TestManPrintFlagsHidesShortDeperecated(t *testing.T) { +func TestManPrintFlagsHidesShortDeprecated(t *testing.T) { c := &cobra.Command{} c.Flags().StringP("foo", "f", "default", "Foo flag") assertNoErr(t, c.Flags().MarkShorthandDeprecated("foo", "don't use it no more")) From fd865a44e3c48afeb6a6dbddadb8a5519173e029 Mon Sep 17 00:00:00 2001 From: Jun Nishimura Date: Sun, 23 Jul 2023 20:31:55 +0900 Subject: [PATCH 20/26] minor corrections to unit tests (#2003) --- command_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command_test.go b/command_test.go index 0212f5ae..b0f5e860 100644 --- a/command_test.go +++ b/command_test.go @@ -438,7 +438,7 @@ func TestFlagLong(t *testing.T) { output, err := executeCommand(c, "--intf=7", "--sf=abc", "one", "--", "two") if output != "" { - t.Errorf("Unexpected output: %v", err) + t.Errorf("Unexpected output: %v", output) } if err != nil { t.Errorf("Unexpected error: %v", err) @@ -475,7 +475,7 @@ func TestFlagShort(t *testing.T) { output, err := executeCommand(c, "-i", "7", "-sabc", "one", "two") if output != "" { - t.Errorf("Unexpected output: %v", err) + t.Errorf("Unexpected output: %v", output) } if err != nil { t.Errorf("Unexpected error: %v", err) @@ -504,7 +504,7 @@ func TestChildFlag(t *testing.T) { output, err := executeCommand(rootCmd, "child", "-i7") if output != "" { - t.Errorf("Unexpected output: %v", err) + t.Errorf("Unexpected output: %v", output) } if err != nil { t.Errorf("Unexpected error: %v", err) From 4955da7c11ea14678cbca3a4016bd4b0b1c51f39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:53:34 +0000 Subject: [PATCH 21/26] build(deps): bump golangci/golangci-lint-action from 3.6.0 to 3.7.0 (#2021) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 498e3bec..3a365c85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v3 - - uses: golangci/golangci-lint-action@v3.6.0 + - uses: golangci/golangci-lint-action@v3.7.0 with: version: latest args: --verbose From 285460dca6152bb86994fa4a9659c24ca4060e2f Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral <38422348+umarcor@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:04:59 +0200 Subject: [PATCH 22/26] command: temporarily disable G602 due to securego/gosec#1005 (#2022) --- command.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 01f7c6f1..e4945ab8 100644 --- a/command.go +++ b/command.go @@ -752,7 +752,9 @@ func (c *Command) findNext(next string) *Command { } if len(matches) == 1 { - return matches[0] + // Temporarily disable gosec G602, which produces a false positive. + // See https://github.com/securego/gosec/issues/1005. + return matches[0] // #nosec G602 } return nil From c5dacb3ea4512e6766f86907dfa7014ceb9cb041 Mon Sep 17 00:00:00 2001 From: Nuno Adrego <55922671+nunoadrego@users.noreply.github.com> Date: Fri, 8 Sep 2023 01:30:51 +0100 Subject: [PATCH 23/26] ci: test golang 1.21 (#2024) --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3a365c85..6b4c165d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,7 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: '^1.20' + go-version: '^1.21' check-latest: true cache: true @@ -67,6 +67,7 @@ jobs: - 18 - 19 - 20 + - 21 name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x' runs-on: ${{ matrix.platform }}-latest steps: From 0c72800b8dba637092b57a955ecee75949e79a73 Mon Sep 17 00:00:00 2001 From: Souma <101255979+5ouma@users.noreply.github.com> Date: Sat, 9 Sep 2023 02:29:06 +0900 Subject: [PATCH 24/26] Customizable error message prefix (#2023) --- command.go | 24 ++++++++++++++++++++++-- command_test.go | 33 +++++++++++++++++++++++++++++++++ site/content/user_guide.md | 6 ++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/command.go b/command.go index e4945ab8..6866f7d0 100644 --- a/command.go +++ b/command.go @@ -181,6 +181,9 @@ type Command struct { // versionTemplate is the version template defined by user. versionTemplate string + // errPrefix is the error message prefix defined by user. + errPrefix string + // inReader is a reader defined by the user that replaces stdin inReader io.Reader // outWriter is a writer defined by the user that replaces stdout @@ -346,6 +349,11 @@ func (c *Command) SetVersionTemplate(s string) { c.versionTemplate = s } +// SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix. +func (c *Command) SetErrPrefix(s string) { + c.errPrefix = s +} + // SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands. // The user should not have a cyclic dependency on commands. func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) { @@ -595,6 +603,18 @@ func (c *Command) VersionTemplate() string { ` } +// ErrPrefix return error message prefix for the command +func (c *Command) ErrPrefix() string { + if c.errPrefix != "" { + return c.errPrefix + } + + if c.HasParent() { + return c.parent.ErrPrefix() + } + return "Error:" +} + func hasNoOptDefVal(name string, fs *flag.FlagSet) bool { flag := fs.Lookup(name) if flag == nil { @@ -1050,7 +1070,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { c = cmd } if !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(c.ErrPrefix(), err.Error()) c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath()) } return c, err @@ -1079,7 +1099,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // If root command has SilenceErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(cmd.ErrPrefix(), err.Error()) } // If root command has SilenceUsage flagged, diff --git a/command_test.go b/command_test.go index b0f5e860..4afb7f7b 100644 --- a/command_test.go +++ b/command_test.go @@ -1099,6 +1099,39 @@ func TestShorthandVersionTemplate(t *testing.T) { checkStringContains(t, output, "customized version: 1.0.0") } +func TestRootErrPrefixExecutedOnSubcommand(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + rootCmd.SetErrPrefix("root error prefix:") + rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun}) + + output, err := executeCommand(rootCmd, "sub", "--unknown-flag") + if err == nil { + t.Errorf("Expected error") + } + + checkStringContains(t, output, "root error prefix: unknown flag: --unknown-flag") +} + +func TestRootAndSubErrPrefix(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + subCmd := &Command{Use: "sub", Run: emptyRun} + rootCmd.AddCommand(subCmd) + rootCmd.SetErrPrefix("root error prefix:") + subCmd.SetErrPrefix("sub error prefix:") + + if output, err := executeCommand(rootCmd, "--unknown-root-flag"); err == nil { + t.Errorf("Expected error") + } else { + checkStringContains(t, output, "root error prefix: unknown flag: --unknown-root-flag") + } + + if output, err := executeCommand(rootCmd, "sub", "--unknown-sub-flag"); err == nil { + t.Errorf("Expected error") + } else { + checkStringContains(t, output, "sub error prefix: unknown flag: --unknown-sub-flag") + } +} + func TestVersionFlagExecutedOnSubcommand(t *testing.T) { rootCmd := &Command{Use: "root", Version: "1.0.0"} rootCmd.AddCommand(&Command{Use: "sub", Run: emptyRun}) diff --git a/site/content/user_guide.md b/site/content/user_guide.md index 56e8e44a..93daadf5 100644 --- a/site/content/user_guide.md +++ b/site/content/user_guide.md @@ -596,6 +596,12 @@ Running an application with the '--version' flag will print the version to stdou the version template. The template can be customized using the `cmd.SetVersionTemplate(s string)` function. +## Error Message Prefix + +Cobra prints an error message when receiving a non-nil error value. +The default error message is `Error: `. +The Prefix, `Error:` can be customized using the `cmd.SetErrPrefix(s string)` function. + ## PreRun and PostRun Hooks It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: From bd4d1655f6655f690f45d281fe698dacb9c7d750 Mon Sep 17 00:00:00 2001 From: Alexandru-Claudius Virtopeanu Date: Tue, 26 Sep 2023 03:04:25 +0300 Subject: [PATCH 25/26] feat: add getters for flag completions (#1943) --- completions.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/completions.go b/completions.go index ee38c4d0..55a9f62c 100644 --- a/completions.go +++ b/completions.go @@ -145,6 +145,25 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman return nil } +// GetFlagCompletion returns the completion function for the given flag, if available. +func GetFlagCompletion(flag *pflag.Flag) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) { + flagCompletionMutex.RLock() + defer flagCompletionMutex.RUnlock() + + completionFunc, exists := flagCompletionFunctions[flag] + return completionFunc, exists +} + +// GetFlagCompletionByName returns the completion function for the given flag in the command by name, if available. +func (c *Command) GetFlagCompletionByName(flagName string) (func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective), bool) { + flag := c.Flags().Lookup(flagName) + if flag == nil { + return nil, false + } + + return GetFlagCompletion(flag) +} + // Returns a string listing the different directive enabled in the specified parameter func (d ShellCompDirective) string() string { var directives []string From 95d8a1e45d7719c56dc017e075d3e6099deba85d Mon Sep 17 00:00:00 2001 From: Haoming Meng <41393704+Techming@users.noreply.github.com> Date: Mon, 9 Oct 2023 07:50:40 -0500 Subject: [PATCH 26/26] Add notes to doc on preRun and postRun condition (#2041) --- command.go | 2 ++ site/content/user_guide.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 6866f7d0..909d8585 100644 --- a/command.go +++ b/command.go @@ -115,6 +115,8 @@ type Command struct { // * PostRun() // * PersistentPostRun() // All functions get the same args, the arguments after the command name. + // The *PreRun and *PostRun functions will only be executed if the Run function of the current + // command has been declared. // // PersistentPreRun: children of this command will inherit and execute. PersistentPreRun func(cmd *Command, args []string) diff --git a/site/content/user_guide.md b/site/content/user_guide.md index 93daadf5..55cc252b 100644 --- a/site/content/user_guide.md +++ b/site/content/user_guide.md @@ -604,7 +604,7 @@ The Prefix, `Error:` can be customized using the `cmd.SetErrPrefix(s string)` fu ## PreRun and PostRun Hooks -It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: +It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. The `*PreRun` and `*PostRun` functions will only be executed if the `Run` function of the current command has been declared. These functions are run in the following order: - `PersistentPreRun` - `PreRun`