Deploy Production #18
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: Deploy Production | |
| on: | |
| workflow_run: | |
| workflows: ["Build and Push Docker Images"] | |
| types: [completed] | |
| branches: [main, "v*"] | |
| push: | |
| branches: [main] | |
| paths: | |
| - "deployment/cloud/gcp/infrastructure/**" | |
| - "pixi.toml" | |
| workflow_dispatch: # Allow manual triggers | |
| concurrency: | |
| group: deploy-production | |
| cancel-in-progress: true | |
| env: | |
| GCP_PROJECT: biocirv-470318 | |
| GCP_REGION: us-west1 | |
| DEPLOY_ENV: production | |
| # These values come from Pulumi outputs after bootstrapping the production stack. | |
| # Run `DEPLOY_ENV=production pixi run -e deployment cloud-outputs` to verify. | |
| # NOTE: Update after first production deploy (Phase 4 bootstrap). | |
| WIF_PROVIDER: "projects/194468397458/locations/global/workloadIdentityPools/github-actions-production/providers/github-oidc-production" | |
| DEPLOYER_SA: "biocirv-prod-gh-deploy@biocirv-470318.iam.gserviceaccount.com" | |
| jobs: | |
| # --- Preview (runs on pushes to main that touch infrastructure) --- | |
| preview: | |
| name: Pulumi Preview (Production) | |
| if: github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ env.WIF_PROVIDER }} | |
| service_account: ${{ env.DEPLOYER_SA }} | |
| - name: Set up pixi | |
| uses: prefix-dev/setup-pixi@v0.9.4 | |
| with: | |
| pixi-version: v0.63.2 | |
| environments: deployment | |
| - name: Preview infrastructure changes | |
| run: pixi run -e deployment cloud-plan-direct | |
| # --- Compute image tag from docker-build completion or workflow_dispatch --- | |
| image-tag: | |
| name: Compute Image Tag | |
| if: >- | |
| (github.event_name == 'workflow_dispatch') || (github.event_name == | |
| 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'release') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: read | |
| outputs: | |
| image_tag: ${{ steps.vars.outputs.image_tag }} | |
| steps: | |
| - name: Derive image tag | |
| id: vars | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_run" ]; then | |
| # The docker-build was triggered by a release — use short SHA | |
| SHA="${{ github.event.workflow_run.head_sha }}" | |
| echo "image_tag=${SHA::7}" >> "$GITHUB_OUTPUT" | |
| echo "Resolved image tag from docker-build: ${SHA::7}" | |
| else | |
| # workflow_dispatch: query the last successful docker-build run | |
| # on main to get the SHA of the most recently built image. | |
| SHA=$(gh api \ | |
| "repos/${{ github.repository }}/actions/workflows/docker-build.yml/runs?branch=main&status=success&per_page=1" \ | |
| --jq '.workflow_runs[0].head_sha') | |
| if [ -z "$SHA" ] || [ "$SHA" = "null" ]; then | |
| echo "::error::No successful docker-build run found" | |
| exit 1 | |
| fi | |
| echo "image_tag=${SHA::7}" >> "$GITHUB_OUTPUT" | |
| echo "Resolved image tag: ${SHA::7}" | |
| fi | |
| deploy-infrastructure: | |
| name: Deploy Infrastructure (Pulumi) | |
| needs: image-tag | |
| runs-on: ubuntu-latest | |
| environment: production | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ env.WIF_PROVIDER }} | |
| service_account: ${{ env.DEPLOYER_SA }} | |
| - name: Set up pixi | |
| uses: prefix-dev/setup-pixi@v0.9.4 | |
| with: | |
| pixi-version: v0.63.2 | |
| environments: deployment | |
| - name: Deploy infrastructure | |
| env: | |
| IMAGE_TAG: ${{ needs.image-tag.outputs.image_tag }} | |
| # When the production frontend URL is known, add: | |
| # CORS_ORIGINS: '["https://PRODUCTION_FRONTEND_URL"]' | |
| run: pixi run -e deployment cloud-deploy-direct | |
| run-migrations: | |
| name: Run Database Migrations | |
| needs: [image-tag, deploy-infrastructure] | |
| runs-on: ubuntu-latest | |
| environment: production | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ env.WIF_PROVIDER }} | |
| service_account: ${{ env.DEPLOYER_SA }} | |
| - name: Set up pixi | |
| uses: prefix-dev/setup-pixi@v0.9.4 | |
| with: | |
| pixi-version: v0.63.2 | |
| environments: deployment | |
| - name: Run Alembic migrations | |
| env: | |
| IMAGE_TAG: ${{ needs.image-tag.outputs.image_tag }} | |
| run: pixi run -e deployment cloud-migrate-ci | |
| update-services: | |
| name: Force Cloud Run Revision Updates | |
| needs: [image-tag, run-migrations] | |
| runs-on: ubuntu-latest | |
| environment: production | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ env.WIF_PROVIDER }} | |
| service_account: ${{ env.DEPLOYER_SA }} | |
| - name: Set up pixi | |
| uses: prefix-dev/setup-pixi@v0.9.4 | |
| with: | |
| pixi-version: v0.63.2 | |
| environments: deployment | |
| - name: Force new Cloud Run revisions | |
| env: | |
| IMAGE_TAG: ${{ needs.image-tag.outputs.image_tag }} | |
| run: pixi run -e deployment cloud-update-services | |
| validate-deployment: | |
| name: Validate Deployment Health | |
| needs: [image-tag, update-services] | |
| runs-on: ubuntu-latest | |
| environment: production | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Authenticate to GCP | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ env.WIF_PROVIDER }} | |
| service_account: ${{ env.DEPLOYER_SA }} | |
| - name: Set up Cloud SDK | |
| uses: google-github-actions/setup-gcloud@v2 | |
| - name: Validate services are healthy | |
| run: bash scripts/validate-deployment.sh | |
| timeout-minutes: 10 |