A git pre-commit hook that ensures database model and entity changes are always accompanied by a new Alembic migration.
When working with database models in a project using Alembic for migrations, it's easy to forget to create a migration after modifying model files. This hook automatically detects such cases and prevents commits with unmigrated changes.
The hook uses git staging information to check only the files you're actually committing, preventing false positives from other pre-commit hooks (like ruff, black, etc.) that might modify files.
Add this to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/risclog-solution/check_migration
rev: v0.2.0
hooks:
- id: check-model-vs-migration
name: Check database models have migrations
language: script
args: [
"--model-dirs=src/myapp/database,src/myapp/entities",
"--migration-dir=src/myapp/alembic/versions",
"--exclude=__init__.py,base.py,README.md",
"--exclude-dir=__pycache__,.mypy_cache,.pytest_cache,.git"
]
stages: [manual]| Argument | Required | Description |
|---|---|---|
--model-dirs |
Yes | Comma-separated list of directories containing model/entity files |
--migration-dir |
Yes | Directory where Alembic migration files are stored |
--exclude |
No | Comma-separated list of filenames to exclude from checks |
--exclude-dir |
No | Comma-separated list of directory names to exclude |
Basic setup:
args: [
"--model-dirs=src/models",
"--migration-dir=src/migrations"
]Multiple model directories:
args: [
"--model-dirs=src/database,src/entities,src/schemas",
"--migration-dir=src/alembic/versions",
"--exclude=__init__.py,base.py"
]- Reads all files staged for commit using
git diff --cached - Checks if any files in
--model-dirshave been modified - Filters out excluded files (specified with
--exclude) - If model changes exist, verifies that at least one migration file exists in
--migration-dir - Fails with error message if migrations are missing
- No model files were changed
- Model files were changed AND migration file was staged
- Only excluded files were changed
- Model files were changed but no migration file was staged
- Shows which model files were modified
- Suggests using
alembic revision --autogenerate -m '...'
By default, the hook runs in stages: [manual], so you must explicitly run it:
# Run the check manually
pre-commit run check-model-vs-migration --all-files
# Run all manual-stage hooks
pre-commit run --hook-stage manual --all-filesRun the included test suite:
bash test.shThis executes 5 comprehensive tests covering all scenarios.
Earlier versions used filesystem timestamps, which caused false positives. Other pre-commit hooks like ruff, ruff-format, and pyupgrade modify files, updating their timestamps even without functional changes. The new version uses git diff --cached to only check files you're actually committing, providing accurate, reliable detection.