#!/bin/bash set -euo pipefail # Perform health check and rollback if necessary # Usage: ./health-check-and-rollback.sh REPO_PROJECT_PATH IS_PROD HEALTH_CHECK_URL [MAX_ATTEMPTS] if [ $# -lt 3 ]; then echo "Error: Invalid number of arguments" echo "Usage: $0 REPO_PROJECT_PATH IS_PROD HEALTH_CHECK_URL [MAX_ATTEMPTS]" exit 1 fi REPO_PROJECT_PATH="$1" IS_PROD="$2" HEALTH_CHECK_URL="$3" MAX_ATTEMPTS="${4:-30}" CURRENT_LINK_PATH="${REPO_PROJECT_PATH}/current" RELEASES_PATH="${REPO_PROJECT_PATH}/releases" echo "🏥 Performing health check..." echo "Health check URL: $HEALTH_CHECK_URL" get_current_version() { if [ -L "$CURRENT_LINK_PATH" ]; then basename "$(readlink -f "$CURRENT_LINK_PATH")" else echo "unknown" fi } ATTEMPT=0 while [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; do # Check if the service is responding with 200 OK HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' -m 10 "$HEALTH_CHECK_URL" || echo '000') if [ "$HTTP_CODE" = "200" ]; then echo "✅ Health check passed! (HTTP $HTTP_CODE)" CURRENT_VERSION=$(get_current_version) echo "📌 Current version: ${CURRENT_VERSION}" exit 0 fi ATTEMPT=$((ATTEMPT + 1)) if [ "$ATTEMPT" -eq "$MAX_ATTEMPTS" ]; then echo "❌ Health check failed after $MAX_ATTEMPTS attempts (Last HTTP code: $HTTP_CODE)" echo "🔄 Rolling back deployment..." FAILED_VERSION=$(get_current_version) echo "❌ Failed version: ${FAILED_VERSION}" # Check if we have a previous version to roll back to if [ -f "${CURRENT_LINK_PATH}/.previous_version" ]; then PREVIOUS_VERSION_PATH=$(cat "${CURRENT_LINK_PATH}/.previous_version") PREVIOUS_VERSION=$(basename "$PREVIOUS_VERSION_PATH") if [ -d "$PREVIOUS_VERSION_PATH" ]; then echo "🔄 Rolling back to version: ${PREVIOUS_VERSION}" # Stop failed deployment containers cd "$CURRENT_LINK_PATH" echo "Stopping failed deployment containers..." docker compose -f docker-compose_web.yml down || true if [ "$IS_PROD" = "false" ]; then docker compose -f docker-compose_staging.yml down || true fi docker compose -f docker-compose_core.yml down || true # Switch symlink back to previous version ln -sfn "$PREVIOUS_VERSION_PATH" "$CURRENT_LINK_PATH" # Start previous version containers cd "$CURRENT_LINK_PATH" docker compose -f docker-compose_core.yml up -d --no-build if [ "$IS_PROD" = "true" ]; then docker compose -f docker-compose_web.yml up -d --no-build else docker compose -f docker-compose_web.yml -f docker-compose_staging.yml up -d --no-build fi echo "✅ Rollback completed to version: ${PREVIOUS_VERSION}" # Mark failed version if [ -d "${RELEASES_PATH}/${FAILED_VERSION}" ]; then touch "${RELEASES_PATH}/${FAILED_VERSION}/.failed" echo "$(date): Health check failed, rolled back to ${PREVIOUS_VERSION}" > "${RELEASES_PATH}/${FAILED_VERSION}/.failure_reason" fi else echo "❌ Previous version directory not found: $PREVIOUS_VERSION_PATH" exit 1 fi else echo "❌ No previous version information found. Cannot rollback!" echo "💡 This might be the first deployment or the previous version info is missing." exit 1 fi exit 1 fi echo "⏳ Waiting for service to be healthy... (attempt $ATTEMPT/$MAX_ATTEMPTS, HTTP code: $HTTP_CODE)" sleep 10 done