diff --git a/internal-packages/dashboard-agent-db/migrate-status.mjs b/internal-packages/dashboard-agent-db/migrate-status.mjs new file mode 100644 index 00000000000..8d8208fdb2e --- /dev/null +++ b/internal-packages/dashboard-agent-db/migrate-status.mjs @@ -0,0 +1,76 @@ +// Pending-status check for the `trigger_dashboard_agent` schema (sibling of +// migrate.mjs). Exit 0 = up to date, 1 = pending, 2 = error. +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import postgres from "postgres"; + +const MIGRATIONS_SCHEMA = "drizzle"; +const MIGRATIONS_TABLE = "__dashboard_agent_migrations"; + +const connectionString = process.env.DASHBOARD_AGENT_DATABASE_URL ?? process.env.DATABASE_URL; + +if (!connectionString) { + console.error( + "[dashboard-agent-db] DASHBOARD_AGENT_DATABASE_URL / DATABASE_URL not set; cannot check status." + ); + process.exit(2); +} + +// Match migrate.mjs: drop the Prisma-style `?schema=` param postgres.js forwards. +function normalizeConnectionString(value) { + try { + const url = new URL(value); + url.searchParams.delete("schema"); + return url.toString(); + } catch { + return value; + } +} + +const journalPath = join( + dirname(fileURLToPath(import.meta.url)), + "drizzle/meta/_journal.json" +); +const sql = postgres(normalizeConnectionString(connectionString), { + max: 1, + prepare: false, + onnotice: () => {}, +}); + +async function main() { + const journal = JSON.parse(readFileSync(journalPath, "utf-8")); + const entries = [...journal.entries].sort((a, b) => a.when - b.when); + + let lastAppliedAt = -1; + try { + const rows = await sql`SELECT MAX(created_at)::bigint AS last FROM ${sql( + MIGRATIONS_SCHEMA + )}.${sql(MIGRATIONS_TABLE)}`; + lastAppliedAt = rows[0].last === null ? -1 : Number(rows[0].last); + } catch (err) { + // 42P01: journal table absent (fresh database), so nothing is applied. + if (err.code !== "42P01") throw err; + } + + const pending = entries.filter((e) => e.when > lastAppliedAt); + console.log( + `${entries.length} migration(s) found, ${entries.length - pending.length} applied` + ); + + if (pending.length > 0) { + console.log(`${pending.length} pending migration(s):`); + for (const e of pending) console.log(` - ${e.tag}`); + return 1; + } + + console.log("Dashboard agent schema is up to date"); + return 0; +} + +main() + .then((code) => sql.end({ timeout: 5 }).then(() => process.exit(code))) + .catch((err) => { + console.error(err); + sql.end({ timeout: 5 }).finally(() => process.exit(2)); + }); diff --git a/internal-packages/dashboard-agent-db/package.json b/internal-packages/dashboard-agent-db/package.json index 929f6721be6..b91535c0e97 100644 --- a/internal-packages/dashboard-agent-db/package.json +++ b/internal-packages/dashboard-agent-db/package.json @@ -17,6 +17,7 @@ "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:migrate:deploy": "node migrate.mjs", + "db:migrate:status": "node migrate-status.mjs", "db:studio": "drizzle-kit studio" } }