Skip to content

Commit 6c78796

Browse files
committed
Verify migration checksums on startup to detect post-rebase modifications
When a migration file has already been applied but its SHA-256 checksum differs from the stored value, the runner now aborts with a clear error message instructing the developer to drop the branch database and restart. Signed-off-by: Mike Lischke <mike@lischke-online.de>
1 parent 847eacf commit 6c78796

1 file changed

Lines changed: 25 additions & 3 deletions

File tree

build/migration.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const seedPath = resolve(process.cwd(), "build", "seed.sql");
2626

2727
export interface IMigrationRow {
2828
filename: string;
29+
checksum: string;
2930
}
3031

3132
/**
@@ -277,10 +278,12 @@ const applyMigrations = async (pool: IMigrationPool, engine: string): Promise<vo
277278
)`,
278279
);
279280

280-
const appliedResult = await pool.query("SELECT filename FROM migration_history ORDER BY filename");
281+
const appliedResult = await pool.query(
282+
"SELECT filename, checksum FROM migration_history ORDER BY filename",
283+
);
281284
const appliedRows = extractRows<IMigrationRow>(appliedResult);
282-
const applied = new Set(appliedRows.map((r) => {
283-
return r.filename;
285+
const applied = new Map(appliedRows.map((r) => {
286+
return [r.filename, r.checksum];
284287
}));
285288

286289
// Collect migration files in timestamp order.
@@ -302,6 +305,25 @@ const applyMigrations = async (pool: IMigrationPool, engine: string): Promise<vo
302305
return;
303306
}
304307

308+
// Verify checksums of already-applied migrations. A mismatch means the file was
309+
// changed after being applied — likely from a rebase. The branch DB must be dropped.
310+
for (const file of files) {
311+
const storedChecksum = applied.get(file);
312+
313+
if (storedChecksum !== undefined) {
314+
const filePath = join(migrationsDir, file);
315+
const rawSql = readFileSync(filePath, "utf-8");
316+
const currentChecksum = checksum(rawSql);
317+
318+
if (currentChecksum !== storedChecksum) {
319+
throw new Error(
320+
`Migration "${file}" was modified after it was applied.\n`
321+
+ `Drop the branch database and restart the server to re-apply all migrations.`,
322+
);
323+
}
324+
}
325+
}
326+
305327
const pending = files.filter((f) => {
306328
return !applied.has(f);
307329
});

0 commit comments

Comments
 (0)