← back to Watches
scripts/production-deploy.sh
306 lines
#!/bin/bash
################################################################################
# OMEGA WATCHES - PRODUCTION DEPLOYMENT SCRIPT
# Zero-downtime blue-green deployment with automatic rollback
################################################################################
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
APP_NAME="omega-watches"
APP_DIR="/root/Projects/watches"
BACKUP_DIR="$APP_DIR/backups/deployments"
GREEN_DIR="$APP_DIR/deploy-green"
MAIN_PORT=7500
GREEN_PORT=7502
HEALTH_ENDPOINT="http://localhost"
MAX_HEALTH_RETRIES=10
HEALTH_RETRY_DELAY=5
# Logging
LOG_FILE="$APP_DIR/logs/deployment-$(date +%Y%m%d_%H%M%S).log"
mkdir -p "$(dirname "$LOG_FILE")"
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" | tee -a "$LOG_FILE"
}
log_warning() {
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1" | tee -a "$LOG_FILE"
}
log_info() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO:${NC} $1" | tee -a "$LOG_FILE"
}
# Error handler
error_exit() {
log_error "$1"
exit 1
}
# Print header
print_header() {
echo -e "${BLUE}"
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ OMEGA WATCHES - PRODUCTION DEPLOYMENT ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
}
# Check prerequisites
check_prerequisites() {
log_info "Checking prerequisites..."
# Check if PM2 is installed
if ! command -v pm2 &> /dev/null; then
error_exit "PM2 is not installed. Please install PM2 first."
fi
# Check if Node.js is installed
if ! command -v node &> /dev/null; then
error_exit "Node.js is not installed."
fi
# Check if npm is installed
if ! command -v npm &> /dev/null; then
error_exit "npm is not installed."
fi
log "Prerequisites check passed ✓"
}
# Create backup
create_backup() {
log_info "Creating backup of current deployment..."
mkdir -p "$BACKUP_DIR"
BACKUP_FILE="$BACKUP_DIR/backup-$(date +%Y%m%d_%H%M%S).tar.gz"
cd "$APP_DIR"
tar -czf "$BACKUP_FILE" \
--exclude='node_modules' \
--exclude='backups' \
--exclude='logs' \
--exclude='.git' \
--exclude='deploy-green' \
dist/ server.js package.json data/ public/ 2>/dev/null || true
if [ -f "$BACKUP_FILE" ]; then
log "Backup created: $BACKUP_FILE ✓"
else
log_warning "Backup creation failed, continuing anyway..."
fi
}
# Install dependencies
install_dependencies() {
log_info "Installing dependencies..."
cd "$APP_DIR"
npm ci --production --silent 2>&1 | tee -a "$LOG_FILE"
log "Dependencies installed ✓"
}
# Build application
build_application() {
log_info "Building application..."
cd "$APP_DIR"
npm run build 2>&1 | tee -a "$LOG_FILE" || log_warning "Build step skipped or failed"
log "Build completed ✓"
}
# Deploy to green environment
deploy_green() {
log_info "Deploying to green environment..."
# Clean up old green deployment
rm -rf "$GREEN_DIR"
mkdir -p "$GREEN_DIR"
# Copy files to green deployment
cd "$APP_DIR"
cp -r dist/ node_modules/ server.js package.json data/ public/ ecosystem.config.cjs "$GREEN_DIR/" 2>/dev/null || true
log "Green deployment prepared ✓"
}
# Start green instance
start_green_instance() {
log_info "Starting green instance on port $GREEN_PORT..."
cd "$GREEN_DIR"
# Stop any existing green instance
pm2 delete omega-watches-green 2>/dev/null || true
# Start green instance with custom port
PORT=$GREEN_PORT pm2 start ecosystem.config.cjs --name omega-watches-green
log "Green instance started ✓"
}
# Health check
health_check() {
local port=$1
local instance_name=$2
log_info "Running health check on $instance_name (port $port)..."
for i in $(seq 1 $MAX_HEALTH_RETRIES); do
log_info "Health check attempt $i/$MAX_HEALTH_RETRIES..."
if curl -f -s "$HEALTH_ENDPOINT:$port/api/health" > /dev/null 2>&1; then
log "Health check passed for $instance_name ✓"
return 0
fi
if [ $i -lt $MAX_HEALTH_RETRIES ]; then
log_info "Waiting ${HEALTH_RETRY_DELAY}s before retry..."
sleep $HEALTH_RETRY_DELAY
fi
done
log_error "Health check failed for $instance_name after $MAX_HEALTH_RETRIES attempts"
return 1
}
# Switch to green (promote)
switch_to_green() {
log_info "Switching traffic to green deployment..."
# Stop blue instance
pm2 stop $APP_NAME 2>/dev/null || true
pm2 delete $APP_NAME 2>/dev/null || true
# Move green to production
cd "$APP_DIR"
rm -rf dist/ node_modules/ server.js 2>/dev/null || true
mv "$GREEN_DIR"/* . 2>/dev/null || true
rmdir "$GREEN_DIR" 2>/dev/null || true
# Start on main port
pm2 start ecosystem.config.cjs --name $APP_NAME
pm2 save --force
log "Traffic switched to new deployment ✓"
}
# Rollback
rollback() {
log_error "Initiating rollback..."
# Stop and delete green instance
pm2 stop omega-watches-green 2>/dev/null || true
pm2 delete omega-watches-green 2>/dev/null || true
# Find latest backup
LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/backup-*.tar.gz 2>/dev/null | head -1)
if [ -z "$LATEST_BACKUP" ]; then
error_exit "No backup found for rollback!"
fi
log_info "Rolling back to: $LATEST_BACKUP"
# Extract backup
cd "$APP_DIR"
tar -xzf "$LATEST_BACKUP"
# Restart main instance
pm2 restart $APP_NAME 2>/dev/null || pm2 start ecosystem.config.cjs --name $APP_NAME
log "Rollback completed ✓"
}
# Cleanup
cleanup() {
log_info "Cleaning up..."
# Remove green directory
rm -rf "$GREEN_DIR" 2>/dev/null || true
# Keep only last 10 backups
cd "$BACKUP_DIR"
ls -t backup-*.tar.gz 2>/dev/null | tail -n +11 | xargs rm -f 2>/dev/null || true
log "Cleanup completed ✓"
}
# Main deployment flow
main() {
print_header
log "Starting deployment at $(date)"
# Pre-deployment checks
check_prerequisites
# Create backup
create_backup
# Build and prepare
install_dependencies
build_application
# Deploy to green
deploy_green
start_green_instance
# Wait for green to be ready
sleep 5
# Health check green instance
if health_check $GREEN_PORT "green"; then
# Switch to green
switch_to_green
# Delete green instance (now running as main)
pm2 delete omega-watches-green 2>/dev/null || true
# Final health check
sleep 3
if health_check $MAIN_PORT "production"; then
cleanup
echo ""
log "${GREEN}╔════════════════════════════════════════════════════════════════╗${NC}"
log "${GREEN}║ DEPLOYMENT SUCCESSFUL! ║${NC}"
log "${GREEN}╚════════════════════════════════════════════════════════════════╝${NC}"
echo ""
log_info "Application URL: http://45.61.58.125:$MAIN_PORT"
log_info "Health Check: http://45.61.58.125:$MAIN_PORT/api/health"
log_info "PM2 Status: pm2 list"
log_info "Logs: pm2 logs $APP_NAME"
echo ""
else
log_error "Production health check failed after switch!"
rollback
exit 1
fi
else
log_error "Green deployment health check failed!"
rollback
exit 1
fi
}
# Run main function
main "$@"