Safe migrations for production PostgreSQL databases
Project description
🛡️ migsafe
Safe Alembic migrations for production PostgreSQL databases
migsafe (v0.4.1) is a CLI tool and CI linter that analyzes Alembic and Django migrations before they are applied and warns about dangerous, slow, and blocking schema changes.
Project goal — catch migration problems before deployment, not during production downtime.
📋 Contents
- Why it’s needed
- What migsafe does
- Installation
- Quick start
- Using in CI
- Supported operations
- Examples
- How it works
- Limitations
- Roadmap
- Contributing
❓ Why it’s needed
Alembic makes migrations convenient, but not safe by default.
Typical problems migsafe catches early:
| Problem | Consequences |
|---|---|
| ❌ ADD COLUMN NOT NULL DEFAULT | Rewrites the entire table |
| ❌ CREATE INDEX without CONCURRENTLY | Blocks writes |
| ❌ ALTER COLUMN TYPE | Long exclusive lock |
| ❌ DROP COLUMN | Data loss |
| ❌ Raw op.execute() | Dangerous SQL (UPDATE/DELETE without WHERE, DDL without CONCURRENTLY) |
| ❌ Data migrations without batching | Long-lasting locks (large INSERTs without LIMIT) |
💡 This may “work” on a small database.
⚠️ In production — downtime, night deployments, and emergency rollbacks.
✨ What migsafe does
| Capability | Description |
|---|---|
| 🔍 AST analysis | Analyzes Alembic migrations via AST |
| 🧠 Smart detection | Determines real migration behavior, not just its code |
| 🔎 SQL analysis | Analyzes SQL in op.execute() for dangerous patterns (DDL, JOINs, subqueries, CTEs) |
| ⚠️ Risk classification | OK / WARNING / CRITICAL |
| 💥 CI integration | Fails CI if a migration is dangerous |
| 💡 Recommendations | Suggests how to rewrite migrations safely |
| 🔧 Autofix | Automatically fixes some issues with backup creation |
| 📊 Statistics | Collects statistics and metrics across all migrations |
| ⚙️ Configuration | Flexible configuration via JSON/TOML |
| 🎯 Snapshot execution | Runs migrations on production DB snapshots and measures metrics |
| 📜 History analysis | Analyzes migration history via Git to detect problematic patterns |
| 🐍 Django support | Analyzes Django migrations alongside Alembic |
| 🔌 Plugin system | Create custom analysis rules via plugins |
✅ Code is not executed.
✅ Database is not touched.
✅ Analysis is completely safe.
📦 Installation
From PyPI (recommended)
# Basic installation
pip install migsafe
# With optional dependencies for snapshot execution
pip install migsafe[executors]
# With optional dependencies for improved text output
pip install migsafe[formatters]
# All optional dependencies
pip install migsafe[executors,formatters]
From source (for development)
git clone https://github.com/SuccubHunter/migsafe.git
cd migsafe
pip install -e .
# Or with optional dependencies
pip install -e ".[executors,formatters]"
Requirements: Python >= 3.9
Optional dependencies:
executors— formigsafe execute(requirespsycopg2-binary,alembic,sqlalchemy)formatters— improved text output (requiresrich)
🚀 Quick start
Basic usage
In a project directory with Alembic:
migsafe analyze
Analyze specific files/directories
# Analyze a directory
migsafe analyze migrations/
# Analyze a specific file
migsafe analyze migrations/versions/001_add_user.py
Save report
# HTML report
migsafe analyze --format html --output report.html
# JSON report
migsafe analyze --format json --output report.json
Available output formats: text (default), json, html, junit, sarif
Automatic fixes
migsafe can automatically fix some issues:
# Show fixes (dry-run)
migsafe analyze --autofix
# Apply fixes (creates backup)
migsafe analyze --autofix --apply
# Apply without confirmation
migsafe analyze --autofix --apply --yes
# Apply without backup (not recommended)
migsafe analyze --autofix --apply --no-backup
Supported fixes:
ADD COLUMN NOT NULL→ safe pattern (nullable → backfill → NOT NULL)CREATE INDEX→ addCONCURRENTLYDROP INDEX→ addCONCURRENTLY
Configuration file
You can configure migsafe using a config file:
migsafe.json:
{
"exclude": ["**/test_*.py", "**/__pycache__/**"],
"format": "json",
"severity": "warning",
"no_color": true,
"exit_code": true
}
migsafe.toml:
[migsafe]
exclude = ["**/test_*.py", "**/__pycache__/**"]
format = "json"
severity = "warning"
no_color = true
exit_code = true
Usage:
migsafe analyze --config migsafe.json
CLI parameters override configuration file settings.
Migration statistics
The migsafe stats command collects and analyzes migration statistics:
# Show overall statistics
migsafe stats
# Statistics for a specific migration
migsafe stats --migration 001_add_users.py
# Export to JSON
migsafe stats --format json --output stats.json
# Export to CSV
migsafe stats --format csv --output stats.csv
# Filter by severity
migsafe stats --severity critical
# Filter by rule
migsafe stats --rule add_column_not_null_rule
What statistics show:
- Total number of migrations and issues
- Distribution by issue type and severity
- Top issues and rules by frequency
- Automatic recommendations to improve migration practices
Available formats: text (default), json, csv
Demo project
To see the library in action:
python demo/run_demo.py
The demo includes examples of dangerous and safe migrations. See demo/README.md.
New features in version 0.4
Running migrations on snapshots
Execute migrations on production DB snapshots to measure real performance metrics:
⚠️ Required: To use
migsafe execute, install optional dependencies:pip install migsafe[executors]
# Create a snapshot and run migration
migsafe execute migration.py --snapshot-url postgresql://user:pass@localhost/db
# Run on an existing snapshot
migsafe execute migration.py --snapshot-url postgresql://user:pass@localhost/db \
--snapshot-name my_snapshot
# Save results to JSON
migsafe execute migration.py --snapshot-url postgresql://user:pass@localhost/db \
--format json --output results.json
Git-based migration history analysis
Track migration changes over time to detect problematic patterns:
# Analyze full migration history
migsafe history
# Analyze a specific migration
migsafe history --migration migrations/001_add_user.py
# Filter by date
migsafe history --since 2025-01-01 --until 2025-12-31
# Filter by author
migsafe history --author "Ivan Ivanov"
Django migration support
Analyze Django migrations alongside Alembic:
# Auto-detect Django migrations
migsafe analyze
# Analyze migrations of a specific app
migsafe analyze --django-app myapp
# Analyze multiple apps
migsafe analyze --django-app myapp --django-app otherapp
Plugins
Load custom analysis rules via plugins:
# Use plugins from directory
migsafe analyze --plugins-dir ./plugins
# List loaded plugins
migsafe plugins list
# Plugin info
migsafe plugins info my-plugin
📊 Output example
Migration: 2025_12_31_add_email.py
[CRITICAL] add_column_not_null
Table: users
Column: email
Message:
Adding NOT NULL column 'email' to table 'users' rewrites entire table and blocks writes in PostgreSQL
Recommendation:
Use safe pattern:
1) Add column as nullable: op.add_column(..., nullable=True)
2) Backfill data in batches: op.execute('UPDATE ... WHERE ...')
3) Set NOT NULL constraint: op.alter_column(..., nullable=False)
🔧 Using in CI/CD
Quick start
For CI/CD integration, use migsafe lint, which returns a non-zero exit code if critical issues are found:
migsafe lint --format junit --output report.xml --no-color
Behavior
| Level | Exit Code | Behavior |
|---|---|---|
| CRITICAL | 1 |
Build fails (migsafe lint or migsafe analyze --exit-code) |
| WARNING | 0 |
Build passes with warnings |
| OK | 0 |
Build passes |
💡 Note:
migsafe lintautomatically returns a non-zero exit code on critical issues, making it ideal for CI/CD.
Integration examples
Ready-to-use configs for various CI/CD systems are available in examples/ci/:
(…content continues unchanged…)
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 migsafe-0.4.1.tar.gz.
File metadata
- Download URL: migsafe-0.4.1.tar.gz
- Upload date:
- Size: 177.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
365cae2c2b4b97ba019430e308f143d622ff56272bd10d2e91278878c2338f14
|
|
| MD5 |
1ff9711bed1df3130a9020d13a21ca05
|
|
| BLAKE2b-256 |
5b58fdf5d2e80951c7d4d9fdcfedd0f9caa193170f65b0d01a6c075f43b2189c
|
File details
Details for the file migsafe-0.4.1-py3-none-any.whl.
File metadata
- Download URL: migsafe-0.4.1-py3-none-any.whl
- Upload date:
- Size: 135.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a60c440e31bbe0279fcb70e3527d3d9c0a6cb454edcb2fd9ab812af3f7b60fc9
|
|
| MD5 |
8a595ccc035d10405184ccde87de4aa8
|
|
| BLAKE2b-256 |
f554386f6210a4c3aab3715cd2fe0f217cf87640c1ffa19e5638a5001a757c02
|