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:
.envwithPROJECT_ID,SERVICE_NAME,REGION, and allNEXT_PUBLIC_FIREBASE_*(and optionallyFIRESTORE_PROJECT_ID). - Does:
- Sources
.envand checks required vars. - Runs Cloud Build with substitutions so the Docker build gets Firebase (and Jira base URL) config.
- After build succeeds, runs Cloud Run deploy with the new image and mounts secrets from Secret Manager.
- Sources
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 buildwith build-args forNEXT_PUBLIC_FIREBASE_*andNEXT_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.
- One step:
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, runnpm run buildinapps/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 bygcloud run deploy --set-secrets=.... - Optional: If you set
FIRESTORE_PROJECT_IDin.env, the script passes it as--set-env-varsso the server uses that project for Firestore (e.g. shared dev DB).
Two deployment “modes”
| What you run | What gets deployed |
|---|---|
./deploy-to-gcp.sh | App only: build image → deploy to Cloud Run |
./deploy-all.sh | App + 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)
| Thing | Where |
|---|---|
| Deploy script | ./deploy-to-gcp.sh |
| Full deploy (app + scheduler) | ./deploy-all.sh |
| Build config | ./cloudbuild.yaml |
| Image definition | ./Dockerfile |
| Env template | env.example → copy to .env |
| Secrets setup | ./setup-secrets.sh (run once per project) |
| Firestore rules/indexes | firebase 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.envin production. -
“Why do I need Firebase config in .env for deploy?”
Sodeploy-to-gcp.shcan pass them as build args. Next.js bakesNEXT_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-rundeploy-to-gcp.shunless 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(seeaccountandproject). - If
accountis a different project’s service account (e.g.firebase-adminsdk@gtc-chikeen-tan8f5-prj...), switch to an account that can usegtc-tools-dev:- User account:
gcloud auth loginthengcloud config set project gtc-tools-dev - Application default (for scripts):
gcloud auth application-default loginand set project as above.
- User account:
- Then run
./deploy-to-gcp.shagain.
- Check:
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.