#!/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 # install_phase.sh restore [--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 ..." >&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 ..." >&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"