mirror of https://github.com/containers/podman.git
380 lines
13 KiB
Bash
380 lines
13 KiB
Bash
#compdef podman
|
|
|
|
# To get zsh to reread this file: unset -f _podman;rm -f ~/.zcompdump;compinit
|
|
|
|
# On rereads, reset cache. (Not that the caching works, but some day it might)
|
|
unset -m '_podman_*'
|
|
|
|
###############################################################################
|
|
# BEGIN 'podman help' parsers -- for options, subcommands, and usage
|
|
|
|
# Run 'podman XX --help', set _podman_commands to a formatted list of cmds
|
|
_read_podman_commands() {
|
|
local line
|
|
|
|
# Cache: the intention here is to run each 'podman help' only once.
|
|
# Unfortunately it doesn't seem to actually be working: even though
|
|
# I can see the var_ref in my shell, it's not visible here.
|
|
local _var_ref=_podman_commands_"${*// /_}"
|
|
typeset -ga _podman_commands
|
|
_podman_commands=(${(P)_var_ref})
|
|
(( $#_podman_commands )) && return
|
|
|
|
_call_program podman podman "$@" --help |\
|
|
sed -n -e '0,/^Available Commands/d' -e '/^[A-Z]/q;p' |\
|
|
sed -e 's/^ \+\([^ ]\+\) \+/\1:/' |\
|
|
egrep . | while read line; do
|
|
_podman_commands+=($line)
|
|
done
|
|
|
|
eval "typeset -ga $_var_ref"
|
|
eval "$_var_ref=(\$_podman_commands)"
|
|
}
|
|
|
|
# Run 'podman XX --help', set _podman_flag_list to a formatted list
|
|
# of flag options for XX
|
|
_read_podman_flags() {
|
|
local line
|
|
|
|
local _var_ref=_podman_flags_"${*// /_}"
|
|
eval "typeset -ga ${_var_ref}"
|
|
typeset -ga _podman_flag_list
|
|
_podman_flag_list=(${(P)_var_ref})
|
|
(( $#_podman_flag_list )) && return
|
|
|
|
# Extract the Flags; strip leading whitespace; pack '-f, --foo'
|
|
# as '-f,--foo' (no space); then add '=' to '--foo string'.
|
|
# The result will be, e.g. '-f,--foo=string Description of Option'
|
|
_call_program podman podman "$@" --help |\
|
|
sed -n -e '0,/^Flags:/d' -e '/^$/q;p' |\
|
|
grep '^ \+-' |\
|
|
sed -e 's/^ *//' -e 's/^\(-.,\) --/\1--/' |\
|
|
sed -e 's/^\(-[^ ]\+\) \([^ ]\+\) /\1=\2 /' |\
|
|
while read flags desc;do
|
|
# flags like --foo=string: split into --foo & string
|
|
local -a tmpa
|
|
local optval=
|
|
tmpa=(${(s.=.)flags})
|
|
if [ -n "$tmpa[2]" ]; then
|
|
flags=$tmpa[1]
|
|
optval=$tmpa[2]
|
|
fi
|
|
|
|
# 'podman attach --detach-keys' includes ']' in help msg
|
|
desc=${desc//\]/\\]}
|
|
|
|
for flag in ${(s:,:)flags}; do
|
|
if [ -n "$optval" ]; then
|
|
_podman_flag_list+=("${flag}[$desc]:$(_podman_find_helper ${flags} ${optval} ${desc})")
|
|
else
|
|
_podman_flag_list+=("${flag}[$desc]")
|
|
fi
|
|
done
|
|
done
|
|
|
|
eval "typeset -ga $_var_ref=(\$_podman_flag_list)"
|
|
}
|
|
|
|
# Run 'podman XXX --help', set _podman_usage to the line after "Usage:"
|
|
_read_podman_usage() {
|
|
local _var_ref=_podman_usage_"${*// /_}"
|
|
typeset -ga _podman_usage
|
|
_podman_usage=${(P)_var_ref}
|
|
(( $#_podman_usage )) && return
|
|
|
|
_podman_usage=$(_call_program podman podman "$@" --help |\
|
|
grep -A1 'Usage:'|\
|
|
tail -1 |\
|
|
sed -e 's/^ *//')
|
|
|
|
eval "typeset -ga $_var_ref"
|
|
eval "$_var_ref=\$_podman_usage"
|
|
}
|
|
|
|
# END 'podman help' parsers
|
|
###############################################################################
|
|
# BEGIN custom helpers for individual option arguments
|
|
|
|
# Find a zsh helper for a given flag or command-line option
|
|
_podman_find_helper() {
|
|
local flags=$1
|
|
local optval=$2
|
|
local desc=$3
|
|
local helper=
|
|
|
|
# Yes, this is a lot of hardcoding. IMHO it's still better than
|
|
# hardcoding every possible podman option.
|
|
# FIXME: there are many more options that could use helpers.
|
|
if expr "$desc" : ".*[Dd]irectory" >/dev/null; then
|
|
optval="directory"
|
|
helper="_files -/"
|
|
elif expr "$desc" : ".*[Pp]ath" >/dev/null; then
|
|
optval="path"
|
|
helper=_files
|
|
# For messages like 'restart policy ("always"|"no"|"on-failure")
|
|
elif optlist=$(expr "$desc" : '.*(\(\"[^\\)]\+|[^\\)]\+\"\))' 2>/dev/null); then
|
|
optval=${${flags##--}//-/ } # "--log-level" => "log level"
|
|
optlist=${optlist//\"/} # "a"|"b"|"c" => a|b|c
|
|
optlist=${optlist//\|/ } # a|b|c => a b c
|
|
# FIXME: how to present values _in order_, not sorted alphabetically?
|
|
helper="($optlist)"
|
|
fi
|
|
echo "$optval:$helper"
|
|
}
|
|
|
|
# END custom helpers for individual option arguments
|
|
###############################################################################
|
|
# BEGIN helpers for command-line args (containers, images)
|
|
|
|
__podman_helper_generic() {
|
|
local expl line
|
|
local -a results
|
|
|
|
local foo1=$1; shift
|
|
local name=$2; shift
|
|
|
|
_call_program $foo1 podman "$@" |\
|
|
while read line; do
|
|
results+=(${=line})
|
|
done
|
|
|
|
_wanted $foo1 expl $name compadd ${(u)results}
|
|
}
|
|
|
|
_podman_helper_image() {
|
|
__podman_helper_generic podman-images 'images' \
|
|
images --format '{{.ID}}\ {{.Repository}}:{{.Tag}}'
|
|
}
|
|
|
|
# FIXME: at some point, distinguish between running & stopped containers
|
|
_podman_helper_container() {
|
|
__podman_helper_generic podman-containers 'containers' \
|
|
ps -a --format '{{.Names}}\ {{.ID}}'
|
|
}
|
|
|
|
_podman_helper_pod() {
|
|
__podman_helper_generic podman-pods 'pods' pod list --format '{{.Name}}'
|
|
}
|
|
|
|
_podman_helper_volume() {
|
|
__podman_helper_generic podman-volumes 'volumes' volume ls --format '{{.Name}}'
|
|
}
|
|
|
|
# Combinations. This one seen in diff & inspect
|
|
_podman_helper_container-or-image() {
|
|
_podman_helper_image
|
|
_podman_helper_container
|
|
}
|
|
|
|
# Seen in generate-kube
|
|
_podman_helper_container-or-pod() {
|
|
_podman_helper_container
|
|
_podman_helper_pod
|
|
}
|
|
|
|
# For top and pod-top
|
|
_podman_helper_format-descriptors() {
|
|
__podman_helper_generic top-format-descriptors 'format descriptors' \
|
|
top --list-descriptors
|
|
}
|
|
|
|
# for push, login/logout, and trust
|
|
# FIXME: some day, use this to define a helper for IMAGE-PATH (in 'pull')
|
|
_podman_helper_registry() {
|
|
local expl
|
|
local -a registries
|
|
|
|
# Suggestions for improvement more than welcome.
|
|
python3 -c 'from configparser import ConfigParser;cp=ConfigParser();cp.read("/etc/containers/registries.conf");registries=eval(cp.get("registries.search","registries"));[print(r) for r in registries]' 2>/dev/null | while read line; do
|
|
registries+=($line)
|
|
done
|
|
|
|
if (( $#registries )); then
|
|
_wanted podman-registry expl "registry" compadd ${(u)registries}
|
|
else
|
|
_hosts
|
|
fi
|
|
}
|
|
|
|
# END helpers for command-line args
|
|
###############################################################################
|
|
# BEGIN figure out completion helpers for a given (sub)command
|
|
|
|
# Read Usage string for this subcommand, set up helpers for its subargs
|
|
_set_up_podman_args() {
|
|
_read_podman_usage "$@"
|
|
|
|
typeset -ga _podman_args=()
|
|
# E.g. 'podman exec [flags] CONTAINER [...' -> 'CONTAINER [....'
|
|
local usage_rhs=$(expr "$_podman_usage" : ".*\[flags\] \+\(.*\)")
|
|
|
|
# e.g. podman pod ps which takes no further args
|
|
if [ -z "$usage_rhs" ]; then
|
|
return
|
|
fi
|
|
|
|
# podman diff & inspect accept 'CONTAINER | IMAGE'; make into one keyword.
|
|
usage_rhs=${usage_rhs// | /-OR-}
|
|
|
|
# Arg parsing. There are three possibilities in Usage messages:
|
|
#
|
|
# [IMAGE] - optional image arg (zero or one)
|
|
# IMAGE - exactly one image arg
|
|
# IMAGE [IMAGE...] - one or more image args
|
|
# and, theoretically:
|
|
# [IMAGE...] - zero or more? Haven't seen it in practice. Defer.
|
|
#
|
|
# For completion purposes, we only need to provide two options:
|
|
# one, or more than one? That is: continue offering completion
|
|
# suggestions after the first one? For that, we make two passes;
|
|
# in the first, mark an option as either '' (only one) or
|
|
|
|
# Parse each command-line arg seen in usage message
|
|
local word
|
|
local -A _seen=()
|
|
for word in ${=usage_rhs}; do
|
|
local unbracketed=$(expr "$word" : "\[\(.*\)\]")
|
|
|
|
if [ -n "$unbracketed" ]; then
|
|
# Remove all dots; assume(!?) that they'll all be at the end
|
|
unbracketed=${unbracketed//./}
|
|
|
|
if (( $_seen[$unbracketed] )); then
|
|
# Is this the same word as the previous arg?
|
|
if expr "$_podman_args[-1]" : ":$unbracketed:" >/dev/null; then
|
|
# Yes. Make it '*:...' instead of ':...', indicating >1
|
|
_podman_args[-1]="*$_podman_args[-1]"
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
word=$unbracketed
|
|
fi
|
|
|
|
# As of 2019-03 all such instances are '[COMMAND [ARG...]]' and are,
|
|
# of course, at the end of the line. We can't offer completion for
|
|
# these, because the container will have different commands than
|
|
# the host system... but try anyway.
|
|
if [ "$word" = '[COMMAND' ]; then
|
|
# e.g. podman create, exec, run
|
|
_podman_args+=(
|
|
":command: _command_names -e"
|
|
"*::arguments: _normal"
|
|
)
|
|
return
|
|
fi
|
|
|
|
# Look for an existing helper, e.g. IMAGE -> _podman_helper_image
|
|
local helper="_podman_helper_${(L)word}"
|
|
if (( $+functions[$helper] )); then
|
|
:
|
|
else
|
|
# No defined helper. Reset, but check for known expressions.
|
|
helper=
|
|
case "$word" in
|
|
KUBEFILE) helper='_files -g "*.y(|a)ml(-.)"' ;;
|
|
PATH) helper='_files' ;;
|
|
esac
|
|
fi
|
|
|
|
# Another special case: 'top' actually takes multiple options
|
|
local multi=
|
|
if [ "$word" = "FORMAT-DESCRIPTORS" ]; then
|
|
multi='*'
|
|
fi
|
|
_podman_args+=("$multi:${(L)word}:$helper")
|
|
_seen[$word]=1
|
|
done
|
|
}
|
|
|
|
# For an endpoint command, i.e. not a subcommand.
|
|
_podman_terminus() {
|
|
typeset -A opt_args
|
|
typeset -ga _podman_flag_list
|
|
typeset -ga _podman_args
|
|
integer ret=1
|
|
|
|
# Find out what args it takes (e.g. image(s), container(s)) and see
|
|
# if we have helpers for them.
|
|
_set_up_podman_args "$@"
|
|
_arguments -C $_podman_flag_list $_podman_args && ret=0
|
|
|
|
return ret
|
|
}
|
|
|
|
# END figure out completion helpers for a given (sub)command
|
|
################################################################################
|
|
# BEGIN actual entry point
|
|
|
|
# This is the main entry point; it's also where we (recursively) come in
|
|
# to handle nested subcommands such as 'podman container' or even 3-level
|
|
# ones like 'podman generate kube'. Nesting is complicated, so here's
|
|
# my best understanding as of 2019-03-12:
|
|
#
|
|
# Easy first: when you do "podman <TAB>" zsh calls us, we run 'podman --help',
|
|
# figure out the global options and subcommands, and run the magic _arguments
|
|
# command. That will offer those options/subcommands, and complete, etc.
|
|
#
|
|
# Where it gets harder is when you do "podman container mount <TAB>".
|
|
# zsh first calls us with words=(podman container mount) but we don't
|
|
# want all that full context yet! We want to go a piece at a time,
|
|
# handling 'container' first, then 'mount'; ending up with our
|
|
# final 'podman container mount --help' giving us suitable flags
|
|
# and no subcommands; from which we determine that it's a terminus
|
|
# and jump to a function that handles non-subcommand arguments.
|
|
#
|
|
# This is the closest I've yet come to understanding zsh completion;
|
|
# it is still incomplete and may in fact be incorrect. But it works
|
|
# better than anything I've played with so far.
|
|
_podman_subcommand() {
|
|
local curcontext="$curcontext" state line
|
|
typeset -A opt_args
|
|
integer ret=1
|
|
|
|
# Run 'podman --help' / 'podman system --help' for our context (initially
|
|
# empty, then possibly under subcommands); from those, get a list of
|
|
# flags appropriate for this context and, if applicable, subcommands.
|
|
_read_podman_flags "$@"
|
|
_read_podman_commands "$@"
|
|
|
|
# Now, is this a sub-subcommand, or does it have args?
|
|
if (( $#_podman_commands )); then
|
|
# Subcommands required (podman, podman system, etc)
|
|
local cmd=${words// /_}
|
|
_arguments -C $_podman_flag_list \
|
|
"(-): :->command" \
|
|
"(-)*:: :->option-or-argument" \
|
|
&& ret=0
|
|
|
|
case $state in
|
|
(command)
|
|
_describe -t command "podman $* command" _podman_commands && ret=0
|
|
;;
|
|
(option-or-argument)
|
|
# I think this is when we have a _completed_ subcommand.
|
|
# Recurse back into here, offering options for that subcommand.
|
|
curcontext=${curcontext%:*:*}:podman-${words[1]}:
|
|
_podman_subcommand "$@" ${words[1]} && ret=0
|
|
;;
|
|
esac
|
|
else
|
|
# At a terminus, i.e. podman info, podman history; find out
|
|
# what args it takes.
|
|
_podman_terminus "$@" && ret=0
|
|
fi
|
|
|
|
return ret
|
|
}
|
|
|
|
_podman() {
|
|
_podman_subcommand
|
|
}
|
|
|
|
# Local Variables:
|
|
# mode: shell-script
|
|
# sh-indentation: 4
|
|
# indent-tabs-mode: nil
|
|
# sh-basic-offset: 4
|
|
# End:
|
|
# vim: ft=zsh sw=4 ts=4 et
|