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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user