Deploy & CI/CD
Web app (ka26.shop)
CI/CD pipeline (.github/workflows/deploy.yml)
On every push to main:
- Run Tests —
npm test(1874 tests, ~1s) - Run Mobile Tests — vitest + Jest in
mobile/ - Build Image —
docker build→ push to Artifact Registry (us-central1-docker.pkg.dev/school-mgmt-saas/ka26) - Deploy to Staging —
gcloud run deploy ka26-staging - Smoke Tests (Staging) —
tests/e2e-smoke.test.tsagainst staging URL - Deploy to Production —
gcloud run deploy ka26-marketplace - Verify production is healthy — curl
/api/health(10s sleep + retry; if Cold start > sleep window, may falsely fail — re-run viagh run rerun)
Failed at any step → no production deploy.
Manual deploy (don't, unless CI is broken)
gcloud builds submit --tag us-central1-docker.pkg.dev/school-mgmt-saas/ka26/app:manual-$(date +%s)
gcloud run deploy ka26-marketplace --image=... --region=us-central1
Cloud Run service
- Project:
school-mgmt-saas - Region:
us-central1(only — never multi-region for cost) - Service:
ka26-marketplace - Service URL:
https://ka26-marketplace-4374945524.us-central1.run.app - Custom domain:
ka26.shopvia Global Load Balancer - Min instances: 0, max: 10 (default)
Artifact Registry
- Repo:
ka26inus-central1 - Auto-cleanup: keep latest 5, delete >7 days old (configured 2026-04-17 to control storage cost)
Landing page (ka-26.com)
Hosted on GitHub Pages (free) — repo sidgk/ka26-website. Deploy is .github/workflows/deploy.yml in that repo, builds Next.js as static export, publishes to Pages. Custom domain CNAME at static/CNAME.
Docs portal (docs.ka-26.com)
This very portal. Hosted on GitHub Pages — repo sidgk/ka26-docs. See GitHub Setup below.
Database
Migrations (Prisma)
Schema lives in prisma/schema.prisma. Generate migration SQL via prisma migrate dev (local) — but for production, write the migration SQL by hand and apply via psql:
PGPASSWORD=Ka26Mkt2026 psql -h 34.123.40.64 -U ka26user -d ka26 -f prisma/migrations/<timestamp>_<name>/migration.sql
Migrations under prisma/migrations/ are idempotent (IF NOT EXISTS, ADD COLUMN IF NOT EXISTS) so re-runs are safe.
Backup strategy
Cloud SQL automated backups should be ON. Verify periodically (see Backups).
Cloud Run env var management
Update a single env var
gcloud run services update ka26-marketplace --region us-central1 \
--update-env-vars NEW_VAR=value
Update multiple
gcloud run services update ka26-marketplace --region us-central1 \
--update-env-vars VAR1=v1,VAR2=v2
Mount a Secret Manager secret
echo -n "secret-value" | gcloud secrets create my-secret --data-file=-
gcloud run services update ka26-marketplace --region us-central1 \
--update-secrets MY_SECRET=my-secret:latest
Cloud Run picks up :latest on every cold-start automatically. To force immediate rotation, deploy a new revision (any change triggers it).
Rollback
# List revisions
gcloud run revisions list --service ka26-marketplace --region us-central1
# Roll all traffic to a known-good revision
gcloud run services update-traffic ka26-marketplace --region us-central1 \
--to-revisions ka26-marketplace-00XXX-yyy=100
Takes ~30s. Note: this doesn't roll back DB migrations — those need a separate forward migration.
GitHub setup
CI runs without seeding
The pipeline doesn't have DB access. Tests are file-shape + smoke tests only — no DB queries during CI.
Required GitHub secrets
WIF_PROVIDER— workload identity provider forka26-deployer@school-mgmt-saas.iam.gserviceaccount.comSMTP_USER,SMTP_PASS— for the hourly health check workflow's email alertALERT_TO— recipient email for health-cron failuresSENTRY_AUTH_TOKEN— optional, for source-map uploadSENTRY_ORG,SENTRY_PROJECT— optional
What can break the pipeline
| Symptom | Cause | Fix |
|---|---|---|
Build Image: ❌ Type error: Cannot find module 'react-native' | Mobile-only file got picked up by Next.js typecheck | Add to tsconfig.json exclude (already done for _archived-mobile, src/_archived) |
Deploy to Staging: ❌ Staging health check failed with status 503 | Cloud Run cold-start race; health check ran before container booted | Re-run the workflow (gh run rerun <id> --failed) |
Test failed: 'foo' | Code regression | Read the test, fix the code, push again |
npm ci fails on node-gyp | Native module compile issue | Usually transient; re-run |
| Sentry build hook errors | SDK version mismatch | Check next.config.ts — Sentry v10 renamed hideSourceMaps → sourcemaps.deleteSourcemapsAfterUpload |