From 251a4235a6eecb55a0911cbb5dfa2609aa260fd4 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 6 May 2019 21:14:01 -0400 Subject: [PATCH 1/2] fix(completion): --flag=val breaks zsh completion This is a bug I ran into when working on Helm completion. I was surprised that it didn't happen when I was using kubectl, so I investigated and found a PR that fixed this bug in kubectl: https://github.com/kubernetes/kubernetes/pull/48553 I duplicated the code in this commit which: Removes __helm_declare, which is safe to do since `declare -F` is already replaced to `whence -w` by __helm_convert_bash_to_zsh(). The problem was that calling "declare" from inside a function scopes the declaration to that function only. So "declare" should not be called through __helm_declare() but instead directly. To reproduce: 1- setup helm completion in zsh 2- helm --kubeconfig=$HOME/.kube/config statu you will get the error: __helm_handle_flag:27: bad math expression: operand expected at end of string Co-authored-by: Kazuki Suda Signed-off-by: Marc Khouzam --- cmd/helm/completion.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 21d31d155..062451df6 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -126,13 +126,6 @@ __helm_compgen() { __helm_compopt() { true # don't do anything. Not supported by bashcompinit in zsh } -__helm_declare() { - if [ "$1" == "-F" ]; then - whence -w "$@" - else - builtin declare "$@" - fi -} __helm_ltrim_colon_completions() { if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then @@ -210,7 +203,7 @@ __helm_convert_bash_to_zsh() { -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \ -e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \ -e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \ - -e "s/${LWORD}declare${RWORD}/__helm_declare/g" \ + -e "s/${LWORD}declare${RWORD}/builtin declare/g" \ -e "s/\\\$(type${RWORD}/\$(__helm_type/g" \ -e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \ <<'BASH_COMPLETION_EOF' From 8de22efd88060c41aa9a42abd8f3b8709e9309b8 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Sat, 7 Sep 2019 19:09:05 -0400 Subject: [PATCH 2/2] feat(cli): Dynamic completion for global flags Inspired greatly from kubectl code. Completion is provided for: --kube-context - where the available contexts are listed --namespace - where the namespaces of the cluster are listed Signed-off-by: Marc Khouzam --- cmd/helm/root.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/cmd/helm/root.go b/cmd/helm/root.go index cbda8273f..30b452fcb 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -55,6 +55,40 @@ __helm_override_flags() fi done } + +__helm_override_flags_to_kubectl_flags() +{ + # --kubeconfig, -n, --namespace stay the same for kubectl + # --kube-context becomes --context for kubectl + __helm_debug "${FUNCNAME[0]}: flags to convert: $1" + echo "$1" | sed s/kube-context/context/ +} + +__helm_get_contexts() +{ + __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + local template out + template="{{ range .contexts }}{{ .name }} {{ end }}" + if out=$(kubectl config -o template --template="${template}" view 2>/dev/null); then + COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) + fi +} + +__helm_get_namespaces() +{ + __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + local template out + template="{{ range .items }}{{ .metadata.name }} {{ end }}" + + flags=$(__helm_override_flags_to_kubectl_flags "$(__helm_override_flags)") + __helm_debug "${FUNCNAME[0]}: override flags for kubectl are: $flags" + + # Must use eval in case the flags contain a variable such as $HOME + if out=$(eval kubectl get ${flags} -o template --template=\"${template}\" namespace 2>/dev/null); then + COMPREPLY+=( $( compgen -W "${out[*]}" -- "$cur" ) ) + fi +} + __helm_list_releases() { __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" @@ -105,6 +139,15 @@ __helm_custom_func() ` ) +var ( + // Mapping of global flags that can have dynamic completion and the + // completion function to be used. + bashCompletionFlags = map[string]string{ + "namespace": "__helm_get_namespaces", + "kube-context": "__helm_get_contexts", + } +) + var globalUsage = `The Kubernetes package manager Common actions for Helm: @@ -196,6 +239,19 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string newDocsCmd(out), ) + // Add annotation to flags for which we can generate completion choices + for name, completion := range bashCompletionFlags { + if cmd.Flag(name) != nil { + if cmd.Flag(name).Annotations == nil { + cmd.Flag(name).Annotations = map[string][]string{} + } + cmd.Flag(name).Annotations[cobra.BashCompCustom] = append( + cmd.Flag(name).Annotations[cobra.BashCompCustom], + completion, + ) + } + } + // Add *experimental* subcommands registryClient, err := registry.NewClient( registry.ClientOptDebug(settings.Debug),