Files
2026-07-11 10:44:42 -05:00

110 lines
3.6 KiB
Bash

#!/bin/bash
set -euo pipefail
COMPONENT="meshpress-bridge"
TARGET_DIR="/opt/gracepress-meshpress-bridge"
ENV_FILE="/etc/gracepress/meshpress-bridge.env"
SERVICE_NAME="gp-meshpress-bridge"
LOG_PREFIX="[meshpress-bridge-update]"
LOCK_FILE="/tmp/gracepress-meshpress-bridge-update.lock"
TMP_DIR="$(mktemp -d)"
STAGE_DIR=""
BACKUP_DIR=""
cleanup() {
rm -rf "$TMP_DIR"
if [ -n "$STAGE_DIR" ] && [ -d "$STAGE_DIR" ]; then rm -rf "$STAGE_DIR"; fi
}
trap cleanup EXIT
log() { echo "$LOG_PREFIX $(date -Is) $*"; }
if [ "$(id -u)" -ne 0 ]; then
log "Please run as root."
exit 1
fi
exec 9>"$LOCK_FILE"
if command -v flock >/dev/null 2>&1; then
flock -n 9 || { log "Another bridge update is already running."; exit 1; }
fi
[ -f "$ENV_FILE" ] && . "$ENV_FILE"
CATALOG_URL="${GRACEPRESS_CATALOG_URL:-https://christit.com/gracepress/api.php?endpoint=manifest&slug=meshpress-bridge}"
apt-get update -y >/dev/null
apt-get install -y curl unzip python3 ca-certificates nodejs npm >/dev/null
if ! command -v pm2 >/dev/null 2>&1; then
npm install -g pm2 >/dev/null
fi
log "Checking catalog: $CATALOG_URL"
curl -fsSL "$CATALOG_URL&nocache=$(date +%s)" -o "$TMP_DIR/manifest.json"
DOWNLOAD_URL="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("download_url",""))' "$TMP_DIR/manifest.json")"
VERSION="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("version","0.0.0"))' "$TMP_DIR/manifest.json")"
if [ -z "$DOWNLOAD_URL" ]; then
log "No download_url found in catalog manifest."
exit 1
fi
CURRENT="$(cat "$TARGET_DIR/VERSION" 2>/dev/null || echo 0.0.0)"
if [ "$CURRENT" = "$VERSION" ]; then
log "$COMPONENT is already current: $CURRENT"
exit 0
fi
log "Updating $COMPONENT from $CURRENT to $VERSION"
curl -fL "$DOWNLOAD_URL" -o "$TMP_DIR/package.zip"
unzip -q "$TMP_DIR/package.zip" -d "$TMP_DIR/pkg"
PKG_DIR="$(find "$TMP_DIR/pkg" -maxdepth 3 -type f -name server.js -printf '%h\n' | head -n 1)"
if [ -z "$PKG_DIR" ] || [ ! -f "$PKG_DIR/package.json" ]; then
log "Package does not contain a valid bridge payload."
exit 1
fi
STAGE_DIR="${TARGET_DIR}.next.${VERSION}.$$"
rm -rf "$STAGE_DIR"
mkdir -p "$STAGE_DIR"
cp -a "$PKG_DIR"/. "$STAGE_DIR"/
echo "$VERSION" > "$STAGE_DIR/VERSION"
chmod +x "$STAGE_DIR/update.sh" "$STAGE_DIR/meshpress-bridge" 2>/dev/null || true
log "Installing node dependencies in staging directory."
(cd "$STAGE_DIR" && npm install --omit=dev >/dev/null && node --check server.js >/dev/null)
BACKUP_DIR="${TARGET_DIR}.old.$(date +%Y%m%d%H%M%S)"
if [ -d "$TARGET_DIR" ]; then
mv "$TARGET_DIR" "$BACKUP_DIR"
log "Previous install moved to $BACKUP_DIR"
fi
mv "$STAGE_DIR" "$TARGET_DIR"
STAGE_DIR=""
rollback() {
log "Update failed after swap; attempting rollback."
local failed="${TARGET_DIR}.failed.$(date +%Y%m%d%H%M%S)"
if [ -d "$TARGET_DIR" ]; then mv "$TARGET_DIR" "$failed"; fi
if [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then mv "$BACKUP_DIR" "$TARGET_DIR"; fi
if [ -f "$TARGET_DIR/server.js" ]; then
pm2 start "$TARGET_DIR/server.js" --name "$SERVICE_NAME" --time --max-memory-restart 512M >/dev/null 2>&1 || pm2 restart "$SERVICE_NAME" --update-env >/dev/null 2>&1 || true
pm2 save >/dev/null 2>&1 || true
fi
}
if pm2 describe "$SERVICE_NAME" >/dev/null 2>&1; then
if ! (cd "$TARGET_DIR" && pm2 restart "$SERVICE_NAME" --update-env >/dev/null); then
rollback
exit 1
fi
else
if ! (cd "$TARGET_DIR" && pm2 start server.js --name "$SERVICE_NAME" --time --max-memory-restart 512M >/dev/null); then
rollback
exit 1
fi
fi
pm2 save >/dev/null
log "$COMPONENT updated to $VERSION"