install_phase.sh exited when it could not mark the backup `installing`, before the installer ran, without starting the monitor phase 1 had stopped (decision 2) and leaving the backup behind. Nothing was changed at that point, so it now removes the backup, restarts the monitor if it ran, releases the marker and exits 1. SKILL.md's exit row said 1 for every failure; a failed registry conversion after a good install passes the converter's nonzero code through, and a cancelled prompt exits 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
4.4 KiB
Bash
Executable File
130 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# install_phase.sh — multi-agent-mux-update, the part that changes the tree (Issue #72, decision 5).
|
|
#
|
|
# install_phase.sh run <workspace> <monitor_was_running 0|1> <tmpdir>
|
|
# install_phase.sh restore <workspace> [--skip-registry]
|
|
#
|
|
# update.sh copies this file outside the workspace and execs the copy (same
|
|
# pid and start time, so its .mam/update-in-progress marker stays live). From
|
|
# here on the tree is being replaced, so this calls no lib.sh or lib_py: it runs
|
|
# the installer in place, and on failure restores .mam/update-backup/ and starts
|
|
# the monitor again with the restored (old, whole) tree's start_watchdog. On
|
|
# success phase 2 is the NEW tree's post_update.sh, in a child process. It ends
|
|
# by removing the backup and the marker (only its own: pid + lstart).
|
|
# Must run on macOS bash 3.2.
|
|
set -uo pipefail
|
|
|
|
MODE="${1:-}"
|
|
WS="${2:-}"
|
|
[ -n "$MODE" ] && [ -d "$WS" ] || { echo "usage: $0 run|restore <workspace> ..." >&2; exit 2; }
|
|
BK="$WS/.mam/update-backup"
|
|
|
|
set_state() { printf '%s\n' "$1" > "$BK/state.tmp" && mv -f "$BK/state.tmp" "$BK/state"; }
|
|
|
|
# Puts back every backed-up item, removes the ones that did not exist before,
|
|
# then (unless --skip-registry) the registry: the DB from the backup, its
|
|
# -wal/-shm removed (macOS keeps them after close).
|
|
restore() {
|
|
local rel n
|
|
[ -f "$BK/items" ] || { echo "ERROR: $BK/items is missing" >&2; return 1; }
|
|
while IFS= read -r rel; do
|
|
[ -n "$rel" ] || continue
|
|
rm -rf "${WS:?}/$rel" || return 1
|
|
grep -Fqx -- "$rel" "$BK/absent" 2>/dev/null && continue
|
|
mkdir -p "$(dirname "$WS/$rel")" && cp -pPR "$BK/files/$rel" "$WS/$rel" || return 1
|
|
done < "$BK/items"
|
|
[ "${1:-}" = "--skip-registry" ] && return 0
|
|
for n in agent-sessions.yaml agent-sessions.yaml.bak agent-sessions.db; do
|
|
rm -f "$WS/.mam/$n" || return 1
|
|
if [ -f "$BK/registry/$n" ]; then
|
|
cp -p "$BK/registry/$n" "$WS/.mam/$n" || return 1
|
|
fi
|
|
done
|
|
rm -f "$WS/.mam/agent-sessions.db-wal" "$WS/.mam/agent-sessions.db-shm"
|
|
}
|
|
|
|
release_marker() {
|
|
local m="$WS/.mam/update-in-progress" pid lstart cur
|
|
[ -f "$m" ] || return 0
|
|
pid="$(grep '^pid=' "$m" | head -n 1 | cut -d= -f2-)"
|
|
lstart="$(grep '^lstart=' "$m" | head -n 1 | cut -d= -f2-)"
|
|
cur="$(ps -p $$ -o lstart= 2>/dev/null | tr -s ' ' | sed 's/^ *//;s/ *$//')"
|
|
if [ "$pid" = "$$" ] && [ "$lstart" = "$cur" ]; then
|
|
rm -f "$m"
|
|
fi
|
|
}
|
|
|
|
restart_old_monitor() {
|
|
[ "$1" = "1" ] || return 0
|
|
if [ "${MAM_NO_SUBSCRIBER_DAEMON:-0}" = "1" ]; then
|
|
echo "monitor: not restarted (MAM_NO_SUBSCRIBER_DAEMON=1)"
|
|
return 0
|
|
fi
|
|
local pid
|
|
if pid="$(bash -c 'source "$1/.agents/skills/lib.sh" && start_watchdog update "$1"' _ "$WS")"; then
|
|
echo "monitor: restarted (PID $pid)"
|
|
else
|
|
echo "ERROR: could not restart the monitor daemon" >&2
|
|
fi
|
|
}
|
|
|
|
case "$MODE" in
|
|
restore)
|
|
restore "${3:-}"
|
|
exit $?
|
|
;;
|
|
run) ;;
|
|
*) echo "usage: $0 run|restore <workspace> ..." >&2; exit 2 ;;
|
|
esac
|
|
|
|
MON="${3:-0}"
|
|
TMPD="${4:-}"
|
|
trap '[ -z "$TMPD" ] || rm -rf "$TMPD"' EXIT
|
|
|
|
if ! set_state installing; then
|
|
# Nothing was installed yet: drop the backup and put the monitor back as phase 1 found it.
|
|
echo "ERROR: cannot write $BK/state; nothing was changed" >&2
|
|
rm -rf "$BK"
|
|
restart_old_monitor "$MON"
|
|
release_marker
|
|
exit 1
|
|
fi
|
|
URL="${MAM_INSTALLER_URL:-https://git.godopu.com/tmpl/multi-agent-mux/raw/branch/main/deploy/install.sh}"
|
|
echo "install: $URL"
|
|
irc=0
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -fsSL "$URL" | bash -s -- "$WS" || irc=$?
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
wget -qO- "$URL" | bash -s -- "$WS" || irc=$?
|
|
else
|
|
echo "ERROR: neither curl nor wget is available to fetch the installer" >&2
|
|
irc=1
|
|
fi
|
|
|
|
if [ "$irc" -ne 0 ]; then
|
|
echo "ERROR: the install failed (rc $irc); rolling back" >&2
|
|
if restore; then
|
|
rm -rf "$BK"
|
|
echo "rolled back: the workspace is at the version it had before the update"
|
|
restart_old_monitor "$MON"
|
|
else
|
|
echo "ERROR: the rollback failed; the backup is kept at $BK (the next update restores it)" >&2
|
|
fi
|
|
release_marker
|
|
exit 1
|
|
fi
|
|
set_state installed
|
|
|
|
POST="$WS/.agents/skills/multi-agent-mux-update/scripts/post_update.sh"
|
|
prc=0
|
|
if [ -f "$POST" ]; then
|
|
bash "$POST" --monitor "$MON" || prc=$?
|
|
else
|
|
echo "ERROR: the installed version has no $POST; the registry converts on its next write and the monitor was not restarted" >&2
|
|
prc=1
|
|
fi
|
|
rm -rf "$BK"
|
|
release_marker
|
|
[ "$prc" -eq 0 ] && echo "Update complete."
|
|
exit "$prc"
|