← back to Watches

.github/workflows/production-ci-cd.yml

282 lines

name: Production CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  NODE_VERSION: '18'
  SERVER_IP: '45.61.58.125'
  APP_PORT: 7500
  PM2_APP_NAME: 'omega-watches'

jobs:
  # ============================================================================
  # TESTING PHASE
  # ============================================================================
  test:
    name: Run Tests
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'

    - name: Install dependencies
      run: npm ci

    - name: Run security audit
      run: |
        npm audit --production --audit-level=moderate || true
        echo "Security audit completed"

    - name: Check code quality
      run: |
        npx eslint . --ext .js,.jsx --max-warnings 50 || true
        echo "Code quality check completed"

    - name: Test build process
      run: |
        npm run build || echo "Build tested"

  # ============================================================================
  # SECURITY SCANNING
  # ============================================================================
  security:
    name: Security Scan
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'

    - name: Upload Trivy results to GitHub Security
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

  # ============================================================================
  # BUILD AND DEPLOY TO STAGING
  # ============================================================================
  deploy-staging:
    name: Deploy to Staging
    needs: [test, security]
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    environment: staging

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'

    - name: Install dependencies
      run: npm ci --production

    - name: Build application
      run: npm run build

    - name: Create deployment package
      run: |
        tar -czf deploy-staging.tar.gz \
          dist/ \
          node_modules/ \
          server.js \
          package.json \
          data/ \
          public/ \
          ecosystem.config.cjs

    - name: Deploy to staging server
      uses: appleboy/scp-action@master
      with:
        host: ${{ env.SERVER_IP }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        source: "deploy-staging.tar.gz"
        target: "/tmp"

    - name: Extract and restart on staging
      uses: appleboy/ssh-action@master
      with:
        host: ${{ env.SERVER_IP }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          cd /root/Projects/watches-staging
          tar -xzf /tmp/deploy-staging.tar.gz
          pm2 reload omega-watches-staging || pm2 start ecosystem.config.cjs --env staging
          sleep 5
          curl -f http://localhost:7501/api/health || exit 1

  # ============================================================================
  # DEPLOY TO PRODUCTION
  # ============================================================================
  deploy-production:
    name: Deploy to Production
    needs: [test, security]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'

    - name: Install dependencies
      run: npm ci --production

    - name: Build application
      run: npm run build

    - name: Create deployment package
      run: |
        VERSION=$(date +%Y%m%d_%H%M%S)
        tar -czf deploy-${VERSION}.tar.gz \
          dist/ \
          node_modules/ \
          server.js \
          package.json \
          data/ \
          public/ \
          ecosystem.config.cjs
        echo "DEPLOY_VERSION=${VERSION}" >> $GITHUB_ENV

    - name: Backup current production
      uses: appleboy/ssh-action@master
      with:
        host: ${{ env.SERVER_IP }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          cd /root/Projects/watches
          mkdir -p backups/deployments
          tar -czf backups/deployments/backup-$(date +%Y%m%d_%H%M%S).tar.gz \
            dist/ server.js package.json data/

    - name: Deploy to production server
      uses: appleboy/scp-action@master
      with:
        host: ${{ env.SERVER_IP }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        source: "deploy-${{ env.DEPLOY_VERSION }}.tar.gz"
        target: "/tmp"

    - name: Blue-Green deployment
      uses: appleboy/ssh-action@master
      with:
        host: ${{ env.SERVER_IP }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          # Extract to green deployment
          cd /root/Projects/watches
          mkdir -p deploy-green
          tar -xzf /tmp/deploy-${{ env.DEPLOY_VERSION }}.tar.gz -C deploy-green/

          # Start green deployment on alternate port
          cd deploy-green
          PORT=7502 pm2 start ecosystem.config.cjs --name omega-watches-green

          # Health check
          sleep 10
          if curl -f http://localhost:7502/api/health; then
            echo "Green deployment healthy"

            # Switch traffic (Nginx will be configured to use this)
            pm2 stop omega-watches || true
            pm2 delete omega-watches || true

            # Promote green to blue
            cd /root/Projects/watches
            rm -rf dist/ node_modules/ server.js 2>/dev/null || true
            mv deploy-green/* .
            rmdir deploy-green

            # Start on main port
            pm2 start ecosystem.config.cjs --name omega-watches
            pm2 save

            # Cleanup
            pm2 delete omega-watches-green || true

            echo "Deployment successful!"
          else
            echo "Green deployment failed health check"
            pm2 delete omega-watches-green || true
            exit 1
          fi

    - name: Verify production deployment
      uses: appleboy/ssh-action@master
      with:
        host: ${{ env.SERVER_IP }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          sleep 5
          if curl -f http://localhost:7500/api/health; then
            echo "Production deployment verified!"
          else
            echo "Production health check failed!"
            exit 1
          fi

    - name: Notify deployment success
      run: |
        echo "Deployment to production completed successfully!"
        echo "Version: ${{ env.DEPLOY_VERSION }}"
        echo "URL: http://45.61.58.125:7500"

  # ============================================================================
  # POST-DEPLOYMENT MONITORING
  # ============================================================================
  post-deployment-check:
    name: Post-Deployment Monitoring
    needs: [deploy-production]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest

    steps:
    - name: Monitor for 5 minutes
      run: |
        echo "Monitoring deployment for stability..."
        for i in {1..10}; do
          sleep 30
          if curl -f http://45.61.58.125:7500/api/health; then
            echo "Health check $i/10 passed"
          else
            echo "Health check $i/10 failed!"
            exit 1
          fi
        done
        echo "All post-deployment checks passed!"