release: restore ZUPT and harden source-only 5.2.2

This commit is contained in:
Cristian Cezar Moisés 2026-08-31 14:14:36 -03:00
commit ff99770bd0
205 changed files with 19627 additions and 13215 deletions

View file

@ -1,224 +1,207 @@
#!/bin/bash
#!/usr/bin/env bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
#
# Sprint 2.4.7 regression: shell completions + manpage.
#
# Asserts:
# - completions/vaptvupt.bash has bash-clean syntax
# - completions/_vaptvupt has zsh-clean syntax (if zsh available)
# - completions/vaptvupt.fish has fish-clean syntax (if fish available)
# - Each completion file mentions all the major CLI flags the binary
# actually parses (--kdf, --comment, --pq-sdk, --dedup, ...)
# - doc/zupt.1 mentions current v2.4.x features (--kdf, --comment,
# Argon2id, F-11, comment-file)
# - doc/zupt.1 has the standard sections (NAME, SYNOPSIS, DESCRIPTION,
# COMMANDS, EXAMPLES)
set -u
PASS=0
FAIL=0
P() { PASS=$((PASS+1)); echo "$1"; }
F() { FAIL=$((FAIL+1)); echo "$1"; }
SKIP() { echo " - skipped: $1"; }
set -Eeuo pipefail
cd "$(dirname "$0")/.."
VERSION=$(grep '^#define ZUPT_VERSION_STRING' include/zupt.h | awk -F'"' '{print $2}')
echo "Completions + manpage (vaptvupt $VERSION)"
pass_count=0
fail_count=0
skip_count=0
pass() { pass_count=$((pass_count + 1)); printf ' PASS: %s\n' "$1"; }
fail() { fail_count=$((fail_count + 1)); printf ' FAIL: %s\n' "$1"; }
skip() { skip_count=$((skip_count + 1)); printf ' SKIP: %s\n' "$1"; }
# ─── Bash completion ───
if [ -f completions/vaptvupt.bash ]; then
if bash -n completions/vaptvupt.bash 2>/dev/null; then
P "bash completion: syntax clean"
else
F "bash completion: syntax error"
fi
# Should define a _zupt function and register it via complete -F
if grep -q "^_vaptvupt()" completions/vaptvupt.bash; then
P "bash completion: defines _vaptvupt function"
else
F "bash completion: missing _vaptvupt function"
fi
if grep -qE "^complete -F _vaptvupt (vaptvupt|zupt)" completions/vaptvupt.bash; then
P "bash completion: registers via complete -F"
else
F "bash completion: missing complete -F registration"
fi
version=$(sed -n 's/^#define ZUPT_VERSION_STRING "\([^"]*\)".*/\1/p' include/zupt.h)
[[ -n $version ]] || { printf 'FAIL: cannot determine version\n' >&2; exit 1; }
printf 'ZUPT %s completion and manual-page checks\n' "$version"
completion_files=(
completions/zupt.bash
completions/_zupt
completions/zupt.fish
)
for file in "${completion_files[@]}"; do
[[ -f $file ]] && pass "$file exists" || fail "$file is missing"
done
if bash -n completions/zupt.bash; then
pass 'bash completion parses'
else
F "completions/vaptvupt.bash missing"
fail 'bash completion has a syntax error'
fi
# ─── Zsh completion ───
if [ -f completions/_vaptvupt ]; then
if command -v zsh >/dev/null 2>&1; then
if zsh -n completions/_vaptvupt 2>/dev/null; then
P "zsh completion: syntax clean"
else
F "zsh completion: syntax error"
fi
if command -v zsh >/dev/null 2>&1; then
if zsh -n completions/_zupt; then
pass 'zsh completion parses'
else
SKIP "zsh not installed — skipping syntax check"
fi
# Should have #compdef directive
if grep -qE "^#compdef vaptvupt( zupt)?$" completions/_vaptvupt; then
P "zsh completion: has #compdef vaptvupt directive"
else
F "zsh completion: missing #compdef directive"
fail 'zsh completion has a syntax error'
fi
else
F "completions/_vaptvupt missing"
skip 'zsh is unavailable'
fi
# ─── Fish completion ───
if [ -f completions/vaptvupt.fish ]; then
if command -v fish >/dev/null 2>&1; then
if fish -n completions/vaptvupt.fish 2>/dev/null; then
P "fish completion: syntax clean"
else
F "fish completion: syntax error"
fi
if command -v fish >/dev/null 2>&1; then
if fish -n completions/zupt.fish; then
pass 'fish completion parses'
else
SKIP "fish not installed — skipping syntax check"
fi
# Should have complete -c zupt entries
if grep -qE "^complete -c (vaptvupt|zupt)" completions/vaptvupt.fish; then
P "fish completion: has complete -c vaptvupt entries"
else
F "fish completion: no complete -c vaptvupt entries"
fail 'fish completion has a syntax error'
fi
else
F "completions/vaptvupt.fish missing"
skip 'fish is unavailable'
fi
# ─── Flag-coverage check (across all three completion files) ───
# Every flag the binary actually parses should appear in every completion file.
# Each completion format has its own way of writing long options:
# bash: --flag
# zsh: --flag
# fish: -l flag (or --flag in comments)
critical_flags=(kdf comment comment-file pq pq-sdk dedup solid verbose quiet threads level block store fast lzhp vaptvupt)
if grep -qxF 'complete -F _zupt zupt' completions/zupt.bash &&
! grep -Eq '^complete[[:space:]].*[[:space:]]vaptvupt([[:space:]]|$)' completions/zupt.bash; then
pass 'bash registers only zupt'
else
fail 'bash completion is not limited to the primary zupt command'
fi
for f in completions/vaptvupt.bash completions/_vaptvupt; do
[ -f "$f" ] || continue
name=$(basename "$f")
missing=""
for flag in "${critical_flags[@]}"; do
if ! grep -qF -- "--$flag" "$f"; then
missing="$missing --$flag"
if [[ $(sed -n '1p' completions/_zupt) == '#compdef zupt' ]]; then
pass 'zsh registers only zupt'
else
fail 'zsh #compdef is not limited to zupt'
fi
if grep -q '^complete -c zupt' completions/zupt.fish &&
! grep -q '^complete -c vaptvupt\([[:space:]]\|$\)' completions/zupt.fish; then
pass 'fish registers only zupt'
else
fail 'fish completion is not limited to the primary zupt command'
fi
required_flags=(
password-prompt pass-file pass-fd allow-legacy-no-ait kdf comment comment-file
pq pq-only pq-sdk pq-box dedup solid force verbose threads
level block store fast lzhp vaptvupt compare output key pub
sdk box pqonly help version
)
for file in "${completion_files[@]}"; do
missing=()
for flag in "${required_flags[@]}"; do
if ! grep -qF -- "--$flag" "$file" &&
! grep -qE -- "-l[[:space:]]+$flag([[:space:]]|$)" "$file"; then
missing+=("--$flag")
fi
done
if [ -z "$missing" ]; then
P "$name: covers all ${#critical_flags[@]} critical flags"
if ((${#missing[@]} == 0)); then
pass "$file covers current critical flags"
else
F "$name: missing flags:$missing"
fail "$file is missing: ${missing[*]}"
fi
done
if [ -f completions/vaptvupt.fish ]; then
name="vaptvupt.fish"
missing=""
for flag in "${critical_flags[@]}"; do
# fish uses `-l flag-name` for long opts
if ! grep -qE -- "(-l $flag|--$flag)" completions/vaptvupt.fish; then
missing="$missing $flag"
unsupported_flags=(quiet jobs codec keyfile sync no-mtime strip-components block-size)
for file in "${completion_files[@]}"; do
advertised=()
for flag in "${unsupported_flags[@]}"; do
if grep -qF -- "--$flag" "$file" ||
grep -qE -- "-l[[:space:]]+$flag([[:space:]]|$)" "$file"; then
advertised+=("--$flag")
fi
done
if [ -z "$missing" ]; then
P "$name: covers all ${#critical_flags[@]} critical flags (via -l form)"
if ((${#advertised[@]} == 0)); then
pass "$file does not advertise unsupported flags"
else
F "$name: missing flags:$missing"
fail "$file advertises unsupported flags: ${advertised[*]}"
fi
fi
done
# ─── Manpage refresh ───
if [ -f doc/zupt.1 ]; then
# v2.4.x features must be mentioned. Use shell-friendly regexes that
# match groff's `\-\-` escape (literal backslash, dash, backslash, dash).
declare -a manpage_checks=(
"kdf:--kdf option"
"comment:--comment option"
"argon2id:Argon2id KDF"
"Argon2id:Argon2id KDF (capital)"
"verbal probe-oracle:F-11 message change"
"ML-KEM-768:post-quantum KEM"
)
manpage_misses=0
for entry in "${manpage_checks[@]}"; do
key="${entry%%:*}"
desc="${entry#*:}"
if grep -qF "$key" doc/zupt.1; then
:
else
F "manpage: doesn't mention '$desc' (looking for '$key')"
manpage_misses=$((manpage_misses+1))
fi
done
# Two additional checks for groff-escaped hyphens (--comment-file, --pq-sdk
# render as `\-\-comment\-file` and `\-\-pq\-sdk` in the source)
if grep -qE "comment\\\\-file|comment-file" doc/zupt.1; then
:
else
F "manpage: doesn't mention --comment-file (looking for comment\\-file or comment-file)"
manpage_misses=$((manpage_misses+1))
fi
if grep -qE "pq\\\\-sdk|pq-sdk" doc/zupt.1; then
:
else
F "manpage: doesn't mention --pq-sdk (looking for pq\\-sdk or pq-sdk)"
manpage_misses=$((manpage_misses+1))
fi
if [ "$manpage_misses" = 0 ]; then
P "manpage: mentions all v2.4.x features"
fi
# Required sections
for section in NAME SYNOPSIS DESCRIPTION COMMANDS EXAMPLES; do
if grep -qE "^\.SH $section" doc/zupt.1; then
:
else
F "manpage: missing section '.SH $section'"
fi
done
P "manpage: required sections present"
# Version header
if grep -qE "\"(vaptvupt|zupt) $VERSION\"" doc/zupt.1; then
P "manpage: TH version matches include/zupt.h ($VERSION)"
else
F "manpage: TH version doesn't match include/zupt.h"
fi
# Try to render with groff if available
if command -v groff >/dev/null 2>&1; then
if groff -mandoc -Tutf8 doc/zupt.1 > /tmp/render.txt 2>/tmp/groff_warn.txt; then
LINES=$(wc -l < /tmp/render.txt)
if [ "$LINES" -gt 50 ]; then
P "manpage: renders cleanly with groff ($LINES lines)"
else
F "manpage: groff produced suspiciously short output ($LINES lines)"
fi
else
F "manpage: groff rendering failed"
fi
rm -f /tmp/render.txt /tmp/groff_warn.txt
elif command -v mandoc >/dev/null 2>&1; then
if mandoc -Tlint doc/zupt.1 >/tmp/mandoc.out 2>&1; then
P "manpage: mandoc lint clean"
else
P "manpage: mandoc lint had warnings (acceptable)"
fi
rm -f /tmp/mandoc.out
else
SKIP "no groff or mandoc — skipping render lint"
fi
if [[ ! -e doc/vaptvupt.1 && ! -L doc/vaptvupt.1 ]]; then
pass 'former primary man page is absent from the source tree'
else
F "doc/zupt.1 missing"
fail 'doc/vaptvupt.1 remains despite the zupt-only default installation'
fi
echo ""
echo " ───────────────────────────────────────"
echo " completions + manpage: $PASS passed, $FAIL failed"
echo " ───────────────────────────────────────"
[ "$FAIL" = 0 ] || exit 1
manpage=doc/zupt.1
if [[ ! -f $manpage ]]; then
fail "$manpage is missing"
else
required_sections=(NAME SYNOPSIS DESCRIPTION COMMANDS PASSWORD\ INPUT EXAMPLES EXIT\ STATUS LICENSE)
missing_sections=()
for section in "${required_sections[@]}"; do
grep -qxF ".SH $section" "$manpage" || missing_sections+=("$section")
done
if ((${#missing_sections[@]} == 0)); then
pass 'manpage contains required sections'
else
fail "manpage is missing sections: ${missing_sections[*]}"
fi
if grep -qF "ZUPT $version" "$manpage"; then
pass 'manpage version matches include/zupt.h'
else
fail 'manpage version does not match include/zupt.h'
fi
required_man_flags=(
password-prompt pass-file pass-fd allow-legacy-no-ait kdf comment comment-file
pq pq-only pq-sdk pq-box dedup solid force verbose threads
level block store fast lzhp vaptvupt compare output key pub
sdk box pqonly help version
)
missing=()
for flag in "${required_man_flags[@]}"; do
grep -qF -- "--$flag" "$manpage" || missing+=("--$flag")
done
if ((${#missing[@]} == 0)); then
pass 'manpage documents current critical flags'
else
fail "manpage is missing: ${missing[*]}"
fi
advertised=()
for flag in "${unsupported_flags[@]}"; do
grep -qF -- "--$flag" "$manpage" && advertised+=("--$flag")
done
if ((${#advertised[@]} == 0)); then
pass 'manpage does not document unsupported flags'
else
fail "manpage documents unsupported flags: ${advertised[*]}"
fi
if grep -qF 'Plain archives provide compression checksums' "$manpage" &&
grep -qF 'does not restore ownership' "$manpage" &&
grep -qF 'Automatic codec selection' "$manpage"; then
pass 'manpage states current integrity, metadata, and codec behavior'
else
fail 'manpage is missing current behavioral limits'
fi
if grep -q '^\.B 2$\|^\.B 3$\|^\.B 4$\|^\.B 5$' "$manpage"; then
fail 'manpage advertises exit statuses not emitted by the CLI'
else
pass 'manpage documents only emitted exit statuses'
fi
lint_tmp=$(mktemp -d "${TMPDIR:-/tmp}/zupt-man-lint.XXXXXXXX")
trap 'rm -rf -- "$lint_tmp"' EXIT HUP INT TERM
if command -v mandoc >/dev/null 2>&1; then
if mandoc -Tlint "$manpage" >"$lint_tmp/mandoc.log" 2>&1; then
pass 'mandoc lint passes'
else
fail 'mandoc lint reports diagnostics'
sed -n '1,10p' "$lint_tmp/mandoc.log"
fi
elif command -v groff >/dev/null 2>&1; then
if groff -mandoc -Tutf8 "$manpage" >"$lint_tmp/rendered" 2>"$lint_tmp/groff.log" &&
[[ ! -s $lint_tmp/groff.log ]] &&
(($(wc -l <"$lint_tmp/rendered") > 50)); then
pass 'groff renders the manpage without diagnostics'
else
fail 'groff manpage rendering failed or emitted diagnostics'
sed -n '1,10p' "$lint_tmp/groff.log"
fi
else
skip 'mandoc and groff are unavailable'
fi
rm -rf -- "$lint_tmp"
trap - EXIT HUP INT TERM
fi
printf '\nSummary: PASS=%d FAIL=%d SKIP=%d\n' "$pass_count" "$fail_count" "$skip_count"
((fail_count == 0))