cards/docs/playbooks/DRIZZLE_MIGRATIONS_BOOTSTRAP.md
Till JS 4bb1390180
Some checks are pending
CI / validate (push) Waiting to run
db(cards): baseline migration + drizzle-tracking bootstrap script
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>
2026-05-12 18:53:52 +02:00

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

  1. pnpm drizzle:generateapps/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).
  2. apps/api/scripts/bootstrap-drizzle-tracking.ts (neues npm-script pnpm drizzle:bootstrap-tracking) — markiert in einer existierenden Live-DB die Baseline als „bereits angewandt", ohne SQL erneut auszuführen.
  3. Lokal verifiziert: Bootstrap ist idempotent, pnpm drizzle:migrate erkennt 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

  1. Editiere das Schema in apps/api/src/db/schema/**.ts.
  2. Generiere die Migration lokal:
    cd apps/api
    pnpm drizzle:generate --name <kurz_beschreibend>
    
  3. 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.
  4. 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>"
    
  5. Test lokal:
    DATABASE_URL="postgresql://cards:cards@localhost:5435/cards" \
      pnpm drizzle:migrate
    

Deploy

  1. git push auf Forgejo.
  2. Auf der Box: git pull --ff-only.
  3. 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
    
  4. Erst danach Container rebuilden + restarten, wenn der neue Code auf das neue Schema angewiesen ist.

Tabu

  • drizzle-kit push --force auf Prod nie wieder. Das Script bleibt in package.json als Dev-Convenience, aber für Prod ist nur drizzle-kit migrate der 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 push schon „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:migrate schmeiß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.