The pacman cache grows without bound across updates, and nothing in the update flow ever reclaimed it. On a machine that has been updating for a while it reaches several gigabytes of superseded versions that nothing will ever install again. Prune it with paccache -rk2 as the first step of an update. Both halves of that placement are load-bearing. Keeping two versions rather than one preserves the rollback path. The cache is Arch's only offline downgrade: when an update breaks a single package, reinstalling its predecessor from here is the surgical fix, where a snapshot rollback would revert every other package too. Pruning before the packages update means the installed version is still the newest cached, so it survives along with a spare. Retention is by version order and never consults what is installed, so that holds while the installed version is among the two newest cached; a deliberate downgrade or repeated failed transactions can stack newer archives on top of it. Running before the snapshot is what actually frees the space. The cache sits on the snapshotted root subvolume, so a prune taken afterwards leaves the fresh snapshot holding those extents and reclaims nothing until it ages out of the number cleanup. A failed prune warns and continues. Cache housekeeping should not trip the update's ERR trap and tell the user their update went wrong. This runs after omarchy-update-requires-free-space, so it reclaims space during healthy updates but does not rescue a machine already under the 10 GiB gate. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
142 lines
4.6 KiB
Bash
142 lines
4.6 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(dirname "$0")/base-test.sh"
|
|
|
|
unset GUM_STATUS
|
|
unset OMARCHY_UPDATE_FORCE
|
|
unset TEST_AVAILABLE_BYTES
|
|
unset TEST_DF_INVALID
|
|
|
|
test_tmp=$(mktemp -d)
|
|
trap 'rm -rf "$test_tmp"' EXIT
|
|
|
|
stub_bin="$test_tmp/bin"
|
|
test_home="$test_tmp/home"
|
|
runtime_dir="$test_tmp/runtime"
|
|
snapshot_marker="$test_tmp/snapshot"
|
|
gum_marker="$test_tmp/gum"
|
|
mkdir -p "$stub_bin" "$test_home" "$runtime_dir"
|
|
|
|
run_update() {
|
|
HOME="$test_home" \
|
|
XDG_RUNTIME_DIR="$runtime_dir" \
|
|
PATH="$stub_bin:$ROOT/bin:$PATH" \
|
|
LC_ALL=C \
|
|
OMARCHY_UPDATE_LOGGED=1 \
|
|
TEST_AVAILABLE_BYTES=${TEST_AVAILABLE_BYTES:-$((9 * 1024 * 1024 * 1024))} \
|
|
TEST_DF_INVALID=${TEST_DF_INVALID:-0} \
|
|
SNAPSHOT_MARKER="$snapshot_marker" \
|
|
GUM_MARKER="$gum_marker" \
|
|
GUM_STATUS=${GUM_STATUS:-1} \
|
|
"$ROOT/bin/omarchy-update" "$@"
|
|
}
|
|
|
|
write_stub() {
|
|
local name="$1"
|
|
local body="$2"
|
|
|
|
cat >"$stub_bin/$name" <<SH
|
|
#!/bin/bash
|
|
$body
|
|
SH
|
|
chmod +x "$stub_bin/$name"
|
|
}
|
|
|
|
write_stub df '
|
|
if (( TEST_DF_INVALID )); then
|
|
printf "Avail\nunknown\n"
|
|
else
|
|
printf "Avail\n%s\n" "$TEST_AVAILABLE_BYTES"
|
|
fi'
|
|
|
|
write_stub gum '
|
|
printf "%s\n" "$*" >>"$GUM_MARKER"
|
|
if [[ ${1:-} == "confirm" ]]; then
|
|
exit "$GUM_STATUS"
|
|
fi
|
|
exit 0'
|
|
|
|
write_stub omarchy-snapshot '
|
|
touch "$SNAPSHOT_MARKER"
|
|
exit 0'
|
|
|
|
for command in \
|
|
omarchy-cmd-present \
|
|
omarchy-toggle-idle \
|
|
pkexec \
|
|
systemd-inhibit \
|
|
omarchy-update-dev \
|
|
omarchy-update-pkg-prune \
|
|
omarchy-update-keyring \
|
|
omarchy-update-system-pkgs \
|
|
omarchy-migrate \
|
|
omarchy-update-aur-pkgs \
|
|
omarchy-update-mise \
|
|
omarchy-update-orphan-pkgs \
|
|
omarchy-hook \
|
|
omarchy-update-analyze-logs \
|
|
omarchy-shell \
|
|
omarchy-update-restart; do
|
|
write_stub "$command" 'exit 0'
|
|
done
|
|
write_stub omarchy-update-available 'exit 1'
|
|
write_stub pkexec 'exec "$@"'
|
|
|
|
set +e
|
|
TEST_AVAILABLE_BYTES=$((9 * 1024 * 1024 * 1024)) \
|
|
PATH="$stub_bin:$ROOT/bin:$PATH" \
|
|
"$ROOT/bin/omarchy-update-requires-free-space" >/dev/null
|
|
status=$?
|
|
set -e
|
|
(( status == 1 )) || fail "free-space helper exits non-zero when disk space is low"
|
|
pass "free-space helper reports low disk space through its exit status"
|
|
|
|
set +e
|
|
output=$(run_update -y)
|
|
status=$?
|
|
set -e
|
|
(( status == 1 )) || fail "non-interactive update exits non-zero with low disk space"
|
|
[[ $output == *"You need at least 10 GiB free to safely update Omarchy."* ]] || fail "low disk space emits a warning"
|
|
[[ ! -f $gum_marker ]] || fail "non-interactive update does not prompt for low disk space"
|
|
[[ ! -f $snapshot_marker ]] || fail "non-interactive update stops before snapshotting with low disk space"
|
|
pass "non-interactive update stops with low disk space"
|
|
|
|
rm -f "$snapshot_marker" "$gum_marker"
|
|
set +e
|
|
output=$(run_update)
|
|
status=$?
|
|
set -e
|
|
(( status == 1 )) || fail "interactive update exits non-zero with low disk space"
|
|
[[ $output == *"You need at least 10 GiB free to safely update Omarchy."* ]] || fail "interactive low-space update explains the requirement"
|
|
[[ ! -f $gum_marker ]] || fail "interactive update stops before confirmation with low disk space"
|
|
[[ ! -f $snapshot_marker ]] || fail "interactive update stops before snapshotting with low disk space"
|
|
pass "interactive update stops before confirmation with low disk space"
|
|
|
|
rm -f "$snapshot_marker" "$gum_marker"
|
|
output=$(OMARCHY_UPDATE_FORCE=1 run_update -y)
|
|
[[ -z $output ]] || fail "forced update does not emit the free-space warning"
|
|
[[ ! -f $gum_marker ]] || fail "forced non-interactive update does not prompt"
|
|
[[ -f $snapshot_marker ]] || fail "forced update continues with low disk space"
|
|
pass "forced update skips the free-space requirement"
|
|
|
|
rm -f "$snapshot_marker" "$gum_marker"
|
|
output=$(TEST_AVAILABLE_BYTES=$((10 * 1024 * 1024 * 1024)) run_update -y)
|
|
[[ $output != *"You need at least 10 GiB free"* ]] || fail "space equal to the threshold does not produce a warning"
|
|
[[ -f $snapshot_marker ]] || fail "space equal to the threshold allows the update"
|
|
pass "disk-space threshold includes the exact boundary"
|
|
|
|
rm -f "$snapshot_marker" "$gum_marker"
|
|
GUM_STATUS=0 TEST_AVAILABLE_BYTES=$((10 * 1024 * 1024 * 1024)) run_update >/dev/null
|
|
grep -q "confirm Continue with update?" "$gum_marker" ||
|
|
fail "interactive update with enough space uses the normal confirmation prompt"
|
|
[[ -f $snapshot_marker ]] || fail "accepting the normal confirmation starts the update"
|
|
pass "interactive update keeps the normal confirmation prompt when space is sufficient"
|
|
|
|
rm -f "$snapshot_marker"
|
|
output=$(TEST_DF_INVALID=1 run_update -y)
|
|
[[ -z $output ]] || fail "failed disk-space detection remains silent"
|
|
[[ -f $snapshot_marker ]] || fail "failed disk-space detection does not block the update"
|
|
pass "failed disk-space detection silently continues"
|