Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion bin/omarchy-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,21 @@ create)
echo -e "\e[32mCreate system snapshot\e[0m"

for config in "${CONFIGS[@]}"; do
sudo snapper -c "$config" create -c number -d "$DESC"
if output=$(sudo snapper -c "$config" create -c number -d "$DESC" 2>&1); then
if [[ -n $output ]]; then
printf '%s\n' "$output"
fi
else
status=$?
printf '%s\n' "$output" >&2
if [[ $output == *".snapshots is not a btrfs subvolume"* ]]; then
echo "Find the affected SUBVOLUME with: sudo snapper -c \"$config\" get-config" >&2
echo "If its .snapshots is a regular directory, back it up and move it aside, then run:" >&2
echo " sudo btrfs subvolume create <subvolume>/.snapshots" >&2
echo "Replace <subvolume> with SUBVOLUME, restore the directory's ownership and permissions, then retry." >&2
fi
exit "$status"
fi
sudo snapper -c "$config" cleanup number
done

Expand Down
41 changes: 41 additions & 0 deletions test/shell.d/snapshot-create-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,47 @@ grep -qFx 'snapper -c root cleanup number' "$test_tmp/calls.log" ||
fail "snapshot create prunes older snapshots"
pass "snapshot create snapshots every configured Snapper config"

cat >"$fake_bin/snapper" <<'STUB'
#!/bin/bash
printf 'snapper %s\n' "$*" >>"$TEST_LOG"
if [[ $* == *"list-configs"* ]]; then
printf 'config,subvolume\n%s,/home\n' "$TEST_CONFIG"
elif [[ $* == *" create "* ]]; then
echo "$TEST_ERROR" >&2
exit 5
fi
STUB

for config in root home; do
: >"$test_tmp/calls.log"
status=0
stderr=$(TEST_LOG="$test_tmp/calls.log" TEST_CONFIG="$config" \
TEST_ERROR='IO Error (.snapshots is not a btrfs subvolume).' PATH="$fake_bin:$PATH" \
bash "$snapshot" create 2>&1 >/dev/null) || status=$?

(( status == 5 )) || fail "snapshot create preserves Snapper's failure status" "got $status"
grep -qF 'IO Error (.snapshots is not a btrfs subvolume).' <<<"$stderr" ||
fail "snapshot create preserves the original diagnostic" "$stderr"
grep -qF "sudo snapper -c \"$config\" get-config" <<<"$stderr" ||
fail "snapshot recovery identifies the affected config" "$stderr"
grep -qF 'back it up and move it aside' <<<"$stderr" ||
fail "snapshot recovery preserves existing snapshot data" "$stderr"
grep -qF 'sudo btrfs subvolume create <subvolume>/.snapshots' <<<"$stderr" ||
fail "snapshot recovery explains how to recreate the subvolume" "$stderr"
! grep -q ' cleanup ' "$test_tmp/calls.log" ||
fail "snapshot create does not prune after creation fails"
done
pass "snapshot create explains regular snapshot directories for the affected config"

status=0
stderr=$(TEST_LOG="$test_tmp/calls.log" TEST_CONFIG=root \
TEST_ERROR='IO Error (No space left on device).' PATH="$fake_bin:$PATH" \
bash "$snapshot" create 2>&1 >/dev/null) || status=$?
(( status == 5 )) || fail "snapshot create preserves unrelated failure status" "got $status"
[[ $stderr == 'IO Error (No space left on device).' ]] ||
fail "snapshot create does not suggest subvolume repairs for unrelated failures" "$stderr"
pass "snapshot create preserves unrelated errors without subvolume recovery advice"

# Snapper being deliberately absent is the one skip that stays quiet, and the
# update has to keep treating it as such.
cat >"$fake_bin/omarchy-cmd-missing" <<'STUB'
Expand Down