From ca9791268933a486d7b77407220ffd723f664708 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 25 Jan 2023 14:56:02 +0100 Subject: [PATCH] feat(zsh): support for gain-privileges (automatic sudo) Currently, this is not possible to make completion work when using sudo. Zsh supports the "gain-priveleges" feature for this exact use case. By using the `-p` argument of `_call_program`, if correctly set with zstyles, Zsh automatically tries to invoke sudo or doas if the command itself is invoked with sudo or doas. This makes the completion works in this context: sudo docker rm a It seems to be a good practice to distinguish between the various cases where sudo can be invoked by customizing the tag provided to `_call_program`. Instead of `commands`, we therefore use `docker-X` where X is the subcommand invoked. To optin this feature, a user needs something like this in its configuration file: zstyle ':completion:*:docker/*' gain-privileges yes zstyle ':completion:*:docker-*/*' gain-privileges yes Signed-off-by: Vincent Bernat --- contrib/completion/zsh/_docker | 40 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 15030eb614..d3a66da44a 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -42,6 +42,10 @@ # Short-option stacking can be enabled with: # zstyle ':completion:*:*:docker:*' option-stacking yes # zstyle ':completion:*:*:docker-*:*' option-stacking yes +# Automatic sudo for completion can be enabled with: +# zstyle ':completion:*:docker/*' gain-privileges yes +# zstyle ':completion:*:docker-*/*' gain-privileges yes + __docker_arguments() { if zstyle -t ":completion:${curcontext}:" option-stacking; then print -- -s @@ -58,7 +62,7 @@ __docker_get_containers() { type=$1; shift [[ $kind = (stopped|all) ]] && args=($args -a) - lines=(${(f)${:-"$(_call_program commands docker $docker_options ps --format 'table' --no-trunc $args)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-ps docker $docker_options ps --format 'table' --no-trunc $args)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} @@ -145,7 +149,7 @@ __docker_complete_info_plugins() { emulate -L zsh setopt extendedglob local -a plugins - plugins=(${(ps: :)${(M)${(f)${${"$(_call_program commands docker $docker_options info)"##*$'\n'Plugins:}%%$'\n'^ *}}:# $1: *}## $1: }) + plugins=(${(ps: :)${(M)${(f)${${"$(_call_program -p docker-info docker $docker_options info)"##*$'\n'Plugins:}%%$'\n'^ *}}:# $1: *}## $1: }) _describe -t plugins "$1 plugins" plugins && ret=0 return ret } @@ -154,7 +158,7 @@ __docker_complete_images() { [[ $PREFIX = -* ]] && return 1 integer ret=1 declare -a images - images=(${${${(f)${:-"$(_call_program commands docker $docker_options images)"$'\n'}}[2,-1]}/(#b)([^ ]##) ##([^ ]##) ##([^ ]##)*/${match[3]}:${(r:15:: :::)match[2]} in ${match[1]}}) + images=(${${${(f)${:-"$(_call_program -p docker-image $docker_options images)"$'\n'}}[2,-1]}/(#b)([^ ]##) ##([^ ]##) ##([^ ]##)*/${match[3]}:${(r:15:: :::)match[2]} in ${match[1]}}) _describe -t docker-images "images" images && ret=0 __docker_complete_repositories_with_tags && ret=0 return ret @@ -164,7 +168,7 @@ __docker_complete_repositories() { [[ $PREFIX = -* ]] && return 1 integer ret=1 declare -a repos - repos=(${${${(f)${:-"$(_call_program commands docker $docker_options images)"$'\n'}}%% *}[2,-1]}) + repos=(${${${(f)${:-"$(_call_program -p docker-image $docker_options images)"$'\n'}}%% *}[2,-1]}) repos=(${repos#}) _describe -t docker-repos "repositories" repos && ret=0 return ret @@ -175,7 +179,7 @@ __docker_complete_repositories_with_tags() { integer ret=1 declare -a repos onlyrepos matched declare m - repos=(${${${${(f)${:-"$(_call_program commands docker $docker_options images)"$'\n'}}[2,-1]}/ ##/:::}%% *}) + repos=(${${${${(f)${:-"$(_call_program -p docker-image docker $docker_options images)"$'\n'}}[2,-1]}/ ##/:::}%% *}) repos=(${${repos%:::}#}) # Check if we have a prefix-match for the current prefix. onlyrepos=(${repos%::*}) @@ -211,7 +215,7 @@ __docker_search() { if ( [[ ${(P)+cachename} -eq 0 ]] || _cache_invalid ${cachename#_} ) \ && ! _retrieve_cache ${cachename#_}; then _message "Searching for ${searchterm}..." - result=(${${${(f)${:-"$(_call_program commands docker $docker_options search $searchterm)"$'\n'}}%% *}[2,-1]}) + result=(${${${(f)${:-"$(_call_program -p docker-search docker $docker_options search $searchterm)"$'\n'}}%% *}[2,-1]}) _store_cache ${cachename#_} result fi _wanted dockersearch expl 'available images' compadd -a result @@ -325,7 +329,7 @@ __docker_complete_runtimes() { emulate -L zsh setopt extendedglob local -a runtimes_opts - runtimes_opts=(${(ps: :)${(f)${${"$(_call_program commands docker $docker_options info)"##*$'\n'Runtimes: }%%$'\n'^ *}}}) + runtimes_opts=(${(ps: :)${(f)${${"$(_call_program -p docker-info docker $docker_options info)"##*$'\n'Runtimes: }%%$'\n'^ *}}}) _describe -t runtimes-opts "runtimes options" runtimes_opts && ret=0 } @@ -444,8 +448,8 @@ __docker_complete_events_filter() { setopt extendedglob local -a daemon_opts daemon_opts=( - ${(f)${${"$(_call_program commands docker $docker_options info)"##*$'\n'Name: }%%$'\n'^ *}} - ${${(f)${${"$(_call_program commands docker $docker_options info)"##*$'\n'ID: }%%$'\n'^ *}}//:/\\:} + ${(f)${${"$(_call_program -p docker-info docker $docker_options info)"##*$'\n'Name: }%%$'\n'^ *}} + ${${(f)${${"$(_call_program -p docker-info docker $docker_options info)"##*$'\n'ID: }%%$'\n'^ *}}//:/\\:} ) _describe -t daemon-filter-opts "daemon filter options" daemon_opts && ret=0 ;; @@ -1161,7 +1165,7 @@ __docker_get_networks() { type=$1; shift - lines=(${(f)${:-"$(_call_program commands docker $docker_options network ls)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-network docker $docker_options network ls)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} @@ -1383,7 +1387,7 @@ __docker_nodes() { filter=$1; shift [[ $filter != "none" ]] && args=("-f $filter") - lines=(${(f)${:-"$(_call_program commands docker $docker_options node ls $args)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-node docker $docker_options node ls $args)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} declare -A begin end @@ -1565,7 +1569,7 @@ __docker_plugins() { filter=$1; shift [[ $filter != "none" ]] && args=("-f $filter") - lines=(${(f)${:-"$(_call_program commands docker $docker_options plugin ls $args)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-plugin docker $docker_options plugin ls $args)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} @@ -1713,7 +1717,7 @@ __docker_secrets() { type=$1; shift - lines=(${(f)${:-"$(_call_program commands docker $docker_options secret ls)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-secret docker $docker_options secret ls)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} @@ -1887,7 +1891,7 @@ __docker_services() { type=$1; shift - lines=(${(f)${:-"$(_call_program commands docker $docker_options service ls)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-service docker $docker_options service ls)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} @@ -2173,7 +2177,7 @@ __docker_stacks() { local line s declare -a lines stacks - lines=(${(f)${:-"$(_call_program commands docker $docker_options stack ls)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-stack docker $docker_options stack ls)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} @@ -2456,7 +2460,7 @@ __docker_complete_volumes() { integer ret=1 declare -a lines volumes - lines=(${(f)${:-"$(_call_program commands docker $docker_options volume ls)"$'\n'}}) + lines=(${(f)${:-"$(_call_program -p docker-volume docker $docker_options volume ls)"$'\n'}}) # Parse header line to find columns local i=1 j=1 k header=${lines[1]} @@ -2552,7 +2556,7 @@ __docker_complete_contexts() { integer ret=1 declare -a contexts - contexts=(${(f)${:-"$(_call_program commands docker $docker_options context ls -q)"$'\n'}}) + contexts=(${(f)${:-"$(_call_program -p docker-context docker $docker_options context ls -q)"$'\n'}}) _describe -t context-list "context" contexts && ret=0 return ret @@ -2648,7 +2652,7 @@ __docker_commands() { && ! _retrieve_cache docker_subcommands || [[ ${force_invalidation} -eq 1 ]]; then local -a lines - lines=(${(f)"$(_call_program commands docker 2>&1)"}) + lines=(${(f)"$(_call_program docker docker 2>&1)"}) _docker_subcommands=(${${${(M)${lines[$((${lines[(i)*Commands:]} + 1)),-1]}:# *}## #}/\*# ##/:}) _docker_subcommands=($_docker_subcommands 'daemon:Enable daemon mode' 'help:Show help for a command') (( $#_docker_subcommands > 2 )) && _store_cache docker_subcommands _docker_subcommands