110 lines
3.6 KiB
Bash
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"
|