manicanldes-backend/scripts/check-migrations.mjs

43 lines
1.7 KiB
JavaScript

import { createHash } from 'node:crypto';
import { readFile, readdir, writeFile } from 'node:fs/promises';
import { resolve, join } from 'node:path';
const root = resolve(
process.argv.find((value) => value.startsWith('--root='))?.slice(7) ?? '.',
);
const folder = join(root, 'prisma', 'migrations');
const manifest = join(root, 'prisma', 'migration-checksums.json');
const hashes = JSON.parse(await readFile(manifest, 'utf8'));
const canonicalHash = (sql) =>
createHash('sha256').update(sql.replaceAll('\r\n', '\n')).digest('hex');
for (const [name, expected] of Object.entries(hashes)) {
const actual = canonicalHash(
await readFile(join(folder, name, 'migration.sql'), 'utf8'),
);
if (actual !== expected)
throw new Error(`Immutable migration changed: ${name}`);
}
const names = (await readdir(folder, { withFileTypes: true }))
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort();
const stamps = new Set();
for (const name of names) {
const stamp = name.split('_')[0];
if (stamps.has(stamp))
throw new Error(`Duplicate migration timestamp: ${stamp}`);
stamps.add(stamp);
if (hashes[name]) continue;
if (!/^\d{14}_[a-z0-9_]+$/.test(name))
throw new Error(`New migration requires YYYYMMDDHHmmss timestamp: ${name}`);
if (!process.argv.includes('--record-new'))
throw new Error(`New migration checksum not recorded: ${name}`);
hashes[name] = canonicalHash(
await readFile(join(folder, name, 'migration.sql'), 'utf8'),
);
}
if (process.argv.includes('--record-new'))
await writeFile(manifest, JSON.stringify(hashes, null, 2) + '\n');
console.log(`Verified ${Object.keys(hashes).length} immutable migrations`);