From 4637c0ccab3293c55b445faf532c8511ac0918a5 Mon Sep 17 00:00:00 2001 From: shin-core <153108882+shin-core@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:39:09 +0900 Subject: [PATCH] fix(db): stop documenting 0090 as a grandfathered migration duplicate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `KNOWN_MIGRATION_DUPLICATES` (src/db/migration-collisions.ts) is already correct — 0090 is not a key, because #8897 renumbered 0090_pull_request_detail_sync_head_sha to 0092, leaving a single 0090_contributor_cap_label file. But check-migrations.ts's header still listed 0090 as a grandfathered duplicate and cross-referenced "0074/0090", so a future reader could re-add the dead entry from git history. Replace the 0090 bullet with a note that it was renumbered (not grandfathered) and correct the 0156 cross-reference to name only real grandfathers (0074). No script or KNOWN_MIGRATION_DUPLICATES behaviour change. Add two guards so the header and the data cannot silently diverge again: every filename in KNOWN_MIGRATION_DUPLICATES must exist in migrations/ (a re-added 0090 fails this), and every on-disk migration number with more than one file must be a key (a new un-grandfathered collision fails this). Closes #9653 --- scripts/check-migrations.ts | 8 +++---- test/unit/migration-collisions.test.ts | 29 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/scripts/check-migrations.ts b/scripts/check-migrations.ts index 525b1dd75a..98716b50ef 100644 --- a/scripts/check-migrations.ts +++ b/scripts/check-migrations.ts @@ -23,12 +23,12 @@ // • 0074 — both 0074_ai_review_cache (#1462) and 0074_orb_self_enrollment_disabled (#1465, a bare ADD COLUMN) // merged + deployed before the collision surfaced; the column already exists in prod, so a rename would // re-run the ALTER and fail. Grandfathered for the same reason as 0015/0017. -// • 0090 — both 0090_contributor_cap_label (#2479) and 0090_pull_request_detail_sync_head_sha (#2527) -// merged with bare ADD COLUMN statements. Preserve both filenames so already-applied databases never -// replay either ALTER under a new migration name. +// • 0090 — NOT grandfathered: 0090_pull_request_detail_sync_head_sha (#2527) was renumbered to +// migrations/0092_* by #8897, so only 0090_contributor_cap_label (#2479) exists at 0090 today — a single +// file, no collision, and correctly absent from KNOWN_MIGRATION_DUPLICATES. Do not re-add it from git history. // • 0156 — both 0156_draft_pr_close_policy and 0156_pull_request_screenshot_table_presence_satisfied // merged independently from the same base and were both applied to production before the collision -// surfaced; bare ADD COLUMN statements, same grandfather reasoning as 0074/0090. +// surfaced; bare ADD COLUMN statements, same grandfather reasoning as 0074. import { readdirSync, readFileSync } from "node:fs"; import { detectMigrationCollisions, extractMigrationNumber, KNOWN_MIGRATION_DUPLICATES, MIGRATION_FILENAME_PATTERN } from "../src/db/migration-collisions"; import { detectColumnCollisions } from "../src/db/migration-column-extraction"; diff --git a/test/unit/migration-collisions.test.ts b/test/unit/migration-collisions.test.ts index d0bb166b74..c1187f97d7 100644 --- a/test/unit/migration-collisions.test.ts +++ b/test/unit/migration-collisions.test.ts @@ -1,3 +1,4 @@ +import { readdirSync } from "node:fs"; import { describe, expect, it } from "vitest"; import { detectMigrationCollisions, extractMigrationNumber, KNOWN_MIGRATION_DUPLICATES, MIGRATION_FILENAME_PATTERN } from "../../src/db/migration-collisions"; @@ -90,4 +91,32 @@ describe("KNOWN_MIGRATION_DUPLICATES (#2550)", () => { // And the renumbered file at its new number is likewise a lone, non-colliding entry. expect(detectMigrationCollisions(["0092_pull_request_detail_sync_head_sha.sql"], KNOWN_MIGRATION_DUPLICATES)).toEqual([]); }); + + describe("stays bidirectionally in step with the real migrations/ directory (#9653)", () => { + const realMigrationFiles = readdirSync("migrations").filter((f) => MIGRATION_FILENAME_PATTERN.test(f)); + + it("every filename in KNOWN_MIGRATION_DUPLICATES exists on disk (no dead grandfather entries)", () => { + // A re-added 0090 entry (or any stale filename) fails here — the file no longer exists, so the list would + // point at a migration that isn't there. + const onDisk = new Set(realMigrationFiles); + const missing: string[] = []; + for (const files of KNOWN_MIGRATION_DUPLICATES.values()) { + for (const file of files) if (!onDisk.has(file)) missing.push(file); + } + expect(missing).toEqual([]); + }); + + it("every migration number with more than one file on disk is a KNOWN_MIGRATION_DUPLICATES key", () => { + // The reverse direction: a genuine on-disk collision that isn't grandfathered would surface here rather + // than only at premerge, keeping the two sources in step in both directions. + const filesByNumber = new Map(); + for (const file of realMigrationFiles) { + const number = extractMigrationNumber(file); + if (number == null) continue; + filesByNumber.set(number, [...(filesByNumber.get(number) ?? []), file]); + } + const collidingNumbers = [...filesByNumber.entries()].filter(([, files]) => files.length > 1).map(([number]) => number); + for (const number of collidingNumbers) expect(KNOWN_MIGRATION_DUPLICATES.has(number)).toBe(true); + }); + }); });