refactor(data): squash the 40 migrations into one InitialCreate

The incremental chain was ~31k lines of generated Designer snapshots for a
schema no shipped database steps through anymore. MigrateAndConfigure now
baselines an existing DB onto the squashed id (pre-EF, or history ending at
AddPrimeActionKind) and throws with a reinstall message for a mid-chain DB.
MigrationBaselineTests covers all three paths with a real Migrate().
This commit is contained in:
mika kuns
2026-08-26 13:55:30 +02:00
parent 69fcceb2ed
commit 39d8241360
85 changed files with 628 additions and 30686 deletions
+39 -7
View File
@@ -73,11 +73,19 @@ public class ClaudeDoDbContext : DbContext
}
}
// The 40 incremental migrations were squashed into a single InitialCreate on 2026-08-26.
// Any database that already carries the full pre-squash schema has to be re-stamped to the
// squashed id, or Migrate() would run CREATE TABLE over live tables.
private const string SquashedMigrationId = "20260826094154_InitialCreate";
private const string LastPreSquashMigrationId = "20260825063230_AddPrimeActionKind";
/// <summary>
/// Applies EF Core migrations and sets WAL mode. Safe for both fresh and existing databases.
/// Existing databases (created by the old schema.sql) have their tables but no
/// __EFMigrationsHistory — this method detects that case and baselines the initial
/// migration so EF skips re-creating tables that already exist.
/// Existing databases are baselined onto <see cref="SquashedMigrationId"/> instead of being
/// re-created: ones with tables but no __EFMigrationsHistory (created by the old schema.sql),
/// and ones whose history still lists the pre-squash chain. A database that stopped *mid*-chain
/// can no longer be upgraded — the incremental migrations are gone — and throws instead of
/// being silently stamped onto a schema it doesn't have.
/// </summary>
public static void MigrateAndConfigure(ClaudeDoDbContext db)
{
@@ -101,8 +109,6 @@ public class ClaudeDoDbContext : DbContext
fkCmd.ExecuteNonQuery();
}
// If the 'lists' table exists but __EFMigrationsHistory does not,
// this is a pre-EF database. Baseline the InitialCreate migration.
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='lists'";
@@ -113,16 +119,42 @@ public class ClaudeDoDbContext : DbContext
if (hasLists && !hasHistory)
{
cmd.CommandText = """
// Pre-EF database: tables, no history at all.
cmd.CommandText = $"""
CREATE TABLE "__EFMigrationsHistory" (
"MigrationId" TEXT NOT NULL CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY,
"ProductVersion" TEXT NOT NULL
);
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20260416064948_InitialCreate', '8.0.11');
VALUES ('{SquashedMigrationId}', '8.0.11');
""";
cmd.ExecuteNonQuery();
}
else if (hasLists && hasHistory)
{
cmd.CommandText = $"""SELECT COUNT(*) FROM "__EFMigrationsHistory" WHERE "MigrationId" = '{SquashedMigrationId}'""";
var alreadySquashed = Convert.ToInt64(cmd.ExecuteScalar()) > 0;
if (!alreadySquashed)
{
cmd.CommandText = """SELECT MAX("MigrationId") FROM "__EFMigrationsHistory" """;
var newest = cmd.ExecuteScalar() as string ?? "";
if (newest != LastPreSquashMigrationId)
throw new InvalidOperationException(
$"This database was last migrated by '{newest}', but the migration history was " +
$"squashed into '{SquashedMigrationId}' and the incremental migrations no longer " +
"exist. Reinstall ClaudeDo against a fresh database (or restore a backup taken " +
$"after '{LastPreSquashMigrationId}').");
cmd.CommandText = $"""
DELETE FROM "__EFMigrationsHistory";
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('{SquashedMigrationId}', '8.0.11');
""";
cmd.ExecuteNonQuery();
}
}
}
}
finally