fix: rewrite migrate.mjs to bypass missing drizzle-orm migrator module
Some checks failed
Deploy to VPS / deploy (push) Has been cancelled
Some checks failed
Deploy to VPS / deploy (push) Has been cancelled
drizzle-orm v0.45.2 declares a migrator export but does not ship the actual .js file, causing all migrations to silently fail on every deploy. This was the root cause of the missing "documents" table (AIIA-62). The new script reads the drizzle journal and executes SQL files directly via pg, with its own tracking table for idempotent re-runs. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
72
migrate.mjs
72
migrate.mjs
@@ -1,17 +1,73 @@
|
||||
// Runtime migration script — runs drizzle SQL migrations against the database.
|
||||
// Used by the Docker entrypoint before starting the app.
|
||||
// Runtime migration script — reads the drizzle journal and executes pending
|
||||
// SQL migrations directly via pg, bypassing the drizzle-orm migrator module
|
||||
// which may not be shipped in all drizzle-orm builds.
|
||||
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
||||
import pg from 'pg';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
const MIGRATIONS_DIR = './drizzle';
|
||||
const MIGRATION_TABLE = '__drizzle_migrations';
|
||||
|
||||
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
|
||||
|
||||
try {
|
||||
const db = drizzle(pool);
|
||||
console.log('Running database migrations...');
|
||||
await migrate(db, { migrationsFolder: './drizzle' });
|
||||
console.log('Migrations complete.');
|
||||
const client = await pool.connect();
|
||||
|
||||
// Ensure migration tracking table exists
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS ${MIGRATION_TABLE} (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tag TEXT NOT NULL UNIQUE,
|
||||
created_at BIGINT NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// Load journal
|
||||
const journal = JSON.parse(readFileSync(`${MIGRATIONS_DIR}/meta/_journal.json`, 'utf8'));
|
||||
|
||||
// Find already-applied migrations
|
||||
const { rows: applied } = await client.query(`SELECT tag FROM ${MIGRATION_TABLE}`);
|
||||
const appliedTags = new Set(applied.map(r => r.tag));
|
||||
|
||||
let count = 0;
|
||||
for (const entry of journal.entries) {
|
||||
if (appliedTags.has(entry.tag)) continue;
|
||||
|
||||
const sqlFile = `${MIGRATIONS_DIR}/${entry.tag}.sql`;
|
||||
let sql;
|
||||
try {
|
||||
sql = readFileSync(sqlFile, 'utf8');
|
||||
} catch {
|
||||
console.warn(`Migration file not found: ${sqlFile}, skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Split on drizzle statement breakpoints and execute each statement
|
||||
const statements = sql.split('--> statement-breakpoint')
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
console.log(`Applying migration: ${entry.tag} (${statements.length} statements)`);
|
||||
|
||||
await client.query('BEGIN');
|
||||
try {
|
||||
for (const stmt of statements) {
|
||||
await client.query(stmt);
|
||||
}
|
||||
await client.query(
|
||||
`INSERT INTO ${MIGRATION_TABLE} (tag, created_at) VALUES ($1, $2)`,
|
||||
[entry.tag, entry.when],
|
||||
);
|
||||
await client.query('COMMIT');
|
||||
count++;
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK');
|
||||
throw new Error(`Migration ${entry.tag} failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
client.release();
|
||||
console.log(count > 0 ? `Applied ${count} migration(s).` : 'No pending migrations.');
|
||||
} catch (err) {
|
||||
console.error('Migration failed:', err);
|
||||
process.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user