dorm.migrations.lint¶
Online-safe migration linter — walks every migration's
operations list and emits findings for patterns that are
dangerous on a live (high-traffic) database.
CLI¶
dorm lint-migrations # exit non-zero on findings
dorm lint-migrations --format json # CI-consumable
dorm lint-migrations --rule DORM-M001 # filter (may repeat)
dorm lint-migrations --exit-zero # advisory mode
See the Migration safety section in the production guide for the full rule table + suppression syntax.
Programmatic API¶
from pathlib import Path
from dorm.migrations.lint import lint_directory
result = lint_directory(Path("myapp/migrations"))
if not result.ok:
for f in result.findings:
print(f.code, f.file, f.message)
dorm.migrations.lint.lint_directory(directory: Path) -> LintResult
¶
Walk directory recursively for .py migration files and
aggregate findings across all of them.
dorm.migrations.lint.lint_migration_file(path: Path) -> LintResult
¶
Load path, instantiate its Migration class, and lint its
operations. Returns a :class:LintResult.
The temporary module entry is registered in sys.modules (so
relative imports inside the migration resolve), then dropped on
exit so a long-running linter run on hundreds of migrations
doesn't grow sys.modules without bound.
dorm.migrations.lint.lint_operations(operations: list[Any], *, file: Path | str = '<inline>', suppressed: set[str] | None = None) -> LintResult
¶
Lint a single migration's operations list. Programmatic
entry point used by tests; the CLI walks the filesystem and feeds
each loaded migration in.
dorm.migrations.lint.Finding
dataclass
¶
dorm.migrations.lint.LintResult
dataclass
¶
Rules¶
| Code | Trigger |
|---|---|
DORM-M000 |
Migration file failed to import (parse error). |
DORM-M001 |
AddField(null=False, default=…) — full-table backfill at migrate time. |
DORM-M002 |
AlterField — review whether it changes the type (table rewrite on PG / MySQL) or just toggles NOT NULL / default. |
DORM-M003 |
AddIndex without concurrently=True (PG) — ACCESS EXCLUSIVE lock. |
DORM-M004 |
RunPython without reverse_code — irreversible. |
Suppress per-file with a # noqa: DORM-M00X comment anywhere in
the file.