Safely converge Alembic migration graphs by automatically generating minimal merge migrations.
Alembic Converger is a production-grade Python tool that detects and resolves multiple migration heads in your Alembic migration history. It creates graph-only merge migrations without modifying schema operations, ensuring safe convergence for teams working with parallel database migrations.
When multiple developers work on database migrations in parallel, or when merging feature branches, you often end up with multiple heads in your Alembic migration graph:
[feature-1] [feature-2]
| |
rev-abc rev-def
\ /
\ /
[main branch]
|
rev-123
Alembic requires a single linear path to head, and having multiple heads can cause:
- Failed deployments
- CI/CD pipeline failures
- Confusion about which migrations to apply
Traditional approach:
# Manual merge - error-prone and time-consuming
alembic merge -m "merge heads" rev-abc rev-def
# Did it create schema conflicts? Who knows! 😰With Alembic Converger:
# Automatic, safe convergence
alembic-converger converge --from-revision rev-123
# ✓ Validates merge is graph-only
# ✓ Tests upgrade path automatically
# ✓ Fails fast on conflicts🔒 This tool is designed to be safe by default:
- Never modifies existing migrations - Only creates new merge migrations
- Never guesses developer intent - Only performs graph-only merges
- Never auto-resolves schema conflicts - Fails if merge contains operations
- Validates upgrade path - Runs
alembic upgradeafter each merge - Deterministic behavior - Same input always produces same result
- Automatic rollback - Deletes created merges on failure
pip install alembic-convergerRequirements:
- Python 3.8+
- Alembic 1.7.0+
# Converge all heads from a base revision
alembic-converger converge --from-revision ae1027a6acf
# Dry run to preview changes
alembic-converger converge --from-revision ae1027a6acf --dry-run
# Skip database validation (faster, less safe)
alembic-converger converge --from-revision ae1027a6acf --skip-validation
# Verbose output for debugging
alembic-converger converge --from-revision ae1027a6acf --verbosefrom alembic_converger import converge_migrations
# Programmatic convergence
result = converge_migrations(
base_revision="ae1027a6acf",
config_path="alembic.ini",
dry_run=False,
skip_validation=False,
verbose=True
)
print(f"Created {len(result.merges_created)} merge(s)")
print(f"Final head: {result.final_head}")Alembic Converger treats your migrations as a directed acyclic graph (DAG):
- Discovery: Find all heads descending from a known base revision
- Validation: Ensure all heads share a common ancestor
- Convergence: Iteratively merge heads pairwise until one remains
- Verification: After each merge:
- Validate the merge contains no schema operations
- Run
alembic upgradeto test the path - Reload the graph and continue
Initial state (3 heads):
head-1 head-2 head-3
\ | /
\ | /
base (ae1027a6acf)
After convergence:
final-head
|
merge-2
/ \
merge-1 head-3
/ \
head-1 head-2
\ | /
base
name: Check Alembic Convergence
on: [pull_request]
jobs:
check-migrations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install alembic-converger
pip install -r requirements.txt
- name: Check for multiple heads
run: |
# Get the base revision (e.g., from main branch)
BASE_REV=$(git merge-base origin/main HEAD)
# Try to converge (will fail if conflicts exist)
alembic-converger converge \
--from-revision $BASE_REV \
--dry-run#!/bin/bash
# deploy.sh
echo "Checking for multiple Alembic heads..."
alembic-converger converge --from-revision $(git describe --tags --abbrev=0) --dry-run
if [ $? -eq 0 ]; then
echo "✓ Migrations converged"
# Continue with deployment
else
echo "✗ Migration convergence failed"
exit 1
fialembic-converger converge [OPTIONS]
Options:
--from-revision TEXT Base revision to converge from [required]
--alembic-config TEXT Path to alembic.ini [default: alembic.ini]
--dry-run Show what would be done without changes
--skip-validation Skip upgrade validation (not recommended)
--verbose, -v Enable debug logging
--help Show this message and exit
0: Success (already converged or successfully merged)1: Convergence failed (validation error, conflicts, etc.)2: Configuration error (invalid alembic.ini, missing files, etc.)
alembic merge creates a single merge migration but:
- Doesn't validate the merge is conflict-free
- Doesn't test the upgrade path
- Requires manual intervention for each merge
- Can silently create broken migrations
Alembic Converger automates this safely with validation.
Safety first. Schema conflicts require developer judgment:
- Which column definition to keep?
- How to handle conflicting constraints?
- What about data migrations?
Auto-resolving could cause data loss or corruption. Instead, we fail fast and let you resolve manually.
Perfect for:
- ✅ CI/CD pipelines (pre-deployment checks)
- ✅ Multi-developer teams with parallel migrations
- ✅ Feature branch merges
- ✅ Automated migration management
Not suitable for:
- ❌ Resolving schema conflicts (requires manual intervention)
- ❌ Rewriting migration history (never modifies existing files)
When validation fails, Alembic Converger:
- Logs the detailed error
- Rolls back created merge migrations
- Exits with code 1
You'll need to manually resolve conflicts:
# Review the conflicting migrations
alembic history --verbose
# Manually create merge with conflict resolution
alembic merge -m "resolve conflicts" rev-1 rev-2
# Edit the generated file to resolve conflictsYes, with --skip-validation:
alembic-converger converge --from-revision X --skip-validationHowever, this skips upgrade testing, reducing safety guarantees. Use only when:
- Database is not available (e.g., CI without DB)
- You'll test manually later
Alembic Converger is built with clean separation of concerns:
alembic_converger/
├── cli.py # Click-based CLI interface
├── config.py # Alembic configuration loading
├── graph.py # DAG analysis & traversal
├── merge.py # Merge creation & validation
├── validate.py # Upgrade path testing
├── converge.py # Main orchestration algorithm
├── errors.py # Exception hierarchy
└── utils.py # Shared utilities
- Graph-first thinking: Migrations are nodes in a DAG, not files
- Fail fast: Detect problems early with clear errors
- Idempotent: Same input → same output every time
- No side effects: Only creates merge files, never modifies existing ones
- Observable: Comprehensive logging at every step
# Clone the repository
git clone https://github.com/rishav00a/alembic_converger.git
cd alembic_converger
# Create virtual environment
python -m venv venv
source venv/bin/activate # or `venv\Scripts\activate` on Windows
# Install in development mode
pip install -e ".[dev]"# Run all tests
pytest
# With coverage
pytest --cov=alembic_converger --cov-report=html
# Run specific test file
pytest tests/test_graph.py -v# Format code
black alembic_converger tests
# Lint code
ruff check alembic_converger tests
# Type checking (optional)
mypy alembic_converger# Install build tools
pip install build twine
# Build distribution
python -m build
# Check package
twine check dist/*
# Upload to TestPyPI (optional)
twine upload --repository testpypi dist/*
# Upload to PyPI
twine upload dist/*Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run tests and linting
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Built with ❤️ by the open-source community.
Powered by:
- Alembic - Database migrations for SQLAlchemy
- Click - Command-line interface creation
- Colorama - Cross-platform colored terminal text
Remember: This tool is a graph normalizer, not a migration author. It safely converges parallel development paths without guessing your intent. ✨