#!/bin/bash # canary-deploy.sh - Script for canary deployments with automatic rollback # Usage: ./bin/canary-deploy.sh [--percentage=10] [--wait=5] [--force] set -e # Colors for output GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m' NC='\033[0m' # No Color # Default settings CANARY_PERCENTAGE=10 WAIT_MINUTES=5 FORCE_DEPLOY=false CURRENT_DATE=$(date +"%Y-%m-%d") DEPLOY_ID=$(date +"%Y%m%d%H%M%S") # Parse arguments for arg in "$@"; do case $arg in --percentage=*) CANARY_PERCENTAGE="${arg#*=}" shift ;; --wait=*) WAIT_MINUTES="${arg#*=}" shift ;; --force) FORCE_DEPLOY=true shift ;; esac done echo -e "${GREEN}=== Canary Deployment System ===${NC}" echo "Preparing for canary deployment (${CANARY_PERCENTAGE}% of servers, ${WAIT_MINUTES} min wait)..." # Check if we're in the right directory if [ ! -d "tests/e2e" ]; then echo -e "${RED}Error: Please run this script from the wordpress-dev directory${NC}" exit 1 fi # Create logs directory mkdir -p logs/deploy LOG_FILE="logs/deploy/canary-deploy-${DEPLOY_ID}.log" # Log function log() { echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >> "$LOG_FILE" echo "$1" } # Check deployment prerequisites check_prerequisites() { log "Checking deployment prerequisites..." # Run health check if [ -f "bin/health-check.sh" ]; then log "Running health check..." if ! bash bin/health-check.sh; then log "${RED}Health check failed. Aborting deployment.${NC}" if [ "$FORCE_DEPLOY" != true ]; then return 1 else log "${YELLOW}Forcing deployment despite health check failure.${NC}" fi fi fi # Verify selectors if [ -f "bin/verify-selectors.sh" ]; then log "Verifying selectors..." if ! bash bin/verify-selectors.sh; then log "${RED}Selector verification failed. Aborting deployment.${NC}" if [ "$FORCE_DEPLOY" != true ]; then return 1 else log "${YELLOW}Forcing deployment despite selector verification failure.${NC}" fi fi fi # Run pre-deployment validation if [ -f "bin/pre-deploy-validation.sh" ]; then log "Running pre-deployment validation..." if ! bash bin/pre-deploy-validation.sh; then log "${RED}Pre-deployment validation failed. Aborting deployment.${NC}" if [ "$FORCE_DEPLOY" != true ]; then return 1 else log "${YELLOW}Forcing deployment despite validation failure.${NC}" fi fi fi log "${GREEN}Deployment prerequisites check passed.${NC}" return 0 } # Calculate canary server count calculate_canary_servers() { log "Calculating canary server allocation..." # In a real environment, this would query your server inventory # For this example, we'll assume 10 servers total TOTAL_SERVERS=10 CANARY_SERVERS=$(( TOTAL_SERVERS * CANARY_PERCENTAGE / 100 )) # Ensure at least one server if [ $CANARY_SERVERS -lt 1 ]; then CANARY_SERVERS=1 fi log "Deploying to ${CANARY_SERVERS} out of ${TOTAL_SERVERS} servers (${CANARY_PERCENTAGE}%)" return 0 } # Deploy to canary servers deploy_to_canary() { log "Deploying to canary servers..." # In a real environment, this would deploy to specific servers # For this example, we'll simulate the deployment # Create deployment package log "Creating deployment package..." DEPLOY_DIR="deploy/canary-${DEPLOY_ID}" mkdir -p "$DEPLOY_DIR" # Copy plugin files to deployment directory log "Copying plugin files..." mkdir -p "$DEPLOY_DIR/hvac-community-events" cp -r wordpress/wp-content/plugins/hvac-community-events/* "$DEPLOY_DIR/hvac-community-events/" # Create deployment metadata cat > "$DEPLOY_DIR/deploy-meta.json" << EOF { "deployId": "${DEPLOY_ID}", "date": "${CURRENT_DATE}", "type": "canary", "percentage": ${CANARY_PERCENTAGE}, "servers": ${CANARY_SERVERS} } EOF log "Deployment package created: ${DEPLOY_DIR}" # Simulate deployment to canary servers log "Deploying to ${CANARY_SERVERS} canary servers..." sleep 2 log "${GREEN}Canary deployment completed successfully.${NC}" return 0 } # Run smoke tests on canary run_canary_tests() { log "Running smoke tests on canary servers..." # Create a canary test file CANARY_TEST="tests/e2e/canary-test.spec.ts" log "Creating canary test..." cat > "$CANARY_TEST" << 'EOF' import { test } from '@playwright/test'; import { LoginPage } from './pages/LoginPage'; import { TEST_USERS } from './data/test-users'; test('Canary deployment smoke test', async ({ page }) => { // Login test const loginPage = new LoginPage(page); await loginPage.navigate(); await loginPage.login(TEST_USERS.trainer.username, TEST_USERS.trainer.password); // Verify dashboard loads await page.waitForURL(/.*hvac-dashboard/); await page.waitForLoadState('networkidle'); // Take screenshot for verification await page.screenshot({ path: 'screenshots/canary-dashboard.png' }); // Verify critical element is present const eventsTable = await page.isVisible('.hvac-events-table'); if (!eventsTable) { throw new Error('Events table not found on dashboard'); } // Check create event button const createButton = await page.isVisible('.create-event-button, a:has-text("Create Event")'); if (!createButton) { throw new Error('Create event button not found'); } console.log('Canary test passed successfully'); }); EOF log "Running canary test against canary servers..." # Run the test if npx playwright test "$CANARY_TEST"; then log "${GREEN}Canary tests passed successfully.${NC}" return 0 else log "${RED}Canary tests failed. Initiating rollback.${NC}" return 1 fi } # Monitor canary health monitor_canary_health() { log "Monitoring canary health for ${WAIT_MINUTES} minutes..." # In a real environment, this would query metrics from the canary servers # For this example, we'll simulate monitoring # Create monitor output file MONITOR_FILE="logs/deploy/canary-monitor-${DEPLOY_ID}.txt" # Start monitoring loop local end_time=$(( $(date +%s) + WAIT_MINUTES * 60 )) local current_time=$(date +%s) local status="healthy" echo "Canary Health Monitoring - ${CURRENT_DATE}" > "$MONITOR_FILE" echo "=================================" >> "$MONITOR_FILE" echo "" >> "$MONITOR_FILE" while [ $current_time -lt $end_time ]; do # Simulate health check local memory_usage=$((50 + RANDOM % 40)) local cpu_usage=$((20 + RANDOM % 60)) local error_rate=$((RANDOM % 10)) # Log metrics echo "[$(date +"%H:%M:%S")] Memory: ${memory_usage}%, CPU: ${cpu_usage}%, Error rate: ${error_rate}%" >> "$MONITOR_FILE" # Check thresholds if [ $memory_usage -gt 85 ] || [ $cpu_usage -gt 80 ] || [ $error_rate -gt 5 ]; then status="unhealthy" echo "[$(date +"%H:%M:%S")] WARNING: Threshold exceeded" >> "$MONITOR_FILE" fi # Sleep for a bit sleep 30 # Update current time current_time=$(date +%s) done log "Monitoring complete. Results saved to ${MONITOR_FILE}" # Return status if [ "$status" = "healthy" ]; then log "${GREEN}Canary is healthy after ${WAIT_MINUTES} minutes.${NC}" return 0 else log "${RED}Canary is unhealthy. Initiating rollback.${NC}" return 1 fi } # Roll back canary deployment rollback_canary() { log "${RED}Rolling back canary deployment...${NC}" # In a real environment, this would restore the previous version to canary servers # For this example, we'll simulate rollback log "Restoring previous version to canary servers..." sleep 2 log "${GREEN}Rollback completed successfully.${NC}" return 0 } # Deploy to all servers deploy_to_all() { log "Deploying to all servers..." # In a real environment, this would deploy to all remaining servers # For this example, we'll simulate full deployment log "Deploying to remaining servers..." sleep 3 log "${GREEN}Full deployment completed successfully.${NC}" return 0 } # Main deployment logic log "Starting canary deployment process (ID: ${DEPLOY_ID})..." # Check prerequisites if ! check_prerequisites; then log "${RED}Deployment prerequisites not met. Aborting deployment.${NC}" exit 1 fi # Calculate canary servers calculate_canary_servers # Deploy to canary servers if ! deploy_to_canary; then log "${RED}Canary deployment failed. Aborting deployment.${NC}" exit 1 fi # Run canary tests if ! run_canary_tests; then rollback_canary log "${RED}Deployment aborted due to failed canary tests.${NC}" exit 1 fi # Monitor canary health if ! monitor_canary_health; then rollback_canary log "${RED}Deployment aborted due to unhealthy canary.${NC}" exit 1 fi # If we get here, canary is healthy, so deploy to all servers if ! deploy_to_all; then log "${RED}Full deployment failed.${NC}" exit 1 fi log "${GREEN}Canary deployment process completed successfully!${NC}" log "Deployment ID: ${DEPLOY_ID}" exit 0