CD Pipeline - UnHackable #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CD Pipeline - UnHackable | |
| on: | |
| # Triggered after CI completes successfully | |
| workflow_run: | |
| workflows: ["CI Pipeline - UnHackable"] | |
| types: | |
| - completed | |
| branches: [main, master] | |
| # Manual trigger | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: 'Deployment environment' | |
| required: true | |
| default: 'staging' | |
| type: choice | |
| options: | |
| - staging | |
| - production | |
| env: | |
| IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/unhackable | |
| jobs: | |
| # ============================================ | |
| # Stage 1: Deploy to Staging (Kubernetes) | |
| # WHY: Validates deployment in non-production environment, | |
| # allows testing before production release | |
| # ============================================ | |
| deploy-staging: | |
| name: 🚀 Deploy to Staging | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} | |
| environment: staging | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup kubectl | |
| uses: azure/setup-kubectl@v3 | |
| with: | |
| version: 'v1.28.0' | |
| # For demo: Show what would be deployed | |
| - name: Show deployment configuration | |
| run: | | |
| echo "📦 Deploying image: ${{ env.IMAGE_NAME }}:latest" | |
| echo "" | |
| echo "=== Deployment Manifest ===" | |
| sed "s|IMAGE_PLACEHOLDER|${{ env.IMAGE_NAME }}:latest|g" k8s/deployment.yaml | |
| echo "" | |
| echo "=== Service Manifest ===" | |
| cat k8s/service.yaml | |
| # NOTE: In production, you would configure KUBE_CONFIG secret and run: | |
| # - name: Configure kubeconfig | |
| # run: | | |
| # mkdir -p $HOME/.kube | |
| # echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > $HOME/.kube/config | |
| # | |
| # - name: Deploy to Kubernetes | |
| # run: | | |
| # sed -i "s|IMAGE_PLACEHOLDER|${{ env.IMAGE_NAME }}:latest|g" k8s/deployment.yaml | |
| # kubectl apply -f k8s/deployment.yaml -n staging | |
| # kubectl apply -f k8s/service.yaml -n staging | |
| # kubectl rollout status deployment/unhackable -n staging | |
| - name: Deployment ready | |
| run: | | |
| echo "✅ Staging deployment manifests validated!" | |
| echo "📝 To deploy to a real cluster, configure KUBE_CONFIG secret" | |
| # ============================================ | |
| # Stage 2: DAST - Dynamic Application Security Testing | |
| # WHY: Tests running application for vulnerabilities, | |
| # identifies runtime security issues | |
| # ============================================ | |
| dast: | |
| name: 🔐 DAST - Security Scan | |
| runs-on: ubuntu-latest | |
| needs: deploy-staging | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| # Run container locally for DAST scan | |
| - name: Start application for DAST | |
| run: | | |
| docker pull ${{ env.IMAGE_NAME }}:latest || echo "Using local build" | |
| docker run -d --name dast-target -p 3000:3000 \ | |
| -e DATABASE_URI="${{ secrets.DATABASE_URI }}" \ | |
| -e NEXT_PUBLIC_RPC_URL="${{ secrets.NEXT_PUBLIC_RPC_URL }}" \ | |
| -e GOOGLE_CLIENT_ID="${{ secrets.GOOGLE_CLIENT_ID }}" \ | |
| -e GOOGLE_CLIENT_SECRET="${{ secrets.GOOGLE_CLIENT_SECRET }}" \ | |
| -e AUTH_SECRET="${{ secrets.AUTH_SECRET }}" \ | |
| -e NEXTAUTH_URL="http://localhost:3000" \ | |
| ${{ env.IMAGE_NAME }}:latest || \ | |
| echo "⚠️ Could not pull image, skipping DAST" | |
| sleep 15 | |
| # OWASP ZAP Baseline Scan (lightweight scan) | |
| - name: OWASP ZAP Baseline Scan | |
| uses: zaproxy/action-baseline@v0.12.0 | |
| if: success() | |
| continue-on-error: true | |
| with: | |
| target: 'http://localhost:3000' | |
| rules_file_name: '.zap/rules.tsv' | |
| cmd_options: '-a -j' | |
| - name: Upload DAST Report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: dast-report | |
| path: report_html.html | |
| retention-days: 7 | |
| - name: Cleanup | |
| if: always() | |
| run: docker rm -f dast-target || true | |
| # ============================================ | |
| # Stage 3: Deploy to Production | |
| # WHY: Final deployment after security validation, | |
| # requires manual approval for safety | |
| # ============================================ | |
| deploy-production: | |
| name: 🚀 Deploy to Production | |
| runs-on: ubuntu-latest | |
| needs: dast | |
| environment: production # Requires manual approval in GitHub | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Production deployment info | |
| run: | | |
| echo "🚀 Production Deployment" | |
| echo "========================" | |
| echo "" | |
| echo "Image: ${{ env.IMAGE_NAME }}:latest" | |
| echo "Commit: ${{ github.sha }}" | |
| echo "" | |
| echo "In a real setup, this would:" | |
| echo "1. Connect to production Kubernetes cluster" | |
| echo "2. Apply deployment manifests" | |
| echo "3. Perform rolling update" | |
| echo "4. Verify deployment health" | |
| echo "" | |
| echo "✅ Production deployment pipeline validated!" |