98 lines
2.7 KiB
JavaScript
98 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Lightweight migration runner for Supabase.
|
|
* Reads SQL files in supabase/migrations (sorted) and applies any that have not been recorded.
|
|
*
|
|
* Env:
|
|
* - SUPABASE_DB_URL or DATABASE_URL: postgres connection string (service role / connection string)
|
|
*/
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { Pool } from 'pg';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const migrationsDir = path.resolve(__dirname, '..', 'supabase', 'migrations');
|
|
const connectionString = process.env.SUPABASE_DB_URL || process.env.DATABASE_URL;
|
|
|
|
if (!connectionString) {
|
|
console.error('Missing SUPABASE_DB_URL (or DATABASE_URL) environment variable. Aborting migrations.');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!fs.existsSync(migrationsDir)) {
|
|
console.error(`Migrations directory not found: ${migrationsDir}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const pool = new Pool({ connectionString });
|
|
|
|
async function ensureMigrationsTable(client) {
|
|
await client.query(`
|
|
create table if not exists public.schema_migrations (
|
|
id serial primary key,
|
|
filename text not null unique,
|
|
applied_at timestamptz not null default now()
|
|
);
|
|
`);
|
|
}
|
|
|
|
async function getApplied(client) {
|
|
const { rows } = await client.query('select filename from public.schema_migrations');
|
|
return new Set(rows.map((r) => r.filename));
|
|
}
|
|
|
|
function listMigrations() {
|
|
return fs
|
|
.readdirSync(migrationsDir)
|
|
.filter((file) => file.endsWith('.sql'))
|
|
.sort();
|
|
}
|
|
|
|
async function applyMigration(client, filename) {
|
|
const filePath = path.join(migrationsDir, filename);
|
|
const sql = fs.readFileSync(filePath, 'utf8');
|
|
console.log(`Applying migration: ${filename}`);
|
|
await client.query('begin');
|
|
try {
|
|
await client.query(sql);
|
|
await client.query('insert into public.schema_migrations (filename) values ($1)', [filename]);
|
|
await client.query('commit');
|
|
console.log(`✓ Applied ${filename}`);
|
|
} catch (err) {
|
|
await client.query('rollback');
|
|
console.error(`✗ Failed ${filename}:`, err.message);
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
async function run() {
|
|
const client = await pool.connect();
|
|
try {
|
|
await ensureMigrationsTable(client);
|
|
const applied = await getApplied(client);
|
|
const migrations = listMigrations();
|
|
|
|
const pending = migrations.filter((m) => !applied.has(m));
|
|
if (pending.length === 0) {
|
|
console.log('No pending migrations.');
|
|
return;
|
|
}
|
|
|
|
for (const migration of pending) {
|
|
await applyMigration(client, migration);
|
|
}
|
|
console.log('All pending migrations applied.');
|
|
} finally {
|
|
client.release();
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
run().catch((err) => {
|
|
console.error('Migration run failed:', err);
|
|
process.exit(1);
|
|
});
|