Add bash completion for pkica

Static completion rules for all pkica commands, options and option values,
plus unit, interactive (tmux) and --help reconciliation tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
a.gritsenko
2026-09-28 23:22:43 +03:00
co-authored by Claude Opus 5.5
parent 81404a24cc
commit 11283c573a
7 changed files with 620 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Сверка таблиц pkica.bash-completion с реальным выводом `pkica ... --help`.
# Запуск (из WSL/Linux): bash tests/check_tree.sh [путь к pkica]
# Без аргумента используется pkica из PATH.
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
pkica=${1:-$(command -v pkica)}
[[ -x $pkica ]] || { echo "pkica not found: укажите путь аргументом"; exit 1; }
# shellcheck disable=SC1091
source "$root/pkica.bash-completion" || exit 1
fail=0 nodes=0
walk()
{
local path=$1 help cmds opts expected_cmds expected_opts sub
help=$("$pkica" ${path#pkica} --help 2>&1)
nodes=$((nodes + 1))
cmds=$(awk '/^Commands:/{f=1;next} f&&/^$/{f=0} f&&/^ [a-z]/{print $1}' <<<"$help" |
tr '\n' ' ' | sed 's/ $//')
opts=$(awk '/^Options:/{f=1;next} /^(Commands:)?$/{f=0}
f&&/^ -/{s=$1; gsub(/-\?\|/,"",s); if($2 ~ /^</) s=s "="; printf "%s ", s}' <<<"$help" |
sed 's/ $//')
expected_cmds=${_pkica_cmds[$path]-}
expected_opts=${_pkica_opts[$path]:--h|--help}
if [[ $cmds != "$expected_cmds" ]]; then
fail=$((fail + 1))
printf 'MISMATCH commands [%s]\n help: %s\n completion: %s\n' "$path" "$cmds" "$expected_cmds"
fi
if [[ $opts != "$expected_opts" ]]; then
fail=$((fail + 1))
printf 'MISMATCH options [%s]\n help: %s\n completion: %s\n' "$path" "$opts" "$expected_opts"
fi
for sub in $cmds; do
walk "$path $sub"
done
}
walk pkica
# Узлы в таблицах, которых нет в утилите.
for key in "${!_pkica_cmds[@]}" "${!_pkica_opts[@]}"; do
"$pkica" ${key#pkica} --help 2>&1 | grep -q "^Usage: $key " || {
fail=$((fail + 1))
printf 'UNKNOWN node in completion: [%s]\n' "$key"
}
done
echo "checked nodes: $nodes, mismatches: $fail"
((fail == 0))
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env bash
# Модульные тесты pkica.bash-completion.
# Запуск (из WSL/Linux): bash tests/test_completion.sh
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
# shellcheck disable=SC1091
source /usr/share/bash-completion/bash_completion || exit 1
# shellcheck disable=SC1091
source "$root/pkica.bash-completion" || exit 1
pass=0 fail=0
# Разбивает строку на слова так же, как readline: по пробелам и символам
# COMP_WORDBREAKS (группа подряд идущих разделителей - отдельное слово).
_split_line()
{
local line=$1 c word= i
COMP_WORDS=()
for ((i = 0; i < ${#line}; i++)); do
c=${line:i:1}
if [[ $c == ' ' ]]; then
[[ -n $word ]] && COMP_WORDS+=("$word")
word=
elif [[ $c == [=:] ]]; then
if [[ -n $word && $word != *[=:] ]]; then
COMP_WORDS+=("$word")
word=
fi
word+=$c
else
if [[ $word == *[=:] ]]; then
COMP_WORDS+=("$word")
word=
fi
word+=$c
fi
done
COMP_WORDS+=("$word")
COMP_CWORD=$((${#COMP_WORDS[@]} - 1))
}
# complete_line "строка" -> печатает отсортированные варианты через пробел
complete_line()
{
COMP_LINE=$1
COMP_POINT=${#COMP_LINE}
_split_line "$COMP_LINE"
COMPREPLY=()
_pkica pkica "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD - 1]}"
printf '%s\n' "${COMPREPLY[@]}" | sed '/^$/d' | sort | tr '\n' ' ' | sed 's/ $//'
}
check()
{
local line=$1 expected got
expected=$(printf '%s\n' $2 | sort | tr '\n' ' ' | sed 's/ $//')
got=$(complete_line "$line")
if [[ $got == "$expected" ]]; then
pass=$((pass + 1))
printf 'PASS %s\n' "$line|"
else
fail=$((fail + 1))
printf 'FAIL %s\n expected: [%s]\n got: [%s]\n' "$line|" "$expected" "$got"
fi
}
# --- команды ---
check "pkica " "ca cert-registry ra"
check "pkica c" "ca cert-registry"
check "pkica -" "-v --version -h --help"
check "pkica ca " "authority cert cert-request crl db import-legacy issue-cert new-cert-req new-root-cert rev-request signing-key template"
check "pkica ca cert" "cert cert-request"
check "pkica ca -" "-h --help"
check "pkica ca crl schedule " "get update"
check "pkica ca template encode-extension " "certificate-template identification-kind"
check "pkica ra " "db folder group import-legacy operator permission settings"
check "pkica cert-registry db " "info new upgrade"
check "pkica ra group add" "add add-operator"
# --- опции ---
check "pkica ca crl issue --" "--name --delta-only --next-update --final --key-index --output-folder --force --help"
check "pkica ca crl issue " "--name --delta-only --next-update --final --key-index --output-folder --force --help"
check "pkica ca authority show -" "-n --name -h --help --format"
check "pkica ca authority set-default --n" "--name --none"
check "pkica ra permission grant --all" "--all-folders --all-groups --all"
check "pkica ra settings set -" "-n --name -v --value -d --default -f --file -h --help"
# уже указанные опции (и их синонимы) не предлагаются
check "pkica ca authority show -n x -" "-h --help --format"
check "pkica ca authority show --name x --format Json -" "-h --help"
check "pkica ca authority show --name=x --" "--help --format"
# значения опций не мешают определению пути
check "pkica ca crl issue -n ca1 -d -" "-u --next-update --final --key-index -o --output-folder --force -h --help"
# --- значения опций ---
check "pkica ca authority show --format " "Json Yaml"
check "pkica ca authority show --format J" "Json"
check "pkica ca authority show --format=" "Json Yaml"
check "pkica ca authority show --format=Y" "Yaml"
check "pkica ca db new -s " "PostgreSql SqlServer"
check "pkica ra db new --storage-type P" "PostgreSql"
check "pkica ca new-cert-req --sign-tool-class K" "KC1 KC2 KC3 KB1 KB2 KA1"
check "pkica ca new-cert-req -k " "Signature KeyExchange"
check "pkica ca new-root-cert --key-location " "CurrentUser LocalMachine"
check "pkica ca new-root-cert --serial-number-algorithm " "CryptoPro Msca"
check "pkica ca new-root-cert -v " "Years: Months: Weeks: Days: Hours: Minutes:"
check "pkica ca new-root-cert -V M" "Months: Minutes:"
check "pkica ca signing-key add --store-location " "CurrentUser LocalMachine"
check "pkica ca signing-key add -u " "SignCertAndCrl SignCrl Retired"
check "pkica ca signing-key change-usage --usage S" "SignCertAndCrl SignCrl"
check "pkica ca signing-key load -u " "SignCertAndCrl SignCrl"
check "pkica ca template add -d " "SubCa User Tls Ocsp Tsp Qual QualOcsp QualTsp"
check "pkica ca template update --default-template Q" "Qual QualOcsp QualTsp"
check "pkica ca template encode-extension identification-kind -v " "Personal RemoteCert RemotePassport RemoteSystem"
check "pkica ra settings get -n " "NamePolicy Templates DefaultAuthority"
check "pkica ra settings set --name=T" "Templates"
# опции со свободным значением - без вариантов
check "pkica ca authority show -n " ""
check "pkica ca crl issue -u " ""
check "pkica ca crl get -v " ""
check "pkica ra settings set -v " ""
check "pkica ra operator add -f " ""
check "pkica ca new-cert-req -s " ""
check "pkica ca new-cert-req -n " ""
# флаг с той же буквой, что и опция с аргументом в других командах
check "pkica ca crl issue -d " "--name --next-update --final --key-index --output-folder --force --help"
check "pkica ca template list -d " "--file --help --format"
# --- файлы и каталоги ---
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
mkdir "$tmp/outdir"
touch "$tmp/request.req" "$tmp/ext.bin"
cd "$tmp" || exit 1
check "pkica ca issue-cert -f r" "request.req"
check "pkica ca issue-cert --file=r" "request.req"
check "pkica ca issue-cert -o o" "outdir"
check "pkica ca new-cert-req --extensions-file e" "ext.bin"
check "pkica ra operator add --tls-cert-file e" "ext.bin"
check "pkica ra settings set -f r" "request.req"
check "pkica ca crl get -o " "outdir"
check "pkica ca crl issue --output-folder " "outdir"
echo
echo "passed: $pass, failed: $fail"
((fail == 0))
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Интерактивные тесты pkica.bash-completion: реальный bash + readline в tmux,
# нажатие TAB и проверка содержимого строки ввода.
# Запуск (из WSL/Linux, нужен tmux): bash tests/test_interactive.sh
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
command -v tmux >/dev/null || { echo "tmux not found"; exit 1; }
sess=pkica-compl-$$
tmp=$(mktemp -d)
trap 'tmux kill-session -t "$sess" 2>/dev/null; rm -rf "$tmp"' EXIT
mkdir "$tmp/outdir"
touch "$tmp/request.req"
cat >"$tmp/rc" <<EOF
source /usr/share/bash-completion/bash_completion
source "$root/pkica.bash-completion"
bind 'set bell-style none'
PS1='$ '
cd "$tmp"
EOF
tmux new-session -d -s "$sess" -x 200 -y 50 "bash --rcfile '$tmp/rc' -i"
sleep 1
pass=0 fail=0
# check "ввод" "ожидаемая строка после TAB" (после TAB вводится маркер "#",
# т.к. capture-pane обрезает концевые пробелы)
check()
{
local input=$1 expected="$ $2#" got
tmux send-keys -t "$sess" C-u "$input" Tab "#"
sleep 0.5
got=$(tmux capture-pane -p -t "$sess" | sed '/^$/d' | tail -1)
if [[ $got == "$expected" ]]; then
pass=$((pass + 1))
printf 'PASS %s<TAB>\n' "$input"
else
fail=$((fail + 1))
printf 'FAIL %s<TAB>\n expected: [%s]\n got: [%s]\n' "$input" "$expected" "$got"
fi
tmux send-keys -t "$sess" C-u
tmux send-keys -t "$sess" C-l
sleep 0.2
}
check "pkica ce" "pkica cert-registry "
check "pkica ca sig" "pkica ca signing-key "
check "pkica ca signing-key cha" "pkica ca signing-key change-usage "
check "pkica ca authority show --fo" "pkica ca authority show --format "
check "pkica ca authority show --format Y" "pkica ca authority show --format Yaml "
check "pkica ca authority show --format=J" "pkica ca authority show --format=Json "
check "pkica ca new-root-cert -v Y" "pkica ca new-root-cert -v Years:"
check "pkica ca template add -d Su" "pkica ca template add -d SubCa "
check "pkica ca issue-cert -f " "pkica ca issue-cert -f "
check "pkica ca issue-cert -f req" "pkica ca issue-cert -f request.req "
check "pkica ca crl get -o " "pkica ca crl get -o outdir/"
check "./pkica ra fo" "./pkica ra folder "
# Пустое слово: при TAB TAB в списке должны быть и файлы, и каталоги.
tmux send-keys -t "$sess" C-u "pkica ca issue-cert -f " Tab Tab
sleep 0.5
screen=$(tmux capture-pane -p -t "$sess")
if [[ $screen == *request.req* && $screen == *outdir/* ]]; then
pass=$((pass + 1))
echo "PASS pkica ca issue-cert -f <TAB><TAB> lists files"
else
fail=$((fail + 1))
printf 'FAIL pkica ca issue-cert -f <TAB><TAB> lists files\n%s\n' "$screen"
fi
echo
echo "passed: $pass, failed: $fail"
((fail == 0))