Files

60 lines
2.3 KiB
TypeScript

import { readdir, readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import { createHash } from 'node:crypto'
import { createDatabase } from '@trumpsignal/database'
const url = process.env.DATABASE_URL
if (!url) throw new Error('DATABASE_URL required')
const { pool, lockPool } = createDatabase(url),
client = await pool.connect()
try {
await client.query('SELECT pg_advisory_lock(hashtextextended($1,0))', ['trumpsignal-migration'])
await client.query(
'CREATE TABLE IF NOT EXISTS typescript_migrations (name TEXT PRIMARY KEY, sha256 TEXT NOT NULL, applied_at TIMESTAMPTZ NOT NULL DEFAULT now())',
)
const folder = fileURLToPath(new URL('../packages/database/migrations/', import.meta.url)),
files = (await readdir(folder)).filter((n) => n.endsWith('.sql')).sort()
for (const name of files) {
const source = await readFile(folder + name, 'utf8'),
hash = createHash('sha256').update(source).digest('hex'),
old = await client.query<{ sha256: string }>(
'SELECT sha256 FROM typescript_migrations WHERE name=$1',
[name],
)
if (old.rows[0]) {
if (old.rows[0].sha256 !== hash)
throw new Error('Previously applied migration changed: ' + name)
continue
}
if (name.startsWith('0001')) {
const legacy = await client.query<{ exists: boolean }>(
"SELECT to_regclass('public.alembic_version') IS NOT NULL AS exists",
)
if (legacy.rows[0].exists) {
const head = await client.query<{ version_num: string }>(
'SELECT version_num FROM alembic_version',
)
if (!head.rows.some((r) => r.version_num.startsWith('027')))
throw new Error('Existing legacy database must reach Alembic head 027 first')
}
}
await client.query('BEGIN')
try {
await client.query(source)
await client.query('INSERT INTO typescript_migrations(name,sha256) VALUES ($1,$2)', [
name,
hash,
])
await client.query('COMMIT')
process.stdout.write('Applied ' + name + '\n')
} catch (e) {
await client.query('ROLLBACK')
throw e
}
}
} finally {
await client.query('SELECT pg_advisory_unlock(hashtextextended($1,0))', ['trumpsignal-migration'])
client.release()
await pool.end()
await lockPool.end()
}