Merge db-migrations: Add Flask-Migrate support and clean up old migration system
This commit is contained in:
231
docker_migrate_init.py
Executable file
231
docker_migrate_init.py
Executable file
@@ -0,0 +1,231 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Docker-friendly Flask-Migrate initialization.
|
||||
No Git required - works with current schema as baseline.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
|
||||
def run_command(cmd, description, check=True):
|
||||
"""Run a command and handle errors."""
|
||||
print(f"\n➜ {description}")
|
||||
print(f" Command: {cmd}")
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f"✓ Success")
|
||||
if result.stdout.strip():
|
||||
print(f" {result.stdout.strip()}")
|
||||
return True
|
||||
else:
|
||||
print(f"✗ Failed")
|
||||
if result.stderr:
|
||||
print(f" Error: {result.stderr}")
|
||||
if check:
|
||||
sys.exit(1)
|
||||
return False
|
||||
|
||||
def check_database_connection():
|
||||
"""Check if we can connect to the database."""
|
||||
print("\nChecking database connection...")
|
||||
try:
|
||||
from app import app, db
|
||||
with app.app_context():
|
||||
# Try a simple query
|
||||
db.engine.execute("SELECT 1")
|
||||
print("✓ Database connection successful")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"✗ Database connection failed: {e}")
|
||||
return False
|
||||
|
||||
def check_existing_tables():
|
||||
"""Check what tables exist in the database."""
|
||||
print("\nChecking existing tables...")
|
||||
try:
|
||||
from app import app, db
|
||||
with app.app_context():
|
||||
# Get table names
|
||||
inspector = db.inspect(db.engine)
|
||||
tables = inspector.get_table_names()
|
||||
|
||||
if tables:
|
||||
print(f"✓ Found {len(tables)} existing tables:")
|
||||
for table in sorted(tables):
|
||||
if table != 'alembic_version':
|
||||
print(f" - {table}")
|
||||
return True
|
||||
else:
|
||||
print("ℹ️ No tables found (empty database)")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"✗ Error checking tables: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Main initialization function."""
|
||||
print("=== Flask-Migrate Docker Initialization ===")
|
||||
print("\nThis script will set up Flask-Migrate for your Docker deployment.")
|
||||
print("It uses your CURRENT schema as the baseline (no Git required).")
|
||||
|
||||
# Set environment
|
||||
os.environ['FLASK_APP'] = 'app.py'
|
||||
|
||||
# Check prerequisites
|
||||
if not check_database_connection():
|
||||
print("\n❌ Cannot connect to database. Check your DATABASE_URL.")
|
||||
return 1
|
||||
|
||||
has_tables = check_existing_tables()
|
||||
|
||||
print("\n" + "="*50)
|
||||
if has_tables:
|
||||
print("SCENARIO: Existing database with tables")
|
||||
print("="*50)
|
||||
print("\nYour database already has tables. We'll create a baseline")
|
||||
print("migration and mark it as already applied.")
|
||||
else:
|
||||
print("SCENARIO: Empty database")
|
||||
print("="*50)
|
||||
print("\nYour database is empty. We'll create a baseline")
|
||||
print("migration that can be applied to create all tables.")
|
||||
|
||||
response = input("\nContinue? (y/N): ")
|
||||
if response.lower() != 'y':
|
||||
print("Aborting...")
|
||||
return 1
|
||||
|
||||
# Step 1: Clean up any existing migrations
|
||||
if os.path.exists('migrations'):
|
||||
print("\n⚠️ Removing existing migrations directory...")
|
||||
shutil.rmtree('migrations')
|
||||
|
||||
# Step 2: Initialize Flask-Migrate
|
||||
print("\nInitializing Flask-Migrate...")
|
||||
if not run_command("flask db init", "Creating migrations directory"):
|
||||
return 1
|
||||
|
||||
# Step 3: Create baseline migration
|
||||
print("\nCreating baseline migration from current models...")
|
||||
baseline_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
if not run_command(
|
||||
f'flask db migrate -m "Docker baseline migration - {baseline_date}"',
|
||||
"Generating migration"
|
||||
):
|
||||
return 1
|
||||
|
||||
# Step 4: Add documentation to the migration
|
||||
print("\nDocumenting the migration...")
|
||||
try:
|
||||
import glob
|
||||
migration_files = glob.glob('migrations/versions/*.py')
|
||||
if migration_files:
|
||||
latest = max(migration_files, key=os.path.getctime)
|
||||
|
||||
with open(latest, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
note = f'''"""DOCKER BASELINE MIGRATION
|
||||
Generated: {baseline_date}
|
||||
|
||||
This migration represents the current state of your models.
|
||||
It serves as the baseline for all future migrations.
|
||||
|
||||
For existing databases with tables:
|
||||
flask db stamp head # Mark as current without running
|
||||
|
||||
For new empty databases:
|
||||
flask db upgrade # Create all tables
|
||||
|
||||
DO NOT MODIFY THIS MIGRATION
|
||||
"""
|
||||
|
||||
'''
|
||||
with open(latest, 'w') as f:
|
||||
f.write(note + content)
|
||||
|
||||
print(f"✓ Documented {os.path.basename(latest)}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Could not document migration: {e}")
|
||||
|
||||
# Step 5: Handle based on database state
|
||||
print("\n" + "="*50)
|
||||
print("NEXT STEPS")
|
||||
print("="*50)
|
||||
|
||||
if has_tables:
|
||||
print("\nYour database already has tables. Run this command to")
|
||||
print("mark it as up-to-date WITHOUT running the migration:")
|
||||
print("\n flask db stamp head")
|
||||
print("\nThen you can create new migrations normally:")
|
||||
print(" flask db migrate -m 'Add new feature'")
|
||||
print(" flask db upgrade")
|
||||
else:
|
||||
print("\nYour database is empty. Run this command to")
|
||||
print("create all tables from the baseline migration:")
|
||||
print("\n flask db upgrade")
|
||||
print("\nThen you can create new migrations normally:")
|
||||
print(" flask db migrate -m 'Add new feature'")
|
||||
print(" flask db upgrade")
|
||||
|
||||
# Create a helper script
|
||||
helper_content = f"""#!/bin/bash
|
||||
# Flask-Migrate helper for Docker
|
||||
# Generated: {baseline_date}
|
||||
|
||||
export FLASK_APP=app.py
|
||||
|
||||
case "$1" in
|
||||
status)
|
||||
echo "Current migration status:"
|
||||
flask db current
|
||||
;;
|
||||
|
||||
apply)
|
||||
echo "Applying pending migrations..."
|
||||
flask db upgrade
|
||||
;;
|
||||
|
||||
create)
|
||||
if [ -z "$2" ]; then
|
||||
echo "Usage: $0 create 'Migration message'"
|
||||
exit 1
|
||||
fi
|
||||
echo "Creating new migration: $2"
|
||||
flask db migrate -m "$2"
|
||||
echo "Review the migration, then run: $0 apply"
|
||||
;;
|
||||
|
||||
mark-current)
|
||||
echo "Marking database as current (no changes)..."
|
||||
flask db stamp head
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Flask-Migrate Docker Helper"
|
||||
echo "Usage:"
|
||||
echo " $0 status - Show current migration status"
|
||||
echo " $0 apply - Apply pending migrations"
|
||||
echo " $0 create 'msg' - Create new migration"
|
||||
echo " $0 mark-current - Mark DB as current (existing DBs)"
|
||||
;;
|
||||
esac
|
||||
"""
|
||||
|
||||
with open('migrate.sh', 'w') as f:
|
||||
f.write(helper_content)
|
||||
|
||||
os.chmod('migrate.sh', 0o755)
|
||||
print("\n✓ Created migrate.sh helper script")
|
||||
|
||||
print("\n✨ Initialization complete!")
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user