377 lines
14 KiB
Bash
377 lines
14 KiB
Bash
# ============================================================================
|
||
# reg — Docker Registry Client
|
||
# bash completion
|
||
# ============================================================================
|
||
#
|
||
# Установка (любой из вариантов):
|
||
#
|
||
# 1) source ./reg-completion.bash
|
||
# 2) cp reg-completion.bash ~/.local/share/bash-completion/completions/reg
|
||
# 3) sudo cp reg-completion.bash /usr/share/bash-completion/completions/reg
|
||
# 4) echo 'source /path/to/reg-completion.bash' >> ~/.bashrc
|
||
#
|
||
# При изменении CLI (новые команды, подкоманды, флаги) обновляйте:
|
||
# _reg_commands, _reg_*_subcommands, _reg_help_subcommands,
|
||
# _reg_opts_<group>_<cmd>, _reg_value_flags, _reg_values_<flag>.
|
||
#
|
||
# Флаги справки (-h/-help/--help) в опциях намеренно отсутствуют:
|
||
# справка доступна только через `reg help <command>`.
|
||
# ============================================================================
|
||
|
||
# --- Справочники команд и флагов --------------------------------------------
|
||
|
||
_reg_commands="repo blob image manifest auth referrer config export import ping capabilities help"
|
||
|
||
_reg_global_flags="-r -config -lang -token -user -pass -insecure"
|
||
|
||
_reg_repo_subcommands="list tree tags info manifest delete find latest prune stats verify"
|
||
_reg_blob_subcommands="delete get info"
|
||
_reg_image_subcommands="pull push copy config files history"
|
||
_reg_manifest_subcommands="rm tag untag exist digest"
|
||
_reg_auth_subcommands="login logout status migrate token whoami"
|
||
_reg_referrer_subcommands="list"
|
||
_reg_config_subcommands="get set unset list"
|
||
|
||
_reg_help_subcommands="repo blob image manifest auth referrer config export import ping capabilities help"
|
||
|
||
# Флаги по командам и подкомандам
|
||
_reg_opts_repo=""
|
||
_reg_opts_repo_list="-a -filter"
|
||
_reg_opts_repo_tree="-a -s -h"
|
||
_reg_opts_repo_tags="-a -d"
|
||
_reg_opts_repo_info="-platform"
|
||
_reg_opts_repo_manifest=""
|
||
_reg_opts_repo_delete="-f"
|
||
_reg_opts_repo_find="-a"
|
||
_reg_opts_repo_latest="-a"
|
||
_reg_opts_repo_prune="-keep -older-than -dry-run -force-latest"
|
||
_reg_opts_repo_stats="-h"
|
||
_reg_opts_repo_verify="-deep -jobs -v"
|
||
_reg_opts_blob=""
|
||
_reg_opts_blob_delete=""
|
||
_reg_opts_blob_get="-o -v"
|
||
_reg_opts_blob_info=""
|
||
_reg_opts_image=""
|
||
_reg_opts_image_pull="-o -f -platform -v"
|
||
_reg_opts_image_push="-mount-from -chunk-size"
|
||
_reg_opts_image_copy="-platform -mount-from"
|
||
_reg_opts_image_config="-platform -field -raw"
|
||
_reg_opts_image_files="-platform -all -layer -name -n -v"
|
||
_reg_opts_image_history="-platform -raw"
|
||
_reg_opts_manifest=""
|
||
_reg_opts_manifest_rm="-p -f"
|
||
_reg_opts_manifest_tag=""
|
||
_reg_opts_manifest_untag=""
|
||
_reg_opts_manifest_exist=""
|
||
_reg_opts_manifest_digest=""
|
||
_reg_opts_auth=""
|
||
_reg_opts_auth_login="-user -pass"
|
||
_reg_opts_auth_logout=""
|
||
_reg_opts_auth_status=""
|
||
_reg_opts_auth_migrate="-from -to -dry-run -overwrite -registry"
|
||
_reg_opts_auth_token="-raw"
|
||
_reg_opts_auth_whoami=""
|
||
_reg_opts_referrer=""
|
||
_reg_opts_referrer_list="-type"
|
||
_reg_opts_config=""
|
||
_reg_opts_export="-o -platform"
|
||
_reg_opts_import=""
|
||
_reg_opts_ping="-v"
|
||
_reg_opts_capabilities=""
|
||
_reg_opts_help=""
|
||
|
||
# Флаги, принимающие значение (для подсчёта позиционных аргументов)
|
||
_reg_value_flags="-r -config -lang -token -user -pass -o -platform -p -mount-from -chunk-size -type -filter -from -to -registry -keep -older-than -jobs -field -layer -name"
|
||
|
||
# Возможные значения для отдельных флагов
|
||
_reg_values_platform="linux/amd64 linux/arm64 linux/arm/v7 linux/386 windows/amd64 darwin/amd64 darwin/arm64"
|
||
_reg_values_lang="en ru"
|
||
_reg_values_vault="docker local"
|
||
_reg_chunk_values="1048576 5242880 10485760"
|
||
_reg_jobs_values="1 2 4 8 16 32"
|
||
_reg_keep_values="1 3 5 10 20 50"
|
||
|
||
# --- Вспомогательные функции ------------------------------------------------
|
||
|
||
# Значение registry= из ~/.reg/reg.conf (если есть).
|
||
_reg_registry_from_config() {
|
||
local f="$HOME/.reg/reg.conf"
|
||
[[ -r "$f" ]] || return 0
|
||
local line
|
||
while IFS= read -r line; do
|
||
case "$line" in
|
||
registry=*) echo "${line#registry=}"; return 0 ;;
|
||
esac
|
||
done < "$f"
|
||
}
|
||
|
||
# Дополнение каталогов (совместимо с наличием/отсутствием bash-completion)
|
||
_reg_complete_dir() {
|
||
if declare -F _filedir >/dev/null 2>&1; then
|
||
_filedir -d
|
||
else
|
||
COMPREPLY=($(compgen -d -- "$cur"))
|
||
fi
|
||
}
|
||
|
||
# Дополнение файлов и каталогов
|
||
_reg_complete_file() {
|
||
if declare -F _filedir >/dev/null 2>&1; then
|
||
_filedir
|
||
else
|
||
COMPREPLY=($(compgen -f -- "$cur"))
|
||
fi
|
||
}
|
||
|
||
# Значения для -platform / -p / -lang / -chunk-size / -r / -from / -to / -jobs / -keep по имени флага
|
||
_reg_values_for_flag() {
|
||
local flag="$1"
|
||
case "$flag" in
|
||
-platform|-p) echo "$_reg_values_platform" ;;
|
||
-lang) echo "$_reg_values_lang" ;;
|
||
-from|-to) echo "$_reg_values_vault" ;;
|
||
-chunk-size) echo "$_reg_chunk_values" ;;
|
||
-jobs) echo "$_reg_jobs_values" ;;
|
||
-keep) echo "$_reg_keep_values" ;;
|
||
-r) _reg_registry_from_config ;;
|
||
*) : ;;
|
||
esac
|
||
}
|
||
|
||
# Флаги для конкретной группы/подкоманды
|
||
_reg_opts_for() {
|
||
local cmd="$1" sub="$2"
|
||
case "$cmd" in
|
||
repo)
|
||
case "$sub" in
|
||
list) echo "$_reg_opts_repo_list" ;;
|
||
tree) echo "$_reg_opts_repo_tree" ;;
|
||
tags) echo "$_reg_opts_repo_tags" ;;
|
||
info) echo "$_reg_opts_repo_info" ;;
|
||
manifest) echo "$_reg_opts_repo_manifest" ;;
|
||
delete) echo "$_reg_opts_repo_delete" ;;
|
||
find) echo "$_reg_opts_repo_find" ;;
|
||
latest) echo "$_reg_opts_repo_latest" ;;
|
||
prune) echo "$_reg_opts_repo_prune" ;;
|
||
stats) echo "$_reg_opts_repo_stats" ;;
|
||
verify) echo "$_reg_opts_repo_verify" ;;
|
||
*) echo "$_reg_opts_repo" ;;
|
||
esac
|
||
;;
|
||
blob)
|
||
case "$sub" in
|
||
delete) echo "$_reg_opts_blob_delete" ;;
|
||
get) echo "$_reg_opts_blob_get" ;;
|
||
info) echo "$_reg_opts_blob_info" ;;
|
||
*) echo "$_reg_opts_blob" ;;
|
||
esac
|
||
;;
|
||
image)
|
||
case "$sub" in
|
||
pull) echo "$_reg_opts_image_pull" ;;
|
||
push) echo "$_reg_opts_image_push" ;;
|
||
copy) echo "$_reg_opts_image_copy" ;;
|
||
config) echo "$_reg_opts_image_config" ;;
|
||
files) echo "$_reg_opts_image_files" ;;
|
||
history) echo "$_reg_opts_image_history" ;;
|
||
*) echo "$_reg_opts_image" ;;
|
||
esac
|
||
;;
|
||
manifest)
|
||
case "$sub" in
|
||
rm) echo "$_reg_opts_manifest_rm" ;;
|
||
tag) echo "$_reg_opts_manifest_tag" ;;
|
||
untag) echo "$_reg_opts_manifest_untag" ;;
|
||
exist) echo "$_reg_opts_manifest_exist" ;;
|
||
digest) echo "$_reg_opts_manifest_digest" ;;
|
||
*) echo "$_reg_opts_manifest" ;;
|
||
esac
|
||
;;
|
||
auth)
|
||
case "$sub" in
|
||
login) echo "$_reg_opts_auth_login" ;;
|
||
logout) echo "$_reg_opts_auth_logout" ;;
|
||
status) echo "$_reg_opts_auth_status" ;;
|
||
migrate) echo "$_reg_opts_auth_migrate" ;;
|
||
token) echo "$_reg_opts_auth_token" ;;
|
||
whoami) echo "$_reg_opts_auth_whoami" ;;
|
||
*) echo "$_reg_opts_auth" ;;
|
||
esac
|
||
;;
|
||
referrer)
|
||
case "$sub" in
|
||
list) echo "$_reg_opts_referrer_list" ;;
|
||
*) echo "$_reg_opts_referrer" ;;
|
||
esac
|
||
;;
|
||
config) echo "$_reg_opts_config" ;;
|
||
export) echo "$_reg_opts_export" ;;
|
||
import) echo "$_reg_opts_import" ;;
|
||
ping) echo "$_reg_opts_ping" ;;
|
||
capabilities) echo "$_reg_opts_capabilities" ;;
|
||
help) echo "$_reg_opts_help" ;;
|
||
# Top-level aliases
|
||
pull) echo "$_reg_opts_image_pull" ;;
|
||
push) echo "$_reg_opts_image_push" ;;
|
||
copy) echo "$_reg_opts_image_copy" ;;
|
||
rm) echo "$_reg_opts_manifest_rm" ;;
|
||
tag) echo "$_reg_opts_manifest_tag" ;;
|
||
untag) echo "$_reg_opts_manifest_untag" ;;
|
||
exist) echo "$_reg_opts_manifest_exist" ;;
|
||
digest) echo "$_reg_opts_manifest_digest" ;;
|
||
login) echo "$_reg_opts_auth_login" ;;
|
||
logout) echo "$_reg_opts_auth_logout" ;;
|
||
token) echo "$_reg_opts_auth_token" ;;
|
||
whoami) echo "$_reg_opts_auth_whoami" ;;
|
||
esac
|
||
}
|
||
|
||
# --- Основная функция автодополнения ----------------------------------------
|
||
|
||
_reg_complete() {
|
||
local cur prev
|
||
COMPREPLY=()
|
||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||
|
||
# --- Обработка формы --flag=value / -flag=value -------------------------
|
||
if [[ "$cur" == -*=* ]]; then
|
||
local flag="${cur%%=*}"
|
||
local val="${cur#*=}"
|
||
local vals
|
||
vals="$(_reg_values_for_flag "$flag")"
|
||
if [[ -n "$vals" ]]; then
|
||
local matches
|
||
matches=($(compgen -W "$vals" -- "$val"))
|
||
COMPREPLY=("${matches[@]/#/$flag=}")
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# --- Собираем позиционные аргументы до текущего слова -------------------
|
||
local -a positionals=()
|
||
local skip_next=0 i w f
|
||
for ((i=1; i<COMP_CWORD; i++)); do
|
||
w="${COMP_WORDS[i]}"
|
||
if [[ $skip_next -eq 1 ]]; then
|
||
skip_next=0
|
||
continue
|
||
fi
|
||
case "$w" in
|
||
-*=*) ;;
|
||
*)
|
||
local is_value=0
|
||
for f in $_reg_value_flags; do
|
||
if [[ "$w" == "$f" ]]; then is_value=1; break; fi
|
||
done
|
||
if [[ $is_value -eq 1 ]]; then
|
||
skip_next=1
|
||
elif [[ "$w" == -* ]]; then
|
||
:
|
||
else
|
||
positionals+=("$w")
|
||
fi
|
||
;;
|
||
esac
|
||
done
|
||
|
||
local cmd="" subcmd=""
|
||
if [[ ${#positionals[@]} -ge 1 ]]; then cmd="${positionals[0]}"; fi
|
||
if [[ ${#positionals[@]} -ge 2 ]]; then subcmd="${positionals[1]}"; fi
|
||
local npos=${#positionals[@]}
|
||
|
||
# --- Завершение значения флага (после space) ----------------------------
|
||
if [[ "$cur" != -* ]]; then
|
||
local vals
|
||
vals="$(_reg_values_for_flag "$prev")"
|
||
if [[ -n "$vals" ]]; then
|
||
COMPREPLY=($(compgen -W "$vals" -- "$cur"))
|
||
return 0
|
||
fi
|
||
case "$prev" in
|
||
-o)
|
||
_reg_complete_dir
|
||
return 0
|
||
;;
|
||
-config)
|
||
_reg_complete_file
|
||
return 0
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
# --- Завершение подкоманд известных групп -------------------------------
|
||
if [[ "$npos" -eq 1 ]]; then
|
||
case "$cmd" in
|
||
repo) COMPREPLY=($(compgen -W "$_reg_repo_subcommands" -- "$cur")); return 0 ;;
|
||
blob) COMPREPLY=($(compgen -W "$_reg_blob_subcommands" -- "$cur")); return 0 ;;
|
||
image) COMPREPLY=($(compgen -W "$_reg_image_subcommands" -- "$cur")); return 0 ;;
|
||
manifest) COMPREPLY=($(compgen -W "$_reg_manifest_subcommands" -- "$cur")); return 0 ;;
|
||
auth) COMPREPLY=($(compgen -W "$_reg_auth_subcommands" -- "$cur")); return 0 ;;
|
||
referrer) COMPREPLY=($(compgen -W "$_reg_referrer_subcommands" -- "$cur")); return 0 ;;
|
||
config) COMPREPLY=($(compgen -W "$_reg_config_subcommands" -- "$cur")); return 0 ;;
|
||
esac
|
||
fi
|
||
|
||
# --- Завершение аргументов help -----------------------------------------
|
||
if [[ "$cmd" == "help" ]]; then
|
||
case "$npos" in
|
||
1)
|
||
COMPREPLY=($(compgen -W "$_reg_help_subcommands" -- "$cur"))
|
||
return 0
|
||
;;
|
||
2)
|
||
case "$subcmd" in
|
||
repo) COMPREPLY=($(compgen -W "$_reg_repo_subcommands" -- "$cur")); return 0 ;;
|
||
blob) COMPREPLY=($(compgen -W "$_reg_blob_subcommands" -- "$cur")); return 0 ;;
|
||
image) COMPREPLY=($(compgen -W "$_reg_image_subcommands" -- "$cur")); return 0 ;;
|
||
manifest) COMPREPLY=($(compgen -W "$_reg_manifest_subcommands" -- "$cur")); return 0 ;;
|
||
auth) COMPREPLY=($(compgen -W "$_reg_auth_subcommands" -- "$cur")); return 0 ;;
|
||
referrer) COMPREPLY=($(compgen -W "$_reg_referrer_subcommands" -- "$cur")); return 0 ;;
|
||
config) COMPREPLY=($(compgen -W "$_reg_config_subcommands" -- "$cur")); return 0 ;;
|
||
esac
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
# --- Первый позиционный аргумент push/import: файлы/каталоги ------------
|
||
if [[ ( "$cmd" == "push" || "$cmd" == "import" ) && "$npos" -eq 1 && "$cur" != -* ]]; then
|
||
_reg_complete_file
|
||
return 0
|
||
fi
|
||
if [[ "$cmd" == "image" && "$subcmd" == "push" && "$npos" -eq 2 && "$cur" != -* ]]; then
|
||
_reg_complete_file
|
||
return 0
|
||
fi
|
||
|
||
# --- Завершение флагов --------------------------------------------------
|
||
if [[ "$cur" == -* ]]; then
|
||
local opts
|
||
if [[ -z "$cmd" ]]; then
|
||
opts="$_reg_global_flags"
|
||
else
|
||
opts="$(_reg_opts_for "$cmd" "$subcmd")"
|
||
fi
|
||
if [[ -n "$opts" ]]; then
|
||
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
# --- Пустое слово после команды: подсказываем флаги ---------------------
|
||
if [[ -z "$cur" && -n "$cmd" ]]; then
|
||
local opts
|
||
opts="$(_reg_opts_for "$cmd" "$subcmd")"
|
||
if [[ -n "$opts" ]]; then
|
||
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# --- Регистрация --------------------------------------------------------------
|
||
|
||
complete -F _reg_complete reg
|