Skip to content

M14: migrate run/revert/status engine (Phase 5)#14

Merged
lgamorim merged 7 commits into
masterfrom
feature/M14-migrate-run
Jul 12, 2026
Merged

M14: migrate run/revert/status engine (Phase 5)#14
lgamorim merged 7 commits into
masterfrom
feature/M14-migrate-run

Conversation

@lgamorim

Copy link
Copy Markdown
Owner

Second of three in Phase 5. Turns the M13 migration model into a working engine: applies, reverts, and reports migrations, and adds the ledger write side M13 deferred. SQL Server pilot (other providers in M15).

What's here

  • Ledger write sideIMigrationLedger gains RecordAppliedAsync/RemoveAsync, both taking the ambient DbTransaction so a ledger row commits atomically with the migration's own script. SQL Server implements them; the read methods stay transaction-free.
  • Pure planner (SqlBound.Migrations, unit-tested) — the run/revert/status decision logic factored out of I/O:
    • MigrationPlan.Create → ordered pending set, throwing MigrationInconsistencyException on checksum drift (an applied migration was edited) or an out-of-order pending migration.
    • MigrationStatusReport.Build → classifies each migration Applied / Pending / Drifted / Missing.
    • MigrationReverter.Plan → picks the most recently applied migration, refusing when it is irreversible or its files are gone.
  • MigrationRunner — provider-neutral executor: applies each pending migration in its own transaction with its ledger row (a failed script rolls back that one migration and stops, raising MigrationExecutionException); reverts by running the down-script + ledger removal in one transaction; and a read-only status pass.
  • CLImigrate run, migrate revert, migrate status subcommands via a shared MigrationCli helper (resolve target, load directory, open connection, map expected failures to exit 1).
  • Docsdocs/migrations.md and README updated for the three commands, the safety rules, and the documented no-GO-splitting limitation.

Decisions (as agreed on the plan)

  • No GO batch splitting — each script runs as one command; multi-batch scripts must be split. Keeps the engine provider-neutral; documented, revisitable.
  • Single-step revert — one migration per invocation.
  • Transaction per migration; drift and out-of-order pending are hard errors; status reports drift/missing rather than failing.

Verification

  • Release build 0 warnings, dotnet format clean.
  • 485 tests pass, 0 skipped (Docker up; all provider containers ran).
  • All 9 packages pack at 0.5.0-preview.2.

Note: the full-suite run caught a concurrency bug the per-class runs masked — the four SQL Server migration test classes mutated the shared _sqlbound_migrations table in parallel. Fixed by serializing them into one xunit collection.

Deferred to M15: other-provider ledgers and database lifecycle; the transactional-DDL reconciliation (notably MySQL's implicit DDL commits).

🤖 Generated with Claude Code

lgamorim and others added 7 commits July 12, 2026 12:45
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend IMigrationLedger with RecordAppliedAsync and RemoveAsync, both
taking the ambient DbTransaction so a ledger row commits atomically with
the migration's own script. SqlServerMigrationLedger implements them
against dbo._sqlbound_migrations. The read methods stay transaction-free
since they run outside the per-migration transaction migrate run opens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Factor the run/revert/status decision logic into pure, unit-tested
functions over the loaded migrations and the applied ledger rows:

- MigrationPlan.Create computes the ordered pending set and enforces the
  two safety rules — checksum drift on an applied migration and a pending
  migration ordered before an applied one both throw.
- MigrationStatusReport.Build classifies each migration as Applied,
  Pending, Drifted, or Missing.
- MigrationReverter.Plan selects the most recently applied migration to
  roll back, refusing when it is irreversible or its files are gone.

MigrationInconsistencyException marks a directory that disagrees with the
applied history, distinct from a malformed directory (MigrationFormatException).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MigrationRunner.RunAsync ensures the ledger, plans against the applied
history, and applies each pending migration in its own transaction
together with its ledger row — a failing script rolls back that one
migration (raising MigrationExecutionException) and leaves every earlier
one applied. The provider-neutral engine executes scripts as ordinary
DbCommands so every provider will share it.

The CLI 'migrate run' subcommand wires it for SQL Server through a shared
MigrationCli helper that resolves the target, loads the directory, opens
the connection, and maps expected failures to exit code 1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MigrationRunner.RevertAsync rolls back the most recently applied
migration: it runs the down-script and removes the ledger row in one
transaction, refusing (via MigrationReverter) when that migration is
irreversible or its files are gone, and no-opping when nothing is
applied. The CLI 'migrate revert' subcommand wires it for SQL Server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MigrationRunner.StatusAsync ensures the ledger, reads the applied rows,
and returns MigrationStatusReport.Build's classification. The CLI
'migrate status' subcommand prints each migration's version, name, state
(applied/pending/drifted/missing), and applied-on timestamp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update docs/migrations.md and the README for migrate run/revert/status,
including the safety rules (checksum drift, out-of-order) and the
documented no-GO-splitting limitation.

Place the four SQL Server migration integration test classes in one
xunit collection so they no longer run in parallel against the shared
_sqlbound_migrations table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@lgamorim lgamorim merged commit b1ae6ff into master Jul 12, 2026
4 checks passed
@lgamorim lgamorim deleted the feature/M14-migrate-run branch July 12, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant