Schließt die Ops-Lücke „kein versioniertes Schema-Tracking" aus FEATURE_IDEAS.md. * apps/api/src/db/migrations/0000_baseline.sql — Drizzle-generierte Baseline-Migration, 355 Zeilen, 25 Tabellen + 5 Enums (cards- und marketplace-Schema). Eingefrostet auf den Live-Stand 2026-05-12. * apps/api/scripts/bootstrap-drizzle-tracking.ts — neues Script, markiert die Baseline in einer bestehenden DB als „bereits angewandt", ohne SQL erneut auszuführen. Verwendet sha256 wie drizzle-orm/migrator (Hash 312d67ba1aeb…), idempotent. * package.json: drizzle:migrate + drizzle:bootstrap-tracking npm-scripts. * docs/playbooks/DRIZZLE_MIGRATIONS_BOOTSTRAP.md — Hand-Over für Prod (Bootstrap einmalig, dann normaler Workflow: schema → generate → commit → migrate, kein push --force mehr). Lokal verifiziert: 17/104 Tests grün, bootstrap idempotent, drizzle-kit migrate erkennt die Baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.8 KiB
Drizzle-Migrationen — Bootstrap und Workflow
Stand: 2026-05-12.
Cards hat bis 2026-05-12 alle Schema-Änderungen über
drizzle-kit push --force gefahren — Schema-Sync ohne versionierte
Migrationen. Live ist das gefährlich: kein Audit-Trail, keine
sichere Rollback-Story, schwer reviewable.
Ab Commit <dieser> gibt es eine versionierte Migration-Welt mit
einer Baseline (0000_baseline.sql), die das bestehende Schema
festfriert.
Was lokal schon passiert ist
pnpm drizzle:generate→apps/api/src/db/migrations/0000_baseline.sql(25 Tabellen, 5 Enums, alle FKs/Indizes — das gesamte cards- und marketplace-Schema, eingefroren auf den Stand 2026-05-12).apps/api/scripts/bootstrap-drizzle-tracking.ts(neues npm-scriptpnpm drizzle:bootstrap-tracking) — markiert in einer existierenden Live-DB die Baseline als „bereits angewandt", ohne SQL erneut auszuführen.- Lokal verifiziert: Bootstrap ist idempotent,
pnpm drizzle:migrateerkennt die Migration danach als bekannt.
Was du auf der Prod-Box machen musst (einmalig)
ssh mana-server
cd ~/projects/cards
git pull --ff-only origin main
1. Bootstrap auf der Live-DB ausführen
# DATABASE_URL aus der env-Datei lesen, nicht hart codieren.
DB_PW=$(grep CARDS_DB_PASSWORD infrastructure/.env.production | cut -d= -f2-)
docker exec -e DATABASE_URL="postgresql://cards:${DB_PW}@cards-postgres:5432/cards" \
cards-api node /app/apps/api/scripts/bootstrap-drizzle-tracking.ts
…oder wenn das im Container-Image-Setup nicht klappt (Script ist TypeScript, der API-Container kommt mit Bun):
# Alternative: vom Host aus über Container-Network ausführen.
# Mac Mini hat Bun installiert.
cd ~/projects/cards/apps/api
DATABASE_URL="postgresql://cards:${DB_PW}@127.0.0.1:5436/cards" \
bun run scripts/bootstrap-drizzle-tracking.ts
Erwartete Ausgabe:
MARKED 0000_baseline (hash 312d67ba1aeb…)
Tracking-Tabelle hat jetzt 1 Eintrag/Einträge.
Wenn statt MARKED schon SKIP kommt: jemand hat das Script bereits
gelaufen lassen. Idempotent → kein Problem.
2. Verifizieren
docker exec cards-postgres psql -U cards -d cards -c \
"SELECT * FROM drizzle.__drizzle_migrations;"
Erwartete Antwort: eine Zeile mit Hash 312d67ba1aeb… und
created_at = 1778604624860.
3. Smoke-Test
cd ~/projects/cards/apps/api
DATABASE_URL="postgresql://cards:${DB_PW}@127.0.0.1:5436/cards" \
bun x drizzle-kit migrate
Erwartet: [✓] migrations applied successfully! ohne dass das
Schema wirklich angefasst wird (es kommt nur ein NOTICE
„relation '__drizzle_migrations' already exists, skipping").
Künftiger Workflow
Neue Schema-Änderung
- Editiere das Schema in
apps/api/src/db/schema/**.ts. - Generiere die Migration lokal:
cd apps/api pnpm drizzle:generate --name <kurz_beschreibend> - Inspiziere das generierte SQL in
src/db/migrations/0001_<name>.sql(oder höher) — bei destruktiven Änderungen (DROP COLUMN, ALTER TYPE) ggf. von Hand patchen, damit Daten nicht verloren gehen. - Commit Schema-Files + Migration-Files + Journal zusammen:
git add apps/api/src/db/schema apps/api/src/db/migrations git commit -m "db(cards): <was>" - Test lokal:
DATABASE_URL="postgresql://cards:cards@localhost:5435/cards" \ pnpm drizzle:migrate
Deploy
git pushauf Forgejo.- Auf der Box:
git pull --ff-only. - Migration anwenden (kein push --force mehr!):
cd ~/projects/cards/apps/api DATABASE_URL="postgresql://cards:${DB_PW}@127.0.0.1:5436/cards" \ bun x drizzle-kit migrate - Erst danach Container rebuilden + restarten, wenn der neue Code auf das neue Schema angewiesen ist.
Tabu
drizzle-kit push --forceauf Prod nie wieder. Das Script bleibt inpackage.jsonals Dev-Convenience, aber für Prod ist nurdrizzle-kit migrateder Weg.- Migration-Files nicht nachträglich umschreiben, sobald sie in einem Prod-Pull-Stand sind — der Hash würde sich ändern und Drizzle würde sie erneut anwenden wollen.
- Wenn du das Schema lokal mit
pushschon „weiter" als die Migration hast, ist das ein Drift-Bug. Lösung: lokal die DB rückbauen (drop schema cards cascade; drop schema marketplace cascade), dann Migrationen frisch anwenden. Niemals diesen Drift in einen Commit hineinwachsen lassen.
Bei Problemen
pnpm drizzle:migrateschmeißt „relation already exists" und bricht ab? Bootstrap-Script ist nie gelaufen — Schritt 1 oben.- Hash-Mismatch (Migration angeblich „neu" trotz Bootstrap)? Das Migration-File wurde nach dem Bootstrap noch ediert. Entweder Edit rückgängig oder den Tracking-Eintrag von Hand auf den neuen Hash bringen — ausschließlich nach Code-Review.