changemaker.lite/scripts/upgrade-watcher.sh
bunker-admin da3e43fcf7 Add browser-based system upgrade UI with file-based IPC
API container writes trigger files to a shared volume (data/upgrade/),
and a systemd path watcher on the host detects them and runs the
upgrade scripts. This avoids giving the container Docker socket access.

- Add upgrade-check.sh (git fetch + compare + write status.json)
- Add upgrade-watcher.sh (systemd bridge, dispatches check/upgrade)
- Add systemd path/service units with placeholder substitution
- Modify upgrade.sh with --api-mode flag (progress.json + result.json)
- Add API upgrade module (service + routes, SUPER_ADMIN only)
- Add System tab to Settings page with version info, changelog,
  progress steps, and upgrade confirmation modal
- Add upgrade watcher installation to config.sh wizard
- Add data/upgrade/ shared volume to api service in docker-compose

Bunker Admin
2026-03-03 18:00:15 -07:00

89 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# Changemaker Lite V2 — Upgrade Watcher (systemd bridge)
# Called by systemd path unit when data/upgrade/trigger.json is created.
# Reads the trigger, dispatches to the appropriate script, cleans up.
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
UPGRADE_DIR="${PROJECT_DIR}/data/upgrade"
TRIGGER_FILE="${UPGRADE_DIR}/trigger.json"
LOG_DIR="${PROJECT_DIR}/logs"
mkdir -p "$LOG_DIR"
log() {
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*" | tee -a "${LOG_DIR}/upgrade-watcher.log"
}
# Bail if no trigger file
if [[ ! -f "$TRIGGER_FILE" ]]; then
log "No trigger file found, exiting."
exit 0
fi
# Read trigger (minimal JSON parsing with grep/sed — no jq dependency)
TRIGGER_CONTENT="$(cat "$TRIGGER_FILE")"
ACTION="$(echo "$TRIGGER_CONTENT" | grep -o '"action"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"action"[[:space:]]*:[[:space:]]*"//' | sed 's/".*//')"
if [[ -z "$ACTION" ]]; then
log "ERROR: Could not parse action from trigger file"
rm -f "$TRIGGER_FILE"
exit 1
fi
log "Received trigger: action=${ACTION}"
# Remove trigger immediately to prevent re-execution
rm -f "$TRIGGER_FILE"
case "$ACTION" in
check)
log "Running update check..."
# Extract optional branch
BRANCH="$(echo "$TRIGGER_CONTENT" | grep -o '"branch"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"branch"[[:space:]]*:[[:space:]]*"//' | sed 's/".*//' || true)"
ARGS=()
if [[ -n "$BRANCH" ]]; then
ARGS+=(--branch "$BRANCH")
fi
"$SCRIPT_DIR/upgrade-check.sh" "${ARGS[@]}" 2>&1 | tee -a "${LOG_DIR}/upgrade-watcher.log"
log "Update check complete."
;;
upgrade)
log "Running upgrade..."
# Parse options from trigger
ARGS=(--api-mode)
SKIP_BACKUP="$(echo "$TRIGGER_CONTENT" | grep -o '"skipBackup"[[:space:]]*:[[:space:]]*true' || true)"
if [[ -n "$SKIP_BACKUP" ]]; then
ARGS+=(--skip-backup --force)
fi
PULL_SERVICES="$(echo "$TRIGGER_CONTENT" | grep -o '"pullServices"[[:space:]]*:[[:space:]]*true' || true)"
if [[ -n "$PULL_SERVICES" ]]; then
ARGS+=(--pull-services)
fi
DRY_RUN="$(echo "$TRIGGER_CONTENT" | grep -o '"dryRun"[[:space:]]*:[[:space:]]*true' || true)"
if [[ -n "$DRY_RUN" ]]; then
ARGS+=(--dry-run)
fi
BRANCH="$(echo "$TRIGGER_CONTENT" | grep -o '"branch"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"branch"[[:space:]]*:[[:space:]]*"//' | sed 's/".*//' || true)"
if [[ -n "$BRANCH" ]]; then
ARGS+=(--branch "$BRANCH")
fi
"$SCRIPT_DIR/upgrade.sh" "${ARGS[@]}" 2>&1 | tee -a "${LOG_DIR}/upgrade-watcher.log"
log "Upgrade complete."
;;
*)
log "ERROR: Unknown action '${ACTION}'"
exit 1
;;
esac