Files
dotfiles/test/verify_packages.sh
T

121 lines
4.1 KiB
Bash
Executable File

#!/bin/bash
# test/verify_packages.sh
#
# Post-install verification of the packages the smoke test installs.
# Given a package-list file (one package per line, `@section` markers
# allowed - only the section content is verified), it checks that every
# listed package is:
# 1. installed in the pacman DB -> `pacman -Q <pkg>`
# 2. its expected binary exists and reports a version (curated mapping)
#
# Layer 2 runs a real `--version`-style probe for packages that install a
# binary, so a broken/renamed executable is caught even if pacman reports
# the package as installed. Fonts/libs/modules without a binary are verified
# by Layer 1 only.
#
# Usage: bash test/verify_packages.sh <package-list-file>
set -uo pipefail
PKGS_FILE="${1:?usage: verify_packages.sh <package-list-file>}"
[[ -f "$PKGS_FILE" ]] || { echo "verify_packages.sh: not a file: $PKGS_FILE" >&2; exit 1; }
PASS=0
FAIL=0
say_ok() { printf ' ok: %s\n' "$*"; PASS=$((PASS+1)); }
say_fail() { printf ' FAIL: %s\n' "$*"; FAIL=$((FAIL+1)); }
# ---------------------------------------------------------------------------
# Curated mapping: package -> "binary version_flag"
# - packages not listed here are checked with pacman -Q only
# - flag missing (empty) => existence check only
# ---------------------------------------------------------------------------
declare -A BINS=(
[fish]="fish --version"
[kitty]="kitty --version"
[neovim]="nvim --version"
[openssh]="ssh -V"
[rclone]="rclone --version"
[rsync]="rsync --version"
[unison]="unison -version"
[stow]="stow --version"
[nodejs]="node --version"
[npm]="npm --version"
[starship]="starship --version"
[fastfetch]="fastfetch --version"
[tree-sitter-cli]="tree-sitter --version"
[python]="python --version"
[python-pip]="pip3 --version"
[unzip]="unzip --version"
[selene]="selene --version"
[tmux]="tmux -V"
[yazi]="yazi --version"
[zoxide]="zoxide --version"
[fzf]="fzf --version"
[chromium]="chromium --version"
[locate]="locate --version"
)
# python-pip may expose `pip` instead of `pip3` on some setups; treat them
# as interchangeable so the probe does not spuriously fail.
probe_pip() {
for b in pip3 pip; do
if command -v "$b" >/dev/null 2>&1; then
out=$("$b" --version 2>&1) && [[ -n "$out" ]] && { say_ok "$b --version ($out)"; return 0; }
fi
done
say_fail "python-pip: neither pip3 nor pip found/probe failed"
return 1
}
echo "== Verifying installed packages from: $PKGS_FILE =="
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line%%#*}" # strip trailing comments
line="${line// /}" # drop inline whitespace
[[ -z "$line" ]] && continue
[[ "$line" == @* ]] && continue # skip @pacman/@aur/@npm section markers
pkg="${line#*/}" # strip repo prefix (extra/kitty -> kitty)
if [[ -z "$pkg" ]]; then
say_fail "empty package name in '$line'"
continue
fi
# Layer 1: installed in the pacman DB?
if pacman -Q "$pkg" >/dev/null 2>&1; then
say_ok "pacman -Q $pkg (installed)"
else
say_fail "pacman -Q $pkg (not installed)"
continue
fi
# Layer 2: runtime binary probe (curated mapping)?
spec="${BINS[$pkg]:-}"
[[ -z "$spec" ]] && continue # no binary (font/lib/module) - Layer 1 only
# python-pip has special multi-candidate handling
[[ "$pkg" == "python-pip" ]] && { probe_pip; continue; }
read -r bin flag <<< "$spec"
if command -v "$bin" >/dev/null 2>&1; then
if [[ -z "$flag" ]]; then
say_ok "command -v $bin (present)"
else
out=$("$bin" $flag 2>&1) && [[ -n "$out" ]]
if [[ $? -eq 0 ]]; then
say_ok "$bin $flag ($out)"
else
say_fail "$bin $flag (binary present but probe failed)"
fi
fi
else
say_fail "command -v $bin (expected from package '$pkg')"
fi
done < "$PKGS_FILE"
echo
echo "----------------------------------------------"
echo "verify_packages.sh: $PASS passed, $FAIL failed"
echo "----------------------------------------------"
[[ $FAIL -eq 0 ]]