Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 47 additions & 24 deletions .github/workflows/google-cloudrun-docker.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# This workflow builds and pushes Docker containers to Google Artifact Registry
# and deploys both backend and frontend on Cloud Run when a commit is pushed to the "production"
# branch.
# Build and deploy QueryPal to Cloud Run.
# Runs on pushes to the production branch.
#
# Infrastructure changes (VPC connector, Secret Manager, IAM) are managed by
# Terraform in the terraform/ directory and must be applied before first deploy.

name: 'Build and Deploy QueryPal to Cloud Run'

Expand All @@ -16,6 +18,11 @@ env:
BACKEND_SERVICE: 'querypal-backend'
FRONTEND_SERVICE: 'querypal-frontend'
WORKLOAD_IDENTITY_PROVIDER: 'projects/874216619692/locations/global/workloadIdentityPools/github/providers/querypal'
# Short name of the Cloud Run SA and VPC connector created by Terraform.
# The full SA email is constructed inline in flags using ${{ env.PROJECT_ID }}
# because GitHub Actions does not interpolate env vars inside the env: block.
CLOUD_RUN_SA_NAME: 'querypal-cloudrun-sa'
VPC_CONNECTOR: 'querypal-vpc-connector'

jobs:
deploy:
Expand All @@ -29,80 +36,96 @@ jobs:
- name: 'Checkout'
uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4

# Configure Workload Identity Federation and generate an access token.
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2
with:
workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}'
service_account: 'github-actions@gen-lang-client-0698668474.iam.gserviceaccount.com'
service_account: 'github-actions@${{ env.PROJECT_ID }}.iam.gserviceaccount.com'

# Set up Cloud SDK
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@98ddc00a17442e89a24bbf282954a3b65ce6d200' # google-github-actions/setup-gcloud@v2

# Configure Docker to use gcloud as a credential helper
- name: 'Configure Docker for GCR'
run: |-
gcloud auth configure-docker --quiet
run: gcloud auth configure-docker --quiet

# ── Backend ──────────────────────────────────────────────────────────────

# Build and Push Backend Container
- name: 'Build and Push Backend Container'
run: |-
cd backend
DOCKER_TAG="gcr.io/${{ env.PROJECT_ID }}/${{ env.BACKEND_SERVICE }}:${{ github.sha }}"
docker build --tag "${DOCKER_TAG}" --platform linux/amd64 .
docker push "${DOCKER_TAG}"

# Deploy Backend to Cloud Run
- id: 'deploy-backend'
name: 'Deploy Backend to Cloud Run'
uses: 'google-github-actions/deploy-cloudrun@33553064113a37d688aa6937bacbdc481580be17' # google-github-actions/deploy-cloudrun@v2
with:
service: '${{ env.BACKEND_SERVICE }}'
region: '${{ env.REGION }}'
image: 'gcr.io/${{ env.PROJECT_ID }}/${{ env.BACKEND_SERVICE }}:${{ github.sha }}'
# Non-secret runtime configuration only.
env_vars: |
ENVIRONMENT=production
AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET=${{ secrets.AZURE_CLIENT_SECRET }}
ARM_SCOPE=https://management.azure.com/.default
GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }}
DB_USER=${{ secrets.DB_USER }}
DB_PASS=${{ secrets.DB_PASS }}
DB_NAME=querypal
DB_UNIX_SOCKET=/cloudsql/gen-lang-client-0698668474:europe-west1:querypal-db
DB_UNIX_SOCKET=/cloudsql/${{ env.PROJECT_ID }}:${{ env.REGION }}:querypal-db
# Sensitive values are read directly from Secret Manager at runtime.
# Secret must exist before first deploy (created by terraform/secrets.tf).
secrets: |
AZURE_TENANT_ID=querypal-azure-tenant-id:latest
AZURE_CLIENT_ID=querypal-azure-client-id:latest
AZURE_CLIENT_SECRET=querypal-azure-client-secret:latest
GEMINI_API_KEY=querypal-gemini-api-key:latest
DB_USER=querypal-db-user:latest
DB_PASS=querypal-db-pass:latest
flags: |
--port=8000
--add-cloudsql-instances=gen-lang-client-0698668474:europe-west1:querypal-db
--service-account=${{ env.CLOUD_RUN_SA_NAME }}@${{ env.PROJECT_ID }}.iam.gserviceaccount.com
--add-cloudsql-instances=${{ env.PROJECT_ID }}:${{ env.REGION }}:querypal-db
--vpc-connector=${{ env.VPC_CONNECTOR }}
--vpc-egress=private-ranges-only
--ingress=internal
--allow-unauthenticated

# Build and Push Frontend Container
# ── Frontend ─────────────────────────────────────────────────────────────

- name: 'Build and Push Frontend Container'
run: |-
cd frontend
DOCKER_TAG="gcr.io/${{ env.PROJECT_ID }}/${{ env.FRONTEND_SERVICE }}:${{ github.sha }}"
# VITE_API_BASE_URL=/api tells the React app to send all API calls to
# the /api/* path on its own origin instead of a full backend URL.
# Nginx then proxies those requests to the internal backend service.
docker build --tag "${DOCKER_TAG}" --platform linux/amd64 \
--build-arg VITE_API_BASE_URL=${{ steps.deploy-backend.outputs.url }} \
--build-arg VITE_API_BASE_URL=/api \
--build-arg VITE_AZURE_REDIRECT_URI=https://querypal.virtonomy.io \
.
docker push "${DOCKER_TAG}"

# Deploy Frontend to Cloud Run
- id: 'deploy-frontend'
name: 'Deploy Frontend to Cloud Run'
uses: 'google-github-actions/deploy-cloudrun@33553064113a37d688aa6937bacbdc481580be17' # google-github-actions/deploy-cloudrun@v2
with:
service: '${{ env.FRONTEND_SERVICE }}'
region: '${{ env.REGION }}'
image: 'gcr.io/${{ env.PROJECT_ID }}/${{ env.FRONTEND_SERVICE }}:${{ github.sha }}'
# BACKEND_URL is the internal Cloud Run URL; nginx uses it at runtime to
# proxy /api/* requests to the backend (which is not publicly reachable).
env_vars: |
BACKEND_URL=${{ steps.deploy-backend.outputs.url }}
flags: |
--port=4000
--service-account=${{ env.CLOUD_RUN_SA_NAME }}@${{ env.PROJECT_ID }}.iam.gserviceaccount.com
--vpc-connector=${{ env.VPC_CONNECTOR }}
--vpc-egress=all-traffic
--ingress=all
--allow-unauthenticated

# Show output URLs
# ── Summary ───────────────────────────────────────────────────────────────

- name: 'Show deployment URLs'
run: |-
echo "Backend URL: ${{ steps.deploy-backend.outputs.url }}"
echo "Frontend URL: ${{ steps.deploy-frontend.outputs.url }}"
echo "Backend URL: ${{ steps.deploy-backend.outputs.url }} (internal only)"
Loading
Loading