diff options
| author | Craig Jennings <c@cjennings.net> | 2026-08-07 13:41:10 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-08-07 13:41:10 -0500 |
| commit | 5b48bef697a6c98de9b1336ad5a36ad7341a639d (patch) | |
| tree | 490b90f704148e29b4509af4218e629aa77d8419 | |
| parent | 14c08b4706a486ec080de606ccf1a7c2acd672b6 (diff) | |
| download | archangel-5b48bef697a6c98de9b1336ad5a36ad7341a639d.tar.gz archangel-5b48bef697a6c98de9b1336ad5a36ad7341a639d.zip | |
archzfs re-uploads its GitHub Releases assets under the same filenames, so pacoloco can hold package bodies that no longer match what its archzfs.db advertises. Every ZFS scenario then dies at pacstrap with "invalid or corrupted package", which reads exactly like an installer regression. It went stale twice on 2026-08-06, the second time mid-run: fresh at scenario one, rotten by scenario six, and the ZFS half of the suite was lost.
The whole directory goes, not the zfs-dkms and zfs-utils globs build.sh uses. Removing the bodies while leaving a stale db trades a checksum error for "Maximum file size exceeded", which is how the second failure passed itself off as a new bug.
Removal needs root, so it degrades to a warning rather than failing the run. The path is injectable because the first version hardcoded it, and running the unit suite then invoked sudo rm -rf against the live cache. A shape check guards the override, since this is rm -rf under sudo and a mistyped path should cost a warning instead of the machine.
| -rwxr-xr-x | scripts/test-install.sh | 47 | ||||
| -rw-r--r-- | tests/unit/test_test_install.bats | 69 |
2 files changed, 116 insertions, 0 deletions
diff --git a/scripts/test-install.sh b/scripts/test-install.sh index d61f95d..cbca9b3 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -100,6 +100,49 @@ list_configs() { done } +# Drop the cached archzfs repo before the first scenario. +# +# archzfs re-uploads its GitHub Releases assets under the same filenames, so +# pacoloco ends up holding package bodies that no longer match what its +# archzfs.db advertises. Every ZFS scenario then dies at pacstrap with +# "invalid or corrupted package" or "Maximum file size exceeded", which reads +# exactly like an installer regression and isn't one. It went stale twice on +# 2026-08-06, the second time *during* a run: fresh at scenario one, rotten by +# scenario six, and the whole ZFS half was lost. +# +# Clear the directory, not the zfs-dkms/zfs-utils globs build.sh uses. Removing +# the bodies while leaving a stale db just trades a checksum error for a size +# error, which is how the second failure disguised itself as a new bug. +# +# Best-effort by design. The cache is pacoloco-owned so removal needs root, and +# a run without root is still worth having — it just carries the risk this +# exists to remove, so say so rather than failing silently. +# +# The path is injectable so tests can exercise this against a temp directory. +# Without that it hardcoded a system path, and merely sourcing the file and +# calling the function ran `sudo rm -rf` on the real cache — which is what +# happened the first time this was written. +clear_archzfs_cache() { + local dir="${ARCHZFS_CACHE_DIR:-/var/cache/pacoloco/pkgs/archzfs}" + [[ -d "$dir" ]] || return 0 + + # This is `rm -rf` under sudo on an overridable path, so validate the shape + # before running it. A mistyped override should cost a warning, not the + # machine. + if [[ "$(basename "$dir")" != archzfs || "$(dirname "$dir")" == / ]]; then + warn "Refusing to clear implausible archzfs cache path: $dir" + return 0 + fi + + if sudo -n rm -rf "$dir" 2>/dev/null; then + info "Cleared cached archzfs repo (pacoloco refetches on first use)" + else + warn "Could not clear $dir — needs root." + warn "A stale archzfs cache fails every ZFS scenario at pacstrap; clear it by hand if that happens." + fi + return 0 +} + find_iso() { ISO_FILE=$(ls -t "$PROJECT_DIR/out/"*.iso 2>/dev/null | head -1) if [[ -z "$ISO_FILE" ]]; then @@ -1308,6 +1351,10 @@ main() { # Find ISO find_iso + # Before the first scenario, so a stale upstream re-upload can't fail every + # ZFS install with an error that looks like a regression. + clear_archzfs_cache + # Determine which configs to run if [[ ${#configs[@]} -eq 0 ]]; then # Run all configs diff --git a/tests/unit/test_test_install.bats b/tests/unit/test_test_install.bats index 34da2b0..52ea037 100644 --- a/tests/unit/test_test_install.bats +++ b/tests/unit/test_test_install.bats @@ -433,3 +433,72 @@ mkcfg() { SSH_CMD_TIMEOUT=1800 run ssh_cmd true [[ "$output" == "bound=1800" ]] } + +############################# +# clear_archzfs_cache +############################# +# Exercised against an injected directory, never the real one. An earlier +# version of this block hardcoded the system path, so running the unit suite +# invoked `sudo rm -rf /var/cache/pacoloco/pkgs/archzfs` on the live machine. +# A unit test must not reach outside its sandbox. + +@test "clear_archzfs_cache is a no-op when the cache directory is absent" { + ARCHZFS_CACHE_DIR="$BATS_TEST_TMPDIR/absent" run clear_archzfs_cache + [ "$status" -eq 0 ] + [ -z "$output" ] +} + +@test "clear_archzfs_cache removes an existing cache directory" { + local dir="$BATS_TEST_TMPDIR/archzfs" + mkdir -p "$dir" + touch "$dir/archzfs.db" "$dir/zfs-dkms-1-1-x86_64.pkg.tar.zst" + # Stub sudo so the test needs no privilege and stays in its sandbox. + sudo() { shift; "$@"; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + [ "$status" -eq 0 ] + [ ! -d "$dir" ] +} + +@test "clear_archzfs_cache clears the db too, not just the package bodies" { + # Removing bodies while leaving a stale archzfs.db trades a checksum error + # for "Maximum file size exceeded" — same cause, new message. + local dir="$BATS_TEST_TMPDIR/db/archzfs" + mkdir -p "$dir" + touch "$dir/archzfs.db" + sudo() { shift; "$@"; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + [ ! -e "$dir/archzfs.db" ] +} + +@test "clear_archzfs_cache warns and succeeds when removal is denied" { + local dir="$BATS_TEST_TMPDIR/denied/archzfs" + mkdir -p "$dir" + sudo() { return 1; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + # Non-fatal: a run without root is still worth having, but must say so. + [ "$status" -eq 0 ] + [[ "$output" == *"needs root"* ]] + [ -d "$dir" ] +} + +@test "clear_archzfs_cache refuses a path that isn't shaped like the cache" { + # The removal runs `rm -rf` under sudo, so a mistyped override must cost a + # warning rather than the machine. + local dir="$BATS_TEST_TMPDIR/not-the-cache" + mkdir -p "$dir" + sudo() { echo "SUDO RAN"; } + ARCHZFS_CACHE_DIR="$dir" run clear_archzfs_cache + [ "$status" -eq 0 ] + [[ "$output" == *"Refusing"* ]] + [[ "$output" != *"SUDO RAN"* ]] + [ -d "$dir" ] +} + +@test "clear_archzfs_cache refuses a top-level directory" { + sudo() { echo "SUDO RAN"; } + ARCHZFS_CACHE_DIR="/archzfs" run clear_archzfs_cache + # /archzfs won't exist, so this exits on the -d guard; the point is that + # neither guard lets a root-level path reach the removal. + [ "$status" -eq 0 ] + [[ "$output" != *"SUDO RAN"* ]] +} |
