Engineering
Deployment
Cloud Run, Cloud SQL, Secret Manager, and the hard-won learnings baked in from day one.
Targets
- GCP project
screver-market-access, regioneurope-west6. - Cloud Run services per repo, each with a
-stagingand a production variant. - Cloud SQL Postgres 17 (
screver-ma-db-staging/-prod), reached via the Cloud SQL Proxy for migrations. - Secret Manager (
-staging/-prodpairs): database URLs,payload-secret,service-secret,anthropic-api-key,gcs-bucket. - Domains:
ma.screver.com(prod) ·ma-staging.screver.com(staging) ·docs.ma.screver.com. The AI service is internal (run.app) only.
Pipeline shape (per repo)
Each .github/workflows/ci-deploy.yml: lint / typecheck / build → Payload migrations via the Cloud
SQL Proxy → staging auto-deploy → production manual approval gate. Auth via Workload Identity
Federation (no JSON keys).
Learnings baked in (non-negotiable)
These come from prior incidents on sibling projects. They are wired into the build, not left to memory.
- Blank admin panel = the GCS storage plugin missing from the import map. The build runs
GCS_BUCKET=dummy payload generate:importmap. If the admin renders blank, check the Cloud Run server logs first for agetFromImportMaperror — not the browser CSS. - Never
db.push()in CI. It uses interactive prompts that hang forever in a non-TTY Cloud Run Job. Always run pre-generated migrations. - Migrations are additive only. Never drop a column; never change a field type in place. A new
collection also needs its
payload_locked_documents_relsrelation column (the generated migration handles this — never hand-trim it). A missing one 500s all of/admin. - "Deploy is green but new code isn't live" = Cloud Run traffic pinned to an old revision. Deploy
by immutable digest and run
gcloud run services update-traffic --to-latestafter every deploy. - Redirects resolving to
0.0.0.0:8080= readx-forwarded-host/x-forwarded-proto, neverrequest.url, in any redirect route. - CSRF / CORS 403 on admin mutations = the allowlists must include both
ma.screver.comandma-staging.screver.com. - Stop on warnings; verify before reporting success. After a deploy,
curl -sLIthe URL and confirm the new behaviour is live (not cached). Production deploys always ask first.
The Fumadocs Dockerfile ordering
fumadocs-mdx's postinstall detects Next vs Vite from the config files present at install time. The
docs Dockerfile copies the full source before npm ci, so detection sees next.config.mjs.
Reordering to a deps-cache-first pattern breaks the build.