#!/usr/bin/env node // Migration CLI. // // npm run migrate apply pending migrations // npm run migrate:status show what is applied and what is pending // npm run migrate:baseline record pending migrations as applied WITHOUT running // them — for a database that already matches // // Pass --up-to= to baseline only through a given migration. require('dotenv').config(); const os = require('os'); const { Client } = require('pg'); const { status, migrate, baseline } = require('../lib/migrations'); const args = process.argv.slice(2); const command = args.find(a => !a.startsWith('--')) || 'up'; const upTo = (args.find(a => a.startsWith('--up-to=')) || '').split('=')[1] || null; function connect() { return new Client({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT) || 5432, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, ssl: false }); } async function showStatus(client) { const st = await status(client); console.log(`\n${process.env.DB_NAME} (${st.applied.length} applied, ${st.pending.length} pending)\n`); for (const m of st.onDisk) { const hit = st.applied.find(a => a.filename === m.filename); const mark = !hit ? 'PENDING' : st.modified.includes(m.filename) ? 'MODIFIED' : 'applied'; const when = hit ? hit.applied_at.toISOString().slice(0, 19).replace('T', ' ') : ''; console.log(` ${mark.padEnd(9)} ${m.filename.padEnd(40)} ${when}`); } for (const f of st.missing) console.log(` MISSING ${f.padEnd(40)} recorded as applied but not on disk`); if (st.modified.length) console.log(`\n ! modified after being applied: ${st.modified.join(', ')}`); if (st.outOfOrder.length) console.log(` ! would apply out of order: ${st.outOfOrder.join(', ')}`); console.log(''); return st; } (async () => { const client = connect(); await client.connect(); try { const user = `${os.userInfo().username}@${os.hostname()}`; if (command === 'status') { const st = await showStatus(client); process.exitCode = (st.modified.length || st.missing.length) ? 1 : 0; } else if (command === 'baseline') { await baseline(client, { user, upTo }); await showStatus(client); } else if (command === 'up') { await migrate(client, { user }); } else { console.error(`Unknown command "${command}" — expected up, status, or baseline.`); process.exitCode = 2; } } catch (err) { console.error(`\nmigration error: ${err.message}\n`); process.exitCode = 1; } finally { await client.end(); } })();