60 lines
2.3 KiB
TypeScript
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()
|
|
}
|