Always keep a caster's signature attack cantrip #7
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 to Cloud Run | |
| # Builds the single-container app (SPA + API + WebSocket) and deploys it to | |
| # Cloud Run, wired to Cloud SQL (Postgres+pgvector), Memorystore (Redis, via a | |
| # Serverless VPC connector), and a GCS bucket for generated images. | |
| # One-time GCP/GitHub setup is in DEPLOY.md. | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| env: | |
| SERVICE: ai-dm | |
| REGION: europe-west2 # London (infra + Gemini DM + Imagen) | |
| # The image-consistency model (gemini-2.5-flash-image) isn't served in | |
| # europe-west2, so reference-conditioned calls go to europe-west1. | |
| EDIT_LOCATION: europe-west1 | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - run: npm ci | |
| - run: npm run build --workspace @ai-dm/engine # other packages resolve @ai-dm/engine via its dist | |
| - run: npm run typecheck | |
| - run: npm test | |
| deploy: | |
| needs: test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # required for Workload Identity Federation | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - id: auth | |
| uses: google-github-actions/auth@v2 | |
| with: | |
| workload_identity_provider: ${{ secrets.WIF_PROVIDER }} | |
| service_account: ${{ secrets.DEPLOY_SERVICE_ACCOUNT }} | |
| - uses: google-github-actions/setup-gcloud@v2 | |
| # Build the image and push to Artifact Registry (repo provisioned by | |
| # Terraform — see infra/), then deploy it to Cloud Run. | |
| - name: Build & push image | |
| run: | | |
| gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet | |
| echo "IMAGE=${REGION}-docker.pkg.dev/${{ secrets.GCP_PROJECT }}/aidm/ai-dm:${GITHUB_SHA}" >> "$GITHUB_ENV" | |
| - run: docker build -t "$IMAGE" . | |
| - run: docker push "$IMAGE" | |
| # Env vars use the ^##^ delimiter so URLs with special characters pass | |
| # through cleanly. Live session state is shared in Redis with a per-session | |
| # lock, so it's safe to scale out: min-instances=1 (warm), max-instances=5 | |
| # (scale under load). --timeout=3600 keeps WebSockets long-lived. | |
| - name: Deploy | |
| run: | | |
| gcloud run deploy "$SERVICE" \ | |
| --image "$IMAGE" \ | |
| --project "${{ secrets.GCP_PROJECT }}" \ | |
| --region "$REGION" \ | |
| --platform managed \ | |
| --allow-unauthenticated \ | |
| --min-instances=1 --max-instances=5 \ | |
| --timeout=3600 --memory=1Gi --cpu=1 \ | |
| --execution-environment=gen2 \ | |
| --service-account "${{ secrets.RUNTIME_SERVICE_ACCOUNT }}" \ | |
| --add-cloudsql-instances "${{ secrets.CLOUDSQL_INSTANCE }}" \ | |
| --vpc-connector "${{ secrets.VPC_CONNECTOR }}" \ | |
| --vpc-egress private-ranges-only \ | |
| --add-volume=name=images,type=cloud-storage,bucket=${{ secrets.IMAGE_BUCKET }} \ | |
| --add-volume-mount=volume=images,mount-path=/images \ | |
| --set-env-vars="^##^DM_PROVIDER=gemini##IMAGE_PROVIDER=gemini##GEMINI_MODEL=gemini-2.5-flash##GCP_PROJECT=${{ secrets.GCP_PROJECT }}##GCP_LOCATION=$REGION##GEMINI_EDIT_LOCATION=$EDIT_LOCATION##IMAGE_DIR=/images##DATABASE_URL=${{ secrets.DATABASE_URL }}##REDIS_URL=${{ secrets.REDIS_URL }}" | |
| - name: Show URL | |
| run: gcloud run services describe "$SERVICE" --project "${{ secrets.GCP_PROJECT }}" --region "$REGION" --format='value(status.url)' |