Skip to main content

How Deployment Works

This doc explains what happens when you run ./deploy-to-gcp.sh and how the pieces fit together. Useful for new developers and when debugging deploy issues.


High-level flow (one picture)

┌─────────────────────────────────────────────────────────────────────────────────┐
│ YOUR MACHINE │
│ │
│ .env (PROJECT_ID, Firebase config, etc.) │
│ │ │
│ ▼ │
│ ./deploy-to-gcp.sh │
│ │ │
│ ├── 1. Reads .env (required: PROJECT_ID, SERVICE_NAME, REGION, │
│ │ NEXT_PUBLIC_FIREBASE_*) │
│ │ │
│ ├── 2. gcloud builds submit ... --config=cloudbuild.yaml │
│ │ │ │
│ │ │ Sends repo + build args to GCP │
│ │ ▼ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ GOOGLE CLOUD BUILD (in your GCP project) │ │
│ │ │ │ │
│ │ │ • Runs Dockerfile (multi-stage build) │ │
│ │ │ • Installs deps, builds Next.js (standalone output) │ │
│ │ │ • Bakes NEXT_PUBLIC_* into the client bundle at build time │ │
│ │ │ • Produces a Docker image │ │
│ │ │ • Pushes image to: gcr.io/<PROJECT_ID>/<SERVICE_NAME> │ │
│ │ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ └── 3. gcloud run deploy <SERVICE_NAME> ... │
│ │ │
│ │ Tells Cloud Run: "run this image, inject these secrets" │
│ ▼ │
└──────────────────┼─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────────┐
│ GOOGLE CLOUD RUN (same project, same region) │
│ │
│ • Pulls image from Container Registry (gcr.io/...) │
│ • Injects secrets from Secret Manager (JIRA_*, BITBUCKET_*, FIREBASE_*, etc.) │
│ • Optionally sets FIRESTORE_PROJECT_ID from .env (to use shared dev Firestore) │
│ • Serves your app at https://<SERVICE_NAME>-xxxxx-<REGION>.a.run.app │
└─────────────────────────────────────────────────────────────────────────────────┘

So in short: your script builds the app in the cloud (Cloud Build), then tells Cloud Run to run that image and attach secrets.


Step-by-step: what each part does

1. deploy-to-gcp.sh (orchestrator)

  • Location: repo root
  • Needs: .env with PROJECT_ID, SERVICE_NAME, REGION, and all NEXT_PUBLIC_FIREBASE_* (and optionally FIRESTORE_PROJECT_ID).
  • Does:
    1. Sources .env and checks required vars.
    2. Runs Cloud Build with substitutions so the Docker build gets Firebase (and Jira base URL) config.
    3. After build succeeds, runs Cloud Run deploy with the new image and mounts secrets from Secret Manager.

So: no local Docker needed; the image is built on GCP.

2. cloudbuild.yaml (Cloud Build config)

  • Location: repo root
  • Used by: gcloud builds submit --config=cloudbuild.yaml
  • Does:
    • One step: docker build with build-args for NEXT_PUBLIC_FIREBASE_* and NEXT_PUBLIC_JIRA_BASE_URL.
    • Tags the image as _IMAGE_NAME (e.g. gcr.io/<PROJECT_ID>/gamuda-jira-tracker).
    • Pushes that image so Cloud Run can pull it.

Substitutions are filled by deploy-to-gcp.sh when it calls gcloud builds submit.

3. Dockerfile (how the app is built)

  • Location: repo root
  • Stages:
    • deps: Install npm dependencies (root + apps/web + packages).
    • builder: Copy app + packages, set NEXT_PUBLIC_* from ARGs, run npm run build in apps/web (Next.js standalone output).
    • runner: Minimal image that runs node apps/web/server.js (standalone server).

Important: NEXT_PUBLIC_* must be set at build time because they are inlined into the client bundle. That’s why they’re passed as build-args in cloudbuild.yaml and from .env in the deploy script.

4. Cloud Run (runtime)

  • Secrets: Not in the image. They come from Secret Manager (e.g. jira-api-token, firebase-service-account, etc.) and are mounted as env vars by gcloud run deploy --set-secrets=....
  • Optional: If you set FIRESTORE_PROJECT_ID in .env, the script passes it as --set-env-vars so the server uses that project for Firestore (e.g. shared dev DB).

Two deployment “modes”

What you runWhat gets deployed
./deploy-to-gcp.shApp only: build image → deploy to Cloud Run
./deploy-all.shApp + scheduler: same as above, then Terraform to deploy Cloud Scheduler (e.g. daily sync job)

Use deploy-to-gcp.sh for “just ship the app”. Use deploy-all.sh when you also want the scheduled sync infrastructure.


Where things live (quick reference)

ThingWhere
Deploy script./deploy-to-gcp.sh
Full deploy (app + scheduler)./deploy-all.sh
Build config./cloudbuild.yaml
Image definition./Dockerfile
Env templateenv.example → copy to .env
Secrets setup./setup-secrets.sh (run once per project)
Firestore rules/indexesfirebase deploy --only firestore:rules,firestore:indexes

Common “I don’t understand” answers

  • “Where is the app built?”
    In Google Cloud Build (in your GCP project). Your machine only triggers the build and deploy; it doesn’t run Docker locally.

  • “Where do secrets come from?”
    Secret Manager. You put them there with ./setup-secrets.sh. Cloud Run injects them at runtime; they are not in the image or in .env in production.

  • “Why do I need Firebase config in .env for deploy?”
    So deploy-to-gcp.sh can pass them as build args. Next.js bakes NEXT_PUBLIC_* into the client bundle at build time.

  • “What if I only change Firestore rules?”
    Deploy rules/indexes only:
    firebase deploy --only firestore:rules,firestore:indexes --project=$PROJECT_ID
    No need to re-run deploy-to-gcp.sh unless you changed app code or env/secrets.

  • “Deploy fails: forbidden from accessing the bucket” or “does not have permission to access project”
    gcloud is using the wrong identity. Deploy must run as a user or service account that has access to the GCP project in .env (e.g. gtc-tools-dev).

    • Check: gcloud config list (see account and project).
    • If account is a different project’s service account (e.g. firebase-adminsdk@gtc-chikeen-tan8f5-prj...), switch to an account that can use gtc-tools-dev:
      • User account: gcloud auth login then gcloud config set project gtc-tools-dev
      • Application default (for scripts): gcloud auth application-default login and set project as above.
    • Then run ./deploy-to-gcp.sh again.

For first-time project setup (GCP project, Firebase, secrets), use Getting started → Quick start or Deployment guide in this docs site. This page is about how the deploy pipeline works once that’s done.