diff --git a/bin/omarchy-update b/bin/omarchy-update index b45072aa..6c678755 100755 --- a/bin/omarchy-update +++ b/bin/omarchy-update @@ -22,6 +22,10 @@ trap 'omarchy-update-stay-awake stop' EXIT omarchy-update-requires-free-space if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then + # Before the snapshot: the cache is on the snapshotted subvolume, so pruning + # after it frees nothing until that snapshot ages out. + omarchy-update-pkg-prune + # 127 means Snapper is deliberately absent. Any other failure already said # what went wrong, and a missing snapshot is not worth blocking an update # over, but it must not pass for one either. diff --git a/bin/omarchy-update-pkg-prune b/bin/omarchy-update-pkg-prune new file mode 100755 index 00000000..3f53d853 --- /dev/null +++ b/bin/omarchy-update-pkg-prune @@ -0,0 +1,12 @@ +#!/bin/bash + +# omarchy:summary=Prune superseded versions from the pacman package cache +# omarchy:requires-sudo=true + +set -e + +# The cache is the only offline downgrade path, so keep two. Run before the +# packages update and the installed version survives with a spare. paccache +# retains by version order, never by what is installed. +echo -e "\e[32m\nPrune package cache\e[0m" +sudo paccache -rk2 || echo -e "\e[33mCould not prune the package cache.\e[0m" >&2 diff --git a/test/shell.d/update-disk-space-test.sh b/test/shell.d/update-disk-space-test.sh index e99966ae..2c7368ea 100644 --- a/test/shell.d/update-disk-space-test.sh +++ b/test/shell.d/update-disk-space-test.sh @@ -68,6 +68,7 @@ for command in \ pkexec \ systemd-inhibit \ omarchy-update-dev \ + omarchy-update-pkg-prune \ omarchy-update-keyring \ omarchy-update-system-pkgs \ omarchy-migrate \ diff --git a/test/shell.d/update-lock-test.sh b/test/shell.d/update-lock-test.sh index ed195fc9..6c65428d 100644 --- a/test/shell.d/update-lock-test.sh +++ b/test/shell.d/update-lock-test.sh @@ -35,6 +35,7 @@ for command in \ omarchy-toggle-idle \ pkexec \ systemd-inhibit \ + omarchy-update-pkg-prune \ omarchy-update-dev \ omarchy-update-keyring \ omarchy-update-system-pkgs \ diff --git a/test/shell.d/update-pkg-prune-test.sh b/test/shell.d/update-pkg-prune-test.sh new file mode 100755 index 00000000..0709fa3d --- /dev/null +++ b/test/shell.d/update-pkg-prune-test.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +set -euo pipefail + +source "$(dirname "$0")/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +stub_bin="$test_tmp/bin" +mkdir -p "$stub_bin" + +write_stub() { + local name="$1" + local body="$2" + + cat >"$stub_bin/$name" <"$PACCACHE_LOG"; exit 0' + +PACCACHE_LOG="$test_tmp/args" run_pkg_prune >"$test_tmp/prune.out" 2>&1 +grep -q 'paccache' "$test_tmp/args" || fail "cache prune runs paccache" +grep -qE 'paccache .*-rk2' "$test_tmp/args" || + fail "cache prune keeps more than one version" "$(cat "$test_tmp/args")" +pass "cache prune leaves a rollback version to spare" + +# Housekeeping failure must not abort the update. +write_stub sudo 'exit 1' +run_pkg_prune >"$test_tmp/fail.out" 2>&1 || + fail "cache prune survives paccache failure" +grep -q 'Could not prune the package cache' "$test_tmp/fail.out" || + fail "cache prune warns when it fails" "$(cat "$test_tmp/fail.out")" +pass "cache prune warns but does not abort the update" + +# Ordering is the whole guarantee: rollback before the packages update, space +# before the snapshot. +line_of() { + grep -n "^[[:space:]]*$1\b" "$ROOT/bin/omarchy-update" | head -1 | cut -d: -f1 +} + +prune_line=$(line_of omarchy-update-pkg-prune) +snapshot_line=$(line_of omarchy-snapshot) +pkgs_line=$(line_of omarchy-update-system-pkgs) +[[ -n $prune_line && -n $snapshot_line && -n $pkgs_line ]] || + fail "omarchy-update runs the cache prune, the snapshot, and the packages update" + +(( prune_line < pkgs_line )) || + fail "cache prune runs before the packages update" "prune: $prune_line, packages: $pkgs_line" +pass "cache prune runs before the packages update" + +(( prune_line < snapshot_line )) || + fail "cache prune runs before the snapshot" "prune: $prune_line, snapshot: $snapshot_line" +pass "cache prune runs before the snapshot pins what it removes"