/** * Bootstrap-Script für drizzle.__drizzle_migrations. * * Eine Cards-DB, die bisher über `drizzle-kit push` (Schema-Sync ohne * Migrations-Tracking) gepflegt wurde, hat das Drizzle-Tracking-Schema * nicht. Dieses Script holt das nach: * * 1. Erstellt `drizzle.__drizzle_migrations` (idempotent). * 2. Liest `src/db/migrations/meta/_journal.json`. * 3. Markiert jede dort gelistete Migration als „bereits angewandt", * mit dem gleichen Hash, den `drizzle-orm/migrator` selbst * berechnen würde — sha256(file_content) als hex. * * Nutzung: * DATABASE_URL=postgresql://… pnpm bootstrap:drizzle * * Idempotent: ein zweiter Run macht nichts neu, sondern überspringt * Migrations, deren Hash schon eingetragen ist. * * Nach dem Bootstrap wird `drizzle-kit migrate` jede Migration aus * dem Journal als bekannt erkennen und keine SQL erneut ausführen. * Künftige Schema-Änderungen ⇒ `drizzle-kit generate` ⇒ commit ⇒ * `drizzle-kit migrate` führt nur die neuen Migrations aus. */ import { createHash } from 'node:crypto'; import { readFileSync } from 'node:fs'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import postgres from 'postgres'; const url = process.env.DATABASE_URL; if (!url) { console.error('DATABASE_URL not set'); process.exit(1); } const here = dirname(fileURLToPath(import.meta.url)); const migrationsDir = resolve(here, '..', 'src', 'db', 'migrations'); const journalPath = resolve(migrationsDir, 'meta', '_journal.json'); interface JournalEntry { idx: number; version: string; when: number; tag: string; breakpoints: boolean; } interface Journal { version: string; dialect: string; entries: JournalEntry[]; } const journal: Journal = JSON.parse(readFileSync(journalPath, 'utf-8')); const sql = postgres(url, { max: 1 }); try { await sql`CREATE SCHEMA IF NOT EXISTS drizzle`; await sql`CREATE TABLE IF NOT EXISTS drizzle.__drizzle_migrations ( id SERIAL PRIMARY KEY, hash text NOT NULL, created_at bigint )`; const existing = await sql<{ hash: string }[]>`SELECT hash FROM drizzle.__drizzle_migrations`; const existingHashes = new Set(existing.map((r) => r.hash)); for (const entry of journal.entries) { const migrationPath = resolve(migrationsDir, `${entry.tag}.sql`); const fileContent = readFileSync(migrationPath, 'utf-8'); const hash = createHash('sha256').update(fileContent).digest('hex'); if (existingHashes.has(hash)) { console.log(`SKIP ${entry.tag} (hash ${hash.slice(0, 12)}… already tracked)`); continue; } await sql`INSERT INTO drizzle.__drizzle_migrations ("hash", "created_at") VALUES (${hash}, ${entry.when})`; console.log(`MARKED ${entry.tag} (hash ${hash.slice(0, 12)}…)`); } const final = await sql<{ count: number }[]>`SELECT COUNT(*)::int AS count FROM drizzle.__drizzle_migrations`; console.log(`\nTracking-Tabelle hat jetzt ${final[0]!.count} Eintrag/Einträge.`); } finally { await sql.end({ timeout: 5 }); }