updatedb refuses to run at all on a config that defines a variable twice
("variable `PRUNEPATHS' was already defined"), so any rewrite that misses
an existing line and appends a second one takes the locate index down
rather than fixing it. Two shapes updatedb accepts got missed: a trailing
comment after the value, and a setting indented by whitespace.
Read the existing paths out of the quoted value and write the whole
setting back canonically instead of splicing into a line of unknown
shape. Quotes are not optional to updatedb ("value in quotes expected
after `='"), so a bare value is already a broken config: rewriting it
quoted repairs the file as a side effect.
The tests now hand every rewritten file to the real parser through
`updatedb --config-file`, which is what caught this.
Read the Snapper config as root when the running user cannot read it.
snapper create-config leaves the config root-only, and a config the user
cannot read was passing for one that wants its timeline snapshots kept.
Report the snapshots the drain could not delete. omarchy-migrate writes
the completion marker whether or not the batches succeeded, so there is
no later run to pick up the remainder, whatever the comment claimed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
127 lines
4.6 KiB
Bash
127 lines
4.6 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
leak_migration=$(grep -rl 'timeline snapshots leaked by earlier defaults' "$ROOT/migrations" | head -n 1 || true)
|
|
[[ -n $leak_migration ]] || fail "Snapper timeline leak migration exists"
|
|
|
|
test_tmp=$(mktemp -d)
|
|
trap 'rm -rf "$test_tmp"' EXIT
|
|
|
|
fake_bin="$test_tmp/bin"
|
|
mkdir -p "$fake_bin"
|
|
|
|
cat >"$fake_bin/sudo" <<'STUB'
|
|
#!/bin/bash
|
|
printf 'sudo %s\n' "$*" >>"$TEST_LOG"
|
|
exec "$@"
|
|
STUB
|
|
chmod +x "$fake_bin/sudo"
|
|
|
|
cat >"$fake_bin/snapper" <<'STUB'
|
|
#!/bin/bash
|
|
printf 'snapper %s\n' "$*" >>"$TEST_LOG"
|
|
if [[ "$*" == *"--csvout list"* ]]; then
|
|
echo "number,cleanup"
|
|
for i in $(seq 1 45); do
|
|
echo "$i,timeline"
|
|
done
|
|
echo "100,number"
|
|
echo "101,"
|
|
fi
|
|
STUB
|
|
chmod +x "$fake_bin/snapper"
|
|
|
|
snapper_config="$test_tmp/root"
|
|
printf '%s\n' 'TIMELINE_CREATE="no"' 'NUMBER_CLEANUP="yes"' >"$snapper_config"
|
|
|
|
TEST_LOG="$test_tmp/calls.log" \
|
|
PATH="$fake_bin:$PATH" \
|
|
OMARCHY_SNAPPER_CONFIG_PATH="$snapper_config" \
|
|
bash -euo pipefail "$leak_migration" >/dev/null
|
|
|
|
deletes=$(grep -c '^snapper -c root delete ' "$test_tmp/calls.log" || true)
|
|
[[ $deletes -eq 3 ]] || fail "leak migration deletes snapshots in batches" "expected 3 delete calls, got $deletes"
|
|
|
|
first_batch=$(grep -m1 '^snapper -c root delete ' "$test_tmp/calls.log")
|
|
[[ $first_batch == "snapper -c root delete $(seq -s ' ' 1 20)" ]] || fail "leak migration caps delete batches at 20 snapshots" "$first_batch"
|
|
|
|
last_batch=$(grep '^snapper -c root delete ' "$test_tmp/calls.log" | tail -n 1)
|
|
[[ $last_batch == "snapper -c root delete $(seq -s ' ' 41 45)" ]] || fail "leak migration deletes the final partial batch" "$last_batch"
|
|
|
|
! grep -E '^snapper -c root delete .*\b(100|101)\b' "$test_tmp/calls.log" || fail "leak migration only deletes timeline snapshots"
|
|
pass "leak migration removes leaked timeline snapshots in batches and keeps the rest"
|
|
|
|
# omarchy-migrate runs under set -e, so a batch that dies on a DBus timeout
|
|
# would otherwise abort the run and skip every migration queued behind it.
|
|
: >"$test_tmp/calls.log"
|
|
printf '%s\n' 'TIMELINE_CREATE="no"' 'NUMBER_CLEANUP="yes"' >"$snapper_config"
|
|
|
|
cat >"$fake_bin/snapper" <<'STUB'
|
|
#!/bin/bash
|
|
printf 'snapper %s\n' "$*" >>"$TEST_LOG"
|
|
if [[ "$*" == *"--csvout list"* ]]; then
|
|
echo "number,cleanup"
|
|
for i in $(seq 1 45); do
|
|
echo "$i,timeline"
|
|
done
|
|
exit 0
|
|
fi
|
|
echo "failure: dbus timeout" >&2
|
|
exit 1
|
|
STUB
|
|
|
|
output=$(TEST_LOG="$test_tmp/calls.log" \
|
|
PATH="$fake_bin:$PATH" \
|
|
OMARCHY_SNAPPER_CONFIG_PATH="$snapper_config" \
|
|
bash -euo pipefail "$leak_migration" 2>/dev/null) ||
|
|
fail "leak migration survives a failed delete batch"
|
|
|
|
deletes=$(grep -c '^snapper -c root delete ' "$test_tmp/calls.log" || true)
|
|
[[ $deletes -eq 3 ]] || fail "leak migration keeps draining after a failed batch" "expected 3 delete calls, got $deletes"
|
|
|
|
# omarchy-migrate writes the completion marker even when the drain gave up, so
|
|
# what is left has to be said out loud rather than left for a rerun.
|
|
grep -qF '45 snapshots could not be deleted' <<<"$output" || fail "leak migration reports the snapshots it could not delete" "$output"
|
|
pass "leak migration tolerates a batch that fails partway"
|
|
|
|
: >"$test_tmp/calls.log"
|
|
printf '%s\n' 'TIMELINE_CREATE="yes"' >"$snapper_config"
|
|
|
|
TEST_LOG="$test_tmp/calls.log" \
|
|
PATH="$fake_bin:$PATH" \
|
|
OMARCHY_SNAPPER_CONFIG_PATH="$snapper_config" \
|
|
bash -euo pipefail "$leak_migration" >/dev/null
|
|
|
|
[[ ! -s $test_tmp/calls.log ]] || fail "leak migration leaves deliberate timeline setups alone"
|
|
pass "leak migration skips systems where timeline snapshots are intentional"
|
|
|
|
: >"$test_tmp/calls.log"
|
|
|
|
TEST_LOG="$test_tmp/calls.log" \
|
|
PATH="$fake_bin:$PATH" \
|
|
OMARCHY_SNAPPER_CONFIG_PATH="$test_tmp/missing" \
|
|
bash -euo pipefail "$leak_migration" >/dev/null
|
|
|
|
[[ ! -s $test_tmp/calls.log ]] || fail "leak migration skips systems without a Snapper root config"
|
|
pass "leak migration is a no-op without Snapper configured"
|
|
|
|
# Snapper's create-config writes a root-only config, and a config this user
|
|
# cannot read says nothing about whether timeline snapshots are wanted.
|
|
: >"$test_tmp/calls.log"
|
|
printf '%s\n' 'TIMELINE_CREATE="no"' >"$snapper_config"
|
|
chmod 000 "$snapper_config"
|
|
|
|
TEST_LOG="$test_tmp/calls.log" \
|
|
PATH="$fake_bin:$PATH" \
|
|
OMARCHY_SNAPPER_CONFIG_PATH="$snapper_config" \
|
|
bash -euo pipefail "$leak_migration" >/dev/null 2>&1
|
|
|
|
chmod 600 "$snapper_config"
|
|
|
|
grep -qF "sudo grep -qFx TIMELINE_CREATE=\"no\" $snapper_config" "$test_tmp/calls.log" ||
|
|
fail "leak migration reads a root-only Snapper config as root" "$(cat "$test_tmp/calls.log")"
|
|
pass "leak migration does not mistake an unreadable Snapper config for an intentional one"
|