← back to Watches

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

165 lines

name: Production CI/CD Pipeline

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

env:
  NODE_VERSION: '22'
  SERVER_IP: 45.61.58.125
  APP_PORT: 7600
  PM2_APP_NAME: omega-watches

jobs:
  test:
    name: Run Tests & Quality Checks
    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=high || true

    - name: Check code quality
      run: |
        echo "Running code quality checks..."
        npx eslint --init || echo "ESLint not configured"

    - name: Test API endpoints
      run: |
        echo "Starting server for testing..."
        node server.js &
        SERVER_PID=$!
        sleep 5

        echo "Testing health endpoint..."
        curl -f http://localhost:7600/api/health || exit 1

        echo "Testing watches endpoint..."
        curl -f http://localhost:7600/api/watches?limit=5 || exit 1

        echo "Testing statistics endpoint..."
        curl -f http://localhost:7600/api/statistics || exit 1

        kill $SERVER_PID

    - name: Build frontend
      run: |
        npm run build
        ls -la dist/

    - name: Archive build artifacts
      uses: actions/upload-artifact@v4
      with:
        name: production-build
        path: dist/
        retention-days: 30

  deploy-production:
    name: Deploy to Production
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
    environment: production

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

    - name: Download build artifacts
      uses: actions/download-artifact@v4
      with:
        name: production-build
        path: dist/

    - name: Setup SSH
      run: |
        mkdir -p ~/.ssh
        echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
        chmod 600 ~/.ssh/id_rsa
        ssh-keyscan -H ${{ env.SERVER_IP }} >> ~/.ssh/known_hosts

    - name: Deploy to server
      run: |
        echo "Deploying to production server..."

        # Create backup before deployment
        ssh root@${{ env.SERVER_IP }} "cd /root/Projects/watches && npm run backup || true"

        # Sync files (excluding node_modules and data)
        rsync -avz --exclude 'node_modules' \
                   --exclude 'data/backups' \
                   --exclude '.git' \
                   --exclude 'logs' \
                   ./ root@${{ env.SERVER_IP }}:/root/Projects/watches/

        # Install dependencies and restart
        ssh root@${{ env.SERVER_IP }} "
          cd /root/Projects/watches && \
          npm ci --production && \
          pm2 restart ${{ env.PM2_APP_NAME }} || pm2 start server.js --name ${{ env.PM2_APP_NAME }} && \
          pm2 save
        "

    - name: Health check after deployment
      run: |
        echo "Waiting for service to start..."
        sleep 10

        echo "Checking health endpoint..."
        for i in {1..5}; do
          if curl -f http://${{ env.SERVER_IP }}:${{ env.APP_PORT }}/api/health; then
            echo "✓ Service is healthy!"
            exit 0
          fi
          echo "Attempt $i failed, retrying..."
          sleep 5
        done

        echo "✗ Health check failed!"
        exit 1

    - name: Notify deployment status
      if: always()
      run: |
        if [ "${{ job.status }}" == "success" ]; then
          echo "✅ Deployment successful!"
        else
          echo "❌ Deployment failed!"
        fi

  rollback:
    name: Rollback Deployment
    runs-on: ubuntu-latest
    if: failure()
    environment: production

    steps:
    - name: Setup SSH
      run: |
        mkdir -p ~/.ssh
        echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
        chmod 600 ~/.ssh/id_rsa
        ssh-keyscan -H ${{ env.SERVER_IP }} >> ~/.ssh/known_hosts

    - name: Restore from backup
      run: |
        ssh root@${{ env.SERVER_IP }} "
          cd /root/Projects/watches && \
          npm run restore-latest-backup && \
          pm2 restart ${{ env.PM2_APP_NAME }}
        "