The update is a skill (decision 4); deploy/update.sh and the installed .mam_deploy/update.sh are gone. - Phase 1 (installed code, update.sh): publish the update marker under the exclusive admission lock; refuse before any change while a running row, a live herdr agent in the workspace on any running server, an open job (unreadable JSON included, dry_run not), a live loop marker or a monitor.lock held by a monitor the skills did not start (U3) is there - the calling agent (server + pane, its row and one running job) excepted (lib_py/update.py precheck, decisions 1 and 7). Stop the monitor (TERM, then KILL and restart it if it does not end; decision 2), handle a leftover backup by its state (U4), back up .agents, .mam_deploy, root files, .venv, the installer's .mam files and the registry (SQLite backup API under the registry lock) into .mam/update-backup/, then exec a copy of install_phase.sh outside the tree (decision 5). - The copy (no lib.sh/lib_py, bash 3.2) runs the installer in place; on failure it restores the backup and restarts the monitor from the restored tree; on success it runs phase 2, post_update.sh of the new tree, which converts the registry with lib_py.registry_format and restarts the monitor if it ran. The backup and the marker are removed at the end. - install.sh updates in place (decisions 3, 4, 9): install_state keys are set, one line each; no updater is installed and an old one (.mam_deploy/update.sh, or a root update.sh the manifest lists) is removed; framework files the new version no longer ships are deleted when unmodified, kept and listed when modified, and dropped from base/, the manifest and asset_hashes.txt; a local source's .mam is not staged. remove.sh knows the new skill; CI shellchecks its scripts; docs point at the skill. - test_issue8_update_state and test_deploy_layout td6/td27 run the skill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
4.2 KiB
Bash
Executable File
123 lines
4.2 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
|
|
|
|
set_state installing || { echo "ERROR: cannot write $BK/state" >&2; release_marker; exit 1; }
|
|
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"
|