name: Deploy Staging on: push: branches: [main] env: REGISTRY: ghcr.io IMAGE_NAME: falah-mobile STAGING_HOST: 192.168.0.11 jobs: build: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Generate Prisma client run: npx prisma generate - name: Build Next.js app run: npm run build env: DATABASE_URL: "file:./prisma/dev.db" - name: Build Docker image run: | docker build -t ${{ env.IMAGE_NAME }}:staging . docker save ${{ env.IMAGE_NAME }}:staging | gzip > /tmp/${{ env.IMAGE_NAME }}.tar.gz - name: Tag current image as rollback target run: | ssh admin@${{ env.STAGING_HOST }} "\ docker tag ${{ env.IMAGE_NAME }}:staging ${{ env.IMAGE_NAME }}:rollback 2>/dev/null; \ echo 'Rollback tag created'" continue-on-error: true env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - name: Deploy to staging id: deploy run: | scp /tmp/${{ env.IMAGE_NAME }}.tar.gz admin@${{ env.STAGING_HOST }}:/tmp/ ssh admin@${{ env.STAGING_HOST }} << 'ENDSSH' cd /var/services/homes/admin/falah-mobile docker load < /tmp/${{ env.IMAGE_NAME }}.tar.gz docker compose -f docker-compose.staging.yml down docker compose -f docker-compose.staging.yml up -d # Wait for healthy with timeout for i in $(seq 1 60); do sleep 2 HEALTH=\\$(curl -sf http://localhost:4014/mobile/api/health 2>/dev/null) if echo \"\$HEALTH\" | grep -q '\"status\":\"ok\"'; then echo "✅ Staging is healthy" exit 0 fi echo "Waiting... attempt \$i" done echo "❌ Deployment failed health check" exit 1 ENDSSH env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - name: Auto-rollback on failure if: failure() && steps.deploy.outcome == 'failure' run: | echo "🚨 Deployment failed — initiating auto-rollback..." ssh admin@${{ env.STAGING_HOST }} << 'ENDSSH' cd /var/services/homes/admin/falah-mobile if docker images ${{ env.IMAGE_NAME }}:rollback --format '{{.ID}}' | head -1; then echo "🔄 Rolling back to previous image..." docker compose -f docker-compose.staging.yml down docker tag ${{ env.IMAGE_NAME }}:rollback ${{ env.IMAGE_NAME }}:staging docker compose -f docker-compose.staging.yml up -d for i in \$(seq 1 30); do sleep 2 HEALTH=\\$(curl -sf http://localhost:4014/mobile/api/health 2>/dev/null) if echo \"\$HEALTH\" | grep -q '\"status\":\"ok\"'; then echo "✅ Rollback successful — service healthy" exit 0 fi done echo "❌ Rollback also failed — manual intervention required" exit 1 else echo "⚠️ No rollback image found — keeping current state" docker compose -f docker-compose.staging.yml up -d fi ENDSSH env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}