43 lines
1.7 KiB
JavaScript
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`);
|