Give an installed machine a way to catch up
./install only ever adds. It copies over /, links dotfiles, installs packages -- and has no way to say "remove that file", "disable that unit", "that symlink points nowhere now". So a machine set up months ago keeps whatever this repository has since decided was wrong, and the only thing that ever fixes it is somebody reading a commit message. With a curl installer in the README, that stopped being hypothetical. A migration is one script that performs one repair, exactly once, on the machines that need it. Named by the commit timestamp that authored it, so glob order is chronological without a sequence number two branches could both pick. Marked in ~/.local/state on success and only on success, so a repair that failed stays pending rather than being recorded as done and hidden forever. Ordered, and stopped at the first failure, because a later repair may assume an earlier one landed. A fresh install marks everything without running it, the way Migrations.qml stamps a pre-versioning settings file at its baseline. The first real one removes the dangling ~/.config/forge symlink left behind when the GNOME session was cut: link-dotfiles could link it but never unlink it. Verified both ways -- a no-op on a machine that never had it, an actual repair on one that did. Root work goes through panama-sudo --reason so the password prompt names the repair, and the contract fails any migration reaching for bare sudo.
This commit is contained in:
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Stamps a new migration, so the name is never chosen by hand.
|
||||
#
|
||||
# panama-dev-migration "remove the stale settings-ssh-keys.sh launcher command"
|
||||
#
|
||||
# The filename is the commit timestamp of HEAD, which makes glob order
|
||||
# chronological without a sequence number that two branches could pick at the
|
||||
# same time. Two migrations authored against the same commit would collide, so
|
||||
# a taken name gets the next free second rather than silently overwriting.
|
||||
#
|
||||
# Developer tool, not part of any install path. See bin/panama-migrate for what
|
||||
# runs these and what rules they have to follow.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PANAMA_PATH="${PANAMA_PATH:-$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)}"
|
||||
MIGRATIONS_DIR="$PANAMA_PATH/migrations"
|
||||
|
||||
describe="${1:-}"
|
||||
if [[ -z "$describe" ]]; then
|
||||
echo 'usage: panama-dev-migration "what this repairs"' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
stamp="$(git -C "$PANAMA_PATH" log -1 --format=%cd --date=unix 2>/dev/null || date +%s)"
|
||||
mkdir -p "$MIGRATIONS_DIR"
|
||||
while [[ -e "$MIGRATIONS_DIR/$stamp.sh" ]]; do
|
||||
stamp=$(( stamp + 1 ))
|
||||
done
|
||||
file="$MIGRATIONS_DIR/$stamp.sh"
|
||||
|
||||
cat >"$file" <<EOF
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# $describe
|
||||
#
|
||||
# Rules, because the runner cannot enforce them:
|
||||
#
|
||||
# * Safe to run twice. The marker records success, not intent.
|
||||
# * Tolerant of the repair already being correct -- the user may have fixed
|
||||
# it by hand, or a later ./install may have put it back.
|
||||
# * Root work goes through \`panama-sudo --reason "..."\`, never bare sudo,
|
||||
# so the password prompt names the repair.
|
||||
# * Exit non-zero to be retried at the next login. Exit zero only when the
|
||||
# machine is genuinely in the state this describes.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PANAMA_PATH="\${PANAMA_PATH:-\$HOME/.local/share/Panama}"
|
||||
|
||||
# ... the repair goes here.
|
||||
EOF
|
||||
|
||||
chmod +x "$file"
|
||||
printf 'Created %s\n' "$file"
|
||||
Reference in New Issue
Block a user