add postgres to dependencies #2916
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: staging-pipeline | |
| on: | |
| push: | |
| branches: | |
| - staging | |
| # Security: Set minimal default permissions for all jobs | |
| permissions: | |
| contents: read # All jobs can read code, but can't write by default | |
| jobs: | |
| linters-and-build: | |
| name: Run linters and build | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read # Read code | |
| pull-requests: write # Post lint comments | |
| statuses: write # Update check status | |
| steps: | |
| - name: Check out Git repository | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22.18.0 | |
| cache: 'npm' | |
| # ESLint and Prettier must be in `package.json` | |
| - name: Install Node.js dependencies | |
| run: npm ci | |
| - name: Run linters | |
| uses: wearerequired/lint-action@v2.3.0 | |
| with: | |
| eslint: true | |
| #prettier: true | |
| continue_on_error: true | |
| - name: Build project | |
| run: | | |
| echo "🔨 Building TypeScript..." | |
| npm run build | |
| echo "✅ Build complete" | |
| publish: | |
| needs: linters-and-build | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read # Read code | |
| packages: write # Push to GHCR | |
| id-token: write # Cosign signing | |
| outputs: | |
| image-digest: ${{ steps.build.outputs.digest }} | |
| steps: | |
| - name: Check out the repo | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 1 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3.6.0 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3.11.1 | |
| # Performance: Build with cache for faster builds | |
| - name: Build image and push to GitHub Packages | |
| id: build | |
| uses: docker/build-push-action@v6.18.0 | |
| with: | |
| context: . | |
| push: true | |
| tags: | | |
| ghcr.io/giveth/impact-graph:staging | |
| ghcr.io/giveth/impact-graph:${{ github.sha }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| provenance: true | |
| sbom: true | |
| # Security: Sign container image with Cosign | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@v4.0.0 | |
| - name: Sign container image | |
| run: | | |
| cosign sign --yes ghcr.io/giveth/impact-graph@${{ steps.build.outputs.digest }} | |
| deploy: | |
| needs: publish | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read # Read code for actions | |
| id-token: write # Cosign signature verification | |
| steps: | |
| # Security: Verify image signature before deployment | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@v4.0.0 | |
| - name: Verify Image Signature | |
| run: | | |
| echo "🔐 Verifying image signature..." | |
| cosign verify ghcr.io/giveth/impact-graph@${{ needs.publish.outputs.image-digest }} \ | |
| --certificate-identity "${{ github.server_url }}/${{ github.repository }}/.github/workflows/staging-pipeline.yml@${{ github.ref }}" \ | |
| --certificate-oidc-issuer="https://token.actions.githubusercontent.com" | |
| echo "✅ Image signature verified successfully! Image is authentic and untampered." | |
| # Only deploys if signature verification passes | |
| - name: SSH and Pull Verified Image | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.STAGING_HOST_ALL }} | |
| username: ${{ secrets.STAGING_USERNAME_ALL }} | |
| key: ${{ secrets.STAGING_PRIVATE_KEY_ALL }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script: | | |
| cd giveth-all | |
| # Pull the verified image by its immutable SHA | |
| docker pull ghcr.io/giveth/impact-graph@${{ needs.publish.outputs.image-digest }} | |
| # Export the verified SHA for docker-compose to use | |
| echo "IMPACT_GRAPH_IMAGE=ghcr.io/giveth/impact-graph@${{ needs.publish.outputs.image-digest }}" > .env.impact-graph | |
| echo "✅ Verified image pulled: ghcr.io/giveth/impact-graph@${{ needs.publish.outputs.image-digest }}" | |
| migrate: | |
| needs: deploy | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: none # Minimal - only for actions to work | |
| steps: | |
| - name: SSH and Run DB Migrations (one-shot) | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.STAGING_HOST_ALL }} | |
| username: ${{ secrets.STAGING_USERNAME_ALL }} | |
| key: ${{ secrets.STAGING_PRIVATE_KEY_ALL }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script: | | |
| set -e | |
| cd giveth-all | |
| # Load the verified image SHA so migrations run on the exact image being deployed | |
| export $(cat .env.impact-graph | xargs) | |
| echo "🗃️ Running database migrations using verified image: $IMPACT_GRAPH_IMAGE" | |
| # One-shot container: runs migrations, then exits. Non-zero exit fails the deploy. | |
| docker compose run --rm impact-graph-migrations | |
| echo "✅ Migrations complete" | |
| rollout-deploy-1: | |
| needs: migrate | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: none # Minimal - only for actions to work | |
| steps: | |
| - name: SSH and Redeploy | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.STAGING_HOST_ALL }} | |
| username: ${{ secrets.STAGING_USERNAME_ALL }} | |
| key: ${{ secrets.STAGING_PRIVATE_KEY_ALL }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script: | | |
| cd giveth-all | |
| # Load the verified image SHA from environment | |
| export $(cat .env.impact-graph | xargs) | |
| ## Update each backend service one by one | |
| ## First Deployment - uses IMPACT_GRAPH_IMAGE with verified SHA | |
| docker compose up -d --no-deps --force-recreate impact-graph-ql1 | |
| docker compose up -d --no-deps --force-recreate impact-graph-jobs | |
| echo "First deployment phase triggered using verified image: $IMPACT_GRAPH_IMAGE" | |
| rollout-deploy-2: | |
| needs: rollout-deploy-1 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: none # Minimal - only for actions to work | |
| steps: | |
| - name: SSH and Redeploy | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.STAGING_HOST_ALL }} | |
| username: ${{ secrets.STAGING_USERNAME_ALL }} | |
| key: ${{ secrets.STAGING_PRIVATE_KEY_ALL }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script: | | |
| cd giveth-all | |
| # Load the verified image SHA from environment | |
| export $(cat .env.impact-graph | xargs) | |
| ## Update each backend service one by one | |
| ## Second Deployment - uses IMPACT_GRAPH_IMAGE with verified SHA | |
| docker compose up -d --no-deps --force-recreate impact-graph-ql2 | |
| echo "Second deployment phase triggered using verified image: $IMPACT_GRAPH_IMAGE" | |
| rollout-deploy-3: | |
| needs: rollout-deploy-2 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: none # Minimal - only for actions to work | |
| steps: | |
| - name: SSH and Redeploy | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.STAGING_HOST_ALL }} | |
| username: ${{ secrets.STAGING_USERNAME_ALL }} | |
| key: ${{ secrets.STAGING_PRIVATE_KEY_ALL }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script: | | |
| cd giveth-all | |
| # Load the verified image SHA from environment | |
| export $(cat .env.impact-graph | xargs) | |
| ## Update each backend service one by one | |
| ## Third Deployment - uses IMPACT_GRAPH_IMAGE with verified SHA | |
| docker compose up -d --no-deps --force-recreate impact-graph-ql3 | |
| echo "Third deployment phase triggered using verified image: $IMPACT_GRAPH_IMAGE" | |
| rollout-deploy-4: | |
| needs: rollout-deploy-3 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: none # Minimal - only for actions to work | |
| steps: | |
| - name: SSH and Redeploy | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.STAGING_HOST_ALL }} | |
| username: ${{ secrets.STAGING_USERNAME_ALL }} | |
| key: ${{ secrets.STAGING_PRIVATE_KEY_ALL }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script: | | |
| set -e | |
| cd giveth-all | |
| # Load the verified image SHA from environment | |
| export $(cat .env.impact-graph | xargs) | |
| ## Update each backend service one by one | |
| ## Fourth Deployment - uses IMPACT_GRAPH_IMAGE with verified SHA | |
| docker compose up -d --no-deps --force-recreate impact-graph-ql4 | |
| echo "Fourth deployment phase triggered using verified image: $IMPACT_GRAPH_IMAGE" | |
| verify-health: | |
| needs: | |
| - rollout-deploy-1 | |
| - rollout-deploy-2 | |
| - rollout-deploy-3 | |
| - rollout-deploy-4 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: none # Minimal - only for actions to work | |
| steps: | |
| - name: Wait for services to stabilize | |
| run: sleep 60 | |
| - name: SSH and Verify Health | |
| uses: appleboy/ssh-action@v1.2.2 | |
| with: | |
| host: ${{ secrets.STAGING_HOST_ALL }} | |
| username: ${{ secrets.STAGING_USERNAME_ALL }} | |
| key: ${{ secrets.STAGING_PRIVATE_KEY_ALL }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script: | | |
| set -e | |
| cd giveth-all | |
| services=("impact-graph-ql1" "impact-graph-ql2" "impact-graph-ql3" "impact-graph-ql4") | |
| echo "=== Final Health Check for All Services ===" | |
| for svc in "${services[@]}"; do | |
| echo "Checking health for $svc ..." | |
| ok=0 | |
| for i in {1..12}; do # up to ~2 minutes | |
| status=$(docker inspect --format='{{json .State.Health.Status}}' "$svc" 2>/dev/null || echo '""') | |
| if [ "$status" = "\"healthy\"" ]; then | |
| echo "✓ $svc is healthy" | |
| ok=1 | |
| break | |
| fi | |
| echo "Current health: $status; waiting..." | |
| sleep 10 | |
| done | |
| if [ $ok -ne 1 ]; then | |
| echo "::error::$svc did not reach healthy status" | |
| # Get container logs for debugging | |
| docker logs --tail 100 "$svc" || true | |
| exit 1 | |
| fi | |
| done | |
| echo "Checking impact-graph-jobs is running ..." | |
| job_status=$(docker inspect --format='{{json .State.Status}}' impact-graph-jobs 2>/dev/null || echo '""') | |
| if [ "$job_status" != "\"running\"" ]; then | |
| echo "::error::impact-graph-jobs is not running (status: $job_status)" | |
| exit 1 | |
| fi | |
| echo "All services healthy." | |
| docker image prune -f |