Applied the two-review consolidation: merged the duplicate 2026-06-06/06-09 'Conventions (enforced)' sections into one canonical block (query-invalidation rule stated once instead of three times, Zod-helper 'tracked follow-up' vs 'MANDATORY' contradiction resolved, seed-is-dev-only stated once); removed stale-by-design facts (version pins, test counts, file counts, React 18 typo) and migration-era storytelling; fixed prisma migrate diff flags (--from-url removed in Prisma 7) and documented the non-interactive migration recipe; added the new rules earned this week (@db.Date UTC-truncation, document business rules incl. VAT-only-on-invoices and PO suppliers, document-platform conventions, shared-module reuse list, dashboard invalidation, app_test migrate step). Release/hotfix/drift/baseline runbooks moved to docs/release.md with a pointer (keeps prod details out of every-turn context). Also dropped the db:push npm script - it contradicted the golden rule. CLAUDE.md: 570 -> ~390 lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.7 KiB
Release & deployment runbook — boha-app-ts
Referenced from CLAUDE.md. Never deploy to production or restart services without explicit user confirmation.
Standard release
-
Run the full gates locally:
npx vitest run(all green),npx tsc -b --noEmit,npm run lint(0 errors). -
Bump version:
npm version X.Y.Z --no-git-tag-version(the user picks the number). -
npm run build -
Commit and tag:
git tag -a vX.Y.Z -
Push to Gitea:
git push origin master && git push origin vX.Y.Z -
Create tarball:
tar -czf app-ts-X.Y.Z.tar.gz dist dist-client prisma prisma.config.ts package.json package-lock.json scripts⚠️
prisma.config.tsis REQUIRED — Prisma 7 keeps the datasource URL there; without it,prisma generate/migrate deployon prod have no datasource. -
Deploy via SSH to production (
boha_admin@192.168.50.100, path/var/www/app-ts):scp app-ts-X.Y.Z.tar.gz boha_admin@192.168.50.100:/tmp/ ssh boha_admin@192.168.50.100 cd /var/www/app-ts rm -rf dist dist-client prisma prisma.config.ts scripts package.json package-lock.json tar -xzf /tmp/app-ts-X.Y.Z.tar.gz npm install --omit=dev npx prisma generate # MANDATORY — see below npx prisma migrate deploy pm2 restart app-ts --update-envnpx prisma generateis mandatory.npm installskips client regeneration when dependencies didn't change, leaving a stale client that still selects dropped/renamed columns → P2022 500s in prod (this caused the v2.4.0 incident: every issued-orders query failed until generate ran). -
Verify:
pm2 list(status online, correct version, restart counter not climbing),npx prisma migrate status("up to date"),curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3001/→ 200, and grep logs for errors from the CURRENT pid only (the out log keeps old errors from prior pids). -
Clean up tarballs locally and in
/tmp/on the server.
Risky releases (framework jumps, FK/constraint-altering migrations) get a
read-only pre-flight first: prisma migrate status on prod, verify FK
constraint names the migration DROPs, check no data violates new
UNIQUE/RESTRICT constraints — and confirm with the user before the
irreversible steps. The prod DB is backed up by a cron job (no manual
mysqldump needed).
Hotfixing a migration added after the tarball was built
A migration folder created after the release tarball shipped is not on prod,
so prisma migrate deploy reports "No pending migrations". Ship just the
migration folder (still tracked in git — this is NOT raw SQL on prod):
# Local
cd prisma/migrations
tar -czf /tmp/<name>_migration.tar.gz <timestamp>_<name>/
scp /tmp/<name>_migration.tar.gz boha_admin@192.168.50.100:/tmp/
# On prod
ssh boha_admin@192.168.50.100
cd /var/www/app-ts/prisma/migrations
tar -xzf /tmp/<name>_migration.tar.gz
cd /var/www/app-ts
npx prisma migrate deploy
pm2 restart app-ts --update-env
Drift check / preview before deploy
# Preview what SQL would bring the dev DB (per prisma.config.ts) to the local schema
npx prisma migrate diff --from-config-datasource --to-schema prisma/schema.prisma --script
Empty output = no diff. (Prisma 7 removed --from-url; diffs against another
DB need its URL in a config/env override.) If drift includes DROPs,
investigate before touching production.
Baselining a database that has no migrations
If a database was synced without migration history (no _prisma_migrations
table): create the initial migration locally, copy prisma/migrations to the
server, then mark it applied without running SQL:
npx prisma migrate resolve --applied <migration_name>