#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" . "$ROOT_DIR/scripts/lib.sh" load_env TARGET_VERSION="${1:-}" [ -n "$TARGET_VERSION" ] || fail_json "XUQM_PRIVATE_4020" "usage: upgrade.sh " "upgrade" CURRENT_VERSION="$(cat "$ROOT_DIR/VERSION" | tr -d '[:space:]')" audit "upgrade" "STARTED" "from=$CURRENT_VERSION to=$TARGET_VERSION" progress "upgrade" "STARTED" "from=$CURRENT_VERSION to=$TARGET_VERSION" printf 'Upgrading from %s to %s\n' "$CURRENT_VERSION" "$TARGET_VERSION" # Pre-upgrade backup "$ROOT_DIR/scripts/backup.sh" audit "upgrade" "BACKUP_DONE" "" # Validate target version exists in manifest (if present) MANIFEST="$ROOT_DIR/image-manifest.json" if command -v python3 >/dev/null 2>&1 && [ -f "$MANIFEST" ]; then MANIFEST_VER="$(python3 -c "import json,sys; d=json.load(open('$MANIFEST')); print(d.get('privateVersion',''))" 2>/dev/null || true)" if [ -n "$MANIFEST_VER" ] && [ "$MANIFEST_VER" != "$TARGET_VERSION" ]; then printf 'WARNING: image-manifest.json declares version %s but upgrading to %s\n' "$MANIFEST_VER" "$TARGET_VERSION" printf 'Update image-manifest.json before proceeding (y/N): ' read -r CONFIRM [ "$CONFIRM" = "y" ] || fail_json "XUQM_PRIVATE_4021" "upgrade aborted: version mismatch in image-manifest.json" "upgrade" fi fi # Save old image tag for rollback OLD_TAG="${IMAGE_TAG:-$CURRENT_VERSION}" set_env_value "$ROOT_DIR/.deploy-state/current.json.bak" "IMAGE_TAG" "$OLD_TAG" 2>/dev/null || true printf '%s\n' "$OLD_TAG" > "$ROOT_DIR/.deploy-state/previous-image-tag.txt" # Update image tag set_env_value "$ROOT_DIR/.env" "IMAGE_TAG" "$TARGET_VERSION" set_env_value "$ROOT_DIR/.env" "PRIVATE_VERSION" "$TARGET_VERSION" load_env # Pull new images for active profiles PROFILES="${COMPOSE_PROFILES:-base}" audit "upgrade" "PULL_STARTED" "profiles=$PROFILES" COMPOSE_PROFILES="$PROFILES" compose pull audit "upgrade" "PULL_DONE" "" # Render new configs "$ROOT_DIR/scripts/render-config.sh" # Rolling restart — stop then start services COMPOSE_PROFILES="$PROFILES" compose up -d --remove-orphans # Health check — rollback automatically on failure if ! "$ROOT_DIR/scripts/healthcheck.sh"; then printf 'Health check failed after upgrade. Rolling back...\n' audit "upgrade" "HEALTHCHECK_FAILED" "rolling back to $OLD_TAG" set_env_value "$ROOT_DIR/.env" "IMAGE_TAG" "$OLD_TAG" set_env_value "$ROOT_DIR/.env" "PRIVATE_VERSION" "$OLD_TAG" load_env COMPOSE_PROFILES="$PROFILES" compose up -d --remove-orphans audit "upgrade" "ROLLED_BACK" "restored to $OLD_TAG" fail_json "XUQM_PRIVATE_4022" "upgrade failed; automatically rolled back to $OLD_TAG" "upgrade" fi # Update VERSION file printf '%s\n' "$TARGET_VERSION" > "$ROOT_DIR/VERSION" audit "upgrade" "DONE" "from=$CURRENT_VERSION to=$TARGET_VERSION" progress "upgrade" "DONE" "from=$CURRENT_VERSION to=$TARGET_VERSION" printf 'Upgrade complete: %s → %s\n' "$CURRENT_VERSION" "$TARGET_VERSION"