name: CI/CD Pipeline — Build, Test & Deploy to Ghayma Cloud on: push: branches: [main] pull_request: branches: [main] env: REGISTRY: registry.ghaymah.systems IMAGE_NAME: ghayma-api STAGING_APP: ghayma-api-staging PRODUCTION_APP: ghayma-api-production jobs: # ────────────────────────────────────────────── # Job 1: Build & Test # ────────────────────────────────────────────── build-and-test: name: 🔨 Build & Test runs-on: ubuntu-latest steps: - name: Checkout code 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: Run syntax check run: node -c src/index.js - name: Run health check test run: | # Start the server in the background node src/index.js & SERVER_PID=$! sleep 3 # Test health endpoint HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/health) if [ "$HTTP_STATUS" -ne 200 ]; then echo "❌ Health check failed with status $HTTP_STATUS" kill $SERVER_PID exit 1 fi echo "✅ Health check passed (HTTP $HTTP_STATUS)" kill $SERVER_PID # ────────────────────────────────────────────── # Job 2: Build Docker Image & Push to Registry # ────────────────────────────────────────────── docker-build-push: name: 🐳 Build & Push Docker Image runs-on: ubuntu-latest needs: build-and-test outputs: image-tag: ${{ steps.meta.outputs.tags }} image-digest: ${{ steps.build-push.outputs.digest }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Log in to Ghayma Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.GHAYMAH_REGISTRY_USERNAME }} password: ${{ secrets.GHAYMAH_REGISTRY_PASSWORD }} - name: Extract Docker metadata (tags & labels) id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ secrets.GHAYMAH_REGISTRY_USERNAME }}/${{ env.IMAGE_NAME }} tags: | type=sha,prefix= type=ref,event=branch type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Docker image id: build-push uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - name: Print image details run: | echo "✅ Image pushed successfully" echo "📦 Tags: ${{ steps.meta.outputs.tags }}" echo "🔒 Digest: ${{ steps.build-push.outputs.digest }}" # ────────────────────────────────────────────── # Job 3: Deploy to Staging (automatic) # ────────────────────────────────────────────── deploy-staging: name: 🟡 Deploy to Staging runs-on: ubuntu-latest needs: docker-build-push if: github.ref == 'refs/heads/main' environment: name: staging url: https://staging.ghaymah.systems steps: - name: Install Ghayma CLI run: | curl -fsSL https://cli.ghaymah.systems/install.sh | bash echo "$HOME/.ghaymah/bin" >> $GITHUB_PATH - name: Authenticate with Ghayma Cloud run: | ghaymah auth login \ --token ${{ secrets.GHAYMAH_API_TOKEN }} - name: Deploy to Staging environment run: | ghaymah app deploy ${{ env.STAGING_APP }} \ --image ${{ env.REGISTRY }}/${{ secrets.GHAYMAH_REGISTRY_USERNAME }}/${{ env.IMAGE_NAME }}:latest \ --env NODE_ENV=staging \ --env PORT=3000 \ --region me-central-1 \ --wait - name: Verify Staging deployment run: | sleep 10 HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://staging.ghaymah.systems/health) if [ "$HTTP_STATUS" -ne 200 ]; then echo "❌ Staging health check failed (HTTP $HTTP_STATUS)" exit 1 fi echo "✅ Staging deployment verified (HTTP $HTTP_STATUS)" # ────────────────────────────────────────────── # Job 4: Manual Approval Gate # ────────────────────────────────────────────── # This job uses a GitHub Environment called # "production-approval" with "Required reviewers" # protection rule. The workflow PAUSES here and # waits for a designated team member to approve. # ────────────────────────────────────────────── manual-approval: name: ⏸️ Manual Approval Gate runs-on: ubuntu-latest needs: deploy-staging if: github.ref == 'refs/heads/main' environment: name: production-approval # Configure "Required reviewers" on this environment steps: - name: Approval checkpoint run: | echo "✅ Production deployment has been approved!" echo "👤 Approved by: ${{ github.actor }}" echo "📦 Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" echo "⏰ Approved at: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" # ────────────────────────────────────────────── # Job 5: Deploy to Production (after approval) # ────────────────────────────────────────────── deploy-production: name: 🟢 Deploy to Production runs-on: ubuntu-latest needs: manual-approval if: github.ref == 'refs/heads/main' environment: name: production url: https://api.ghaymah.systems steps: - name: Install Ghayma CLI run: | curl -fsSL https://cli.ghaymah.systems/install.sh | bash echo "$HOME/.ghaymah/bin" >> $GITHUB_PATH - name: Authenticate with Ghayma Cloud run: | ghaymah auth login \ --token ${{ secrets.GHAYMAH_API_TOKEN }} - name: Deploy to Production environment run: | ghaymah app deploy ${{ env.PRODUCTION_APP }} \ --image ${{ env.REGISTRY }}/${{ secrets.GHAYMAH_REGISTRY_USERNAME }}/${{ env.IMAGE_NAME }}:latest \ --env NODE_ENV=production \ --env PORT=3000 \ --region me-central-1 \ --replicas 2 \ --wait - name: Verify Production deployment run: | sleep 10 HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://api.ghaymah.systems/health) if [ "$HTTP_STATUS" -ne 200 ]; then echo "❌ Production health check failed (HTTP $HTTP_STATUS)" exit 1 fi echo "✅ Production deployment verified (HTTP $HTTP_STATUS)" - name: Notify success run: | echo "🚀 Production deployment complete!" echo "📦 Image: ${{ env.REGISTRY }}/${{ secrets.GHAYMAH_REGISTRY_USERNAME }}/${{ env.IMAGE_NAME }}:latest" echo "🌐 URL: https://api.ghaymah.systems"