./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.
57 lines
1.8 KiB
Bash
Executable File
57 lines
1.8 KiB
Bash
Executable File
#!/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"
|