#!/bin/bash # Deployment script for Moyos Wedding App # Usage: ./deploy.sh [--staging|--production] [--skip-build] [--skip-migrations] set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" } error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } warn() { echo -e "${YELLOW}[WARN]${NC} $1" } info() { echo -e "${BLUE}[INFO]${NC} $1" } # Configuration APP_DIR="${APP_DIR:-/opt/moyos-wedding-app/app}" ENV_FILE="${ENV_FILE:-.env.production}" PM2_APP_NAME="${PM2_APP_NAME:-moyos-wedding-app}" ENVIRONMENT="${1:-production}" SKIP_BUILD=false SKIP_MIGRATIONS=false # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --staging) ENVIRONMENT="staging" ENV_FILE=".env.staging" shift ;; --production) ENVIRONMENT="production" ENV_FILE=".env.production" shift ;; --skip-build) SKIP_BUILD=true shift ;; --skip-migrations) SKIP_MIGRATIONS=true shift ;; *) error "Unknown option: $1" exit 1 ;; esac done log "Starting deployment to $ENVIRONMENT environment..." # Check if running as correct user if [ "$EUID" -eq 0 ]; then warn "Running as root. Consider using a non-root user for deployment." fi # Navigate to app directory if [ ! -d "$APP_DIR" ]; then error "App directory not found: $APP_DIR" exit 1 fi cd "$APP_DIR" log "Working directory: $(pwd)" # Check if environment file exists if [ ! -f "$ENV_FILE" ]; then error "Environment file not found: $ENV_FILE" error "Please create $ENV_FILE before deploying" exit 1 fi # Load environment variables set -a source "$ENV_FILE" set +a # Pre-deployment checks log "Running pre-deployment checks..." # Check Node.js version NODE_VERSION=$(node --version) log "Node.js version: $NODE_VERSION" if [[ ! "$NODE_VERSION" =~ ^v20\. ]]; then error "Node.js 20.x required. Found: $NODE_VERSION" exit 1 fi # Check if PM2 is installed if ! command -v pm2 &> /dev/null; then error "PM2 is not installed. Install with: npm install -g pm2" exit 1 fi # Check database connection if [ "$SKIP_MIGRATIONS" = false ]; then log "Checking database connection..." if command -v psql &> /dev/null && [ -n "$DATABASE_URL" ]; then # Extract connection details from DATABASE_URL # This is a simple check - actual connection test would be better log "Database URL configured" else warn "Cannot verify database connection. Proceeding anyway..." fi fi # Git pull (if in git repository) if [ -d ".git" ]; then log "Pulling latest changes from Git..." git pull origin main || git pull || { warn "Git pull failed or not on a branch. Continuing with current code..." } else warn "Not a Git repository. Skipping Git pull." fi # Install dependencies log "Installing dependencies..." npm ci --production=false || { error "Failed to install dependencies" exit 1 } # Generate Prisma client log "Generating Prisma client..." npx prisma generate || { error "Failed to generate Prisma client" exit 1 } # Run database migrations if [ "$SKIP_MIGRATIONS" = false ]; then log "Running database migrations..." npx prisma migrate deploy || { error "Database migrations failed" exit 1 } log "Database migrations completed" else warn "Skipping database migrations (--skip-migrations flag)" fi # Build application if [ "$SKIP_BUILD" = false ]; then log "Building application..." npm run build || { error "Build failed" exit 1 } log "Build completed successfully" else warn "Skipping build (--skip-build flag)" if [ ! -d ".next" ]; then error "No .next directory found and --skip-build was used" exit 1 fi fi # Health check before deployment log "Running pre-deployment health check..." if [ -f "scripts/health-check.sh" ]; then bash scripts/health-check.sh || { warn "Pre-deployment health check failed, but continuing..." } fi # PM2 deployment log "Deploying with PM2..." # Check if app is already running if pm2 list | grep -q "$PM2_APP_NAME"; then log "App is already running. Performing graceful reload..." pm2 reload "$PM2_APP_NAME" || { error "PM2 reload failed" exit 1 } else log "Starting app with PM2..." # Check for ecosystem file if [ -f "ecosystem.config.js" ]; then pm2 start ecosystem.config.js || { error "Failed to start PM2 with ecosystem config" exit 1 } else # Fallback: start with npm pm2 start npm --name "$PM2_APP_NAME" -- start || { error "Failed to start PM2" exit 1 } fi fi # Save PM2 process list pm2 save || { warn "Failed to save PM2 process list" } # Wait for app to be ready log "Waiting for application to be ready..." sleep 5 # Health check after deployment log "Running post-deployment health check..." MAX_RETRIES=10 RETRY_COUNT=0 HEALTH_CHECK_URL="${NEXT_PUBLIC_APP_URL:-http://localhost:3000}/api/health" while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do if curl -f -s "$HEALTH_CHECK_URL" > /dev/null 2>&1; then log "Health check passed!" break else RETRY_COUNT=$((RETRY_COUNT + 1)) if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then error "Health check failed after $MAX_RETRIES attempts" error "Check application logs: pm2 logs $PM2_APP_NAME" exit 1 fi warn "Health check failed. Retrying... ($RETRY_COUNT/$MAX_RETRIES)" sleep 3 fi done # Show deployment status log "Deployment completed successfully!" info "PM2 Status:" pm2 status "$PM2_APP_NAME" info "" info "View logs: pm2 logs $PM2_APP_NAME" info "Monitor: pm2 monit" info "Restart: pm2 restart $PM2_APP_NAME" # Optional: Run smoke tests if [ -f "scripts/smoke-tests.sh" ]; then log "Running smoke tests..." bash scripts/smoke-tests.sh || { warn "Smoke tests failed, but deployment succeeded" } fi log "Deployment to $ENVIRONMENT completed successfully! 🎉"