/* oxlint-disable executor/no-error-constructor, executor/no-try-catch-or-throw -- boundary: migration CLI runner uses thrown failures for process exit */ export interface CodeMigrationSql { unsafe[]>( query: string, params?: readonly unknown[], ): Promise; } export interface CodeMigrationContext { readonly sql: CodeMigrationSql; readonly dryRun: boolean; readonly log: (message: string) => void; } export interface CodeMigrationResult { readonly summary: string; } export interface CodeMigration { readonly name: string; readonly run: (context: CodeMigrationContext) => Promise; } export interface RunCodeMigrationsOptions { readonly dryRun?: boolean; readonly log?: (message: string) => void; } const LEDGER_TABLE = "cloud_code_migration"; const ADVISORY_LOCK_KEY = "executor_cloud_code_migration"; const createLedgerSql = ` CREATE TABLE IF NOT EXISTS "${LEDGER_TABLE}" ( "name" text PRIMARY KEY, "time_completed" bigint NOT NULL ) `; const readCompletedMigrations = async ( sql: CodeMigrationSql, dryRun: boolean, ): Promise> => { if (dryRun) { const [table] = await sql.unsafe<{ ledger_table: string | null }[]>( `SELECT to_regclass('public.${LEDGER_TABLE}')::text AS "ledger_table"`, ); if (!table?.ledger_table) return new Set(); } else { await sql.unsafe(createLedgerSql); } const stamped = await sql.unsafe<{ name: string }[]>( `SELECT "name" FROM "${LEDGER_TABLE}" ORDER BY "name"`, ); return new Set(stamped.map((row) => row.name)); }; const assertUniqueNames = (migrations: readonly CodeMigration[]): void => { const names = new Set(); for (const migration of migrations) { if (names.has(migration.name)) { throw new Error(`Duplicate code migration name: ${migration.name}`); } names.add(migration.name); } }; const withMigrationLock = async ( sql: CodeMigrationSql, dryRun: boolean, body: () => Promise, ): Promise => { if (dryRun) return body(); await sql.unsafe(`SELECT pg_advisory_lock(hashtext('${ADVISORY_LOCK_KEY}'))`); try { return await body(); } finally { await sql.unsafe(`SELECT pg_advisory_unlock(hashtext('${ADVISORY_LOCK_KEY}'))`); } }; export const runCodeMigrations = async ( sql: CodeMigrationSql, migrations: readonly CodeMigration[], options: RunCodeMigrationsOptions = {}, ): Promise => { const dryRun = options.dryRun ?? false; const log = options.log ?? console.log; assertUniqueNames(migrations); return withMigrationLock(sql, dryRun, async () => { const completed = await readCompletedMigrations(sql, dryRun); const applied: string[] = []; for (const migration of migrations) { if (completed.has(migration.name)) { log(`[code-migrate] skip ${migration.name}`); continue; } log(`[code-migrate] ${dryRun ? "plan" : "run"} ${migration.name}`); const result = await migration.run({ sql, dryRun, log }); log(`[code-migrate] ${result.summary}`); if (!dryRun) { await sql.unsafe( `INSERT INTO "${LEDGER_TABLE}" ("name", "time_completed") VALUES ($1, $2)`, [migration.name, Date.now()], ); } applied.push(migration.name); } return applied; }); };