Framework-agnostic linter for database migration folders — catch duplicate version numbers, sequence gaps, and missing down-migrations before CI does. Zero dependencies.
Project description
migrolint
A framework-agnostic linter for your database migrations folder. It catches
the boring, expensive mistakes that pass locally and only blow up when CI
actually runs the migrations — duplicate version numbers, sequence gaps, and
up migrations with no matching down. No database connection, no framework
lock-in, zero dependencies (pure standard library).
pip install migrolint
migrolint # auto-detects ./migrations, db/migrations, ...
migrolint db/migrations --strict
The problem
Two developers branch off main, each adds 0007_add_index.sql, both merge.
Now your migrations folder has two migrations claiming version 0007. Your
runner picks one and silently skips the other — or aborts the whole deploy.
This is a known, recurring failure
in every sequence-numbered migration tool.
The existing linters (django-migration-linter, Flyway's own checks) are tied
to one framework or need a live database. If you use raw SQL with goose,
dbmate, golang-migrate, or a hand-rolled folder, there's nothing that just
looks at the filenames and tells you they're sane. That's migrolint.
What it checks
| Rule | Severity | Meaning |
|---|---|---|
DUPE_NUM |
error | two migrations share a version number (the merge-collision bug) |
MISSING_DOWN |
warning | an up migration has no matching down (only flagged if the project uses up/down splits) |
SEQ_GAP |
warning | a hole in an integer sequence — usually a deleted or un-merged migration |
BAD_FORMAT |
warning | a file whose name no known convention recognizes |
Naming conventions it understands
migrolint reads version numbers out of the filename, across the conventions people actually use:
| Convention | Example |
|---|---|
| Flyway | V1__init.sql, U1__undo.sql, R__refresh.sql, V1.1__patch.sql |
| golang-migrate / dbmate | 0001_create_users.up.sql + 0001_create_users.down.sql |
| goose / Rails | 20230101120000_create_users.sql (timestamp prefix) |
| minimalist | 1_init.sql, 2-add-index.sql |
Timestamp-style versions are recognized but exempt from SEQ_GAP (they're not
meant to be contiguous). Well-known non-migration files (schema.rb,
structure.sql, seeds.rb, …) and any non-migration extension are skipped.
Usage
migrolint # scan the first migrations dir it finds
migrolint db/migrations # scan a specific dir
migrolint app/migrations svc/migrations # scan several
migrolint --json # machine-readable, for tooling
migrolint --strict # warnings become errors (exit 1) — good for CI
migrolint --ext .sql,.py # override which extensions count
migrolint --ignore baseline.sql,seed.sql
You can also run it as a module: python -m migrolint db/migrations.
As a pre-commit / CI gate
# .pre-commit-config.yaml
- repo: local
hooks:
- id: migrolint
name: migrolint
entry: migrolint --strict
language: system
pass_filenames: false
# GitHub Actions
- run: pipx run migrolint --strict
Example output
migrolint db/migrations (14 files, 12 migrations)
✗ DUPE_NUM version 0007 used by 2 migrations:
0007_add_index.up.sql
0007_add_orders_fk.up.sql
⚠ MISSING_DOWN 0009_drop_legacy.up.sql — no matching .down file
⚠ SEQ_GAP missing version(s): 5
1 error, 2 warnings.
Exit codes
| Code | Meaning |
|---|---|
0 |
clean (or only warnings, without --strict) |
1 |
errors found — or warnings, when --strict is set |
2 |
usage / IO error (no migrations dir, unreadable path) |
Also available for Node
Same checks, same flags: npx migrolint
(source: migrolint). Both ports read
filenames identically, so a mixed-language team gets the same verdict.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file migrolint-0.1.0.tar.gz.
File metadata
- Download URL: migrolint-0.1.0.tar.gz
- Upload date:
- Size: 11.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f9219829e8107170f416022f50f3c3e6a5afcf9c3b4d238642ce4eb2d2d0852
|
|
| MD5 |
c035dc9f6307a58198c7734b42acf4cc
|
|
| BLAKE2b-256 |
17d5d4651e2cb85207e3fe78c8f0b31dbd698cfc52f4fa008362b52511dd90dc
|
File details
Details for the file migrolint-0.1.0-py3-none-any.whl.
File metadata
- Download URL: migrolint-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8545a7badb58b204e547931848ce322b7b28c799fa48f4b48e557e9011f4da2d
|
|
| MD5 |
4ac53caeb434ee17c9e635fc205a219a
|
|
| BLAKE2b-256 |
5162dd053830d8564757395b9452fa59232fb3a0b612f6124e82ca56d7e6e177
|