#!/usr/bin/env python3 """ run_production_migration.py ───────────────────────────────────────────────────────────────────────────── Execute migrate_production.sql against the configured DATABASE_URL. Usage ───── python run_production_migration.py # normal run python run_production_migration.py --dry-run # parse SQL but do NOT commit Environment variables read from .env (or already in shell): DATABASE_URL postgresql://user:pass@host:port/dbname Exit codes: 0 success 1 error """ import argparse import os import sys from pathlib import Path import psycopg2 from dotenv import load_dotenv MIGRATION_FILE = Path(__file__).parent / "migrate_production.sql" def parse_args(): p = argparse.ArgumentParser(description="Run Invy production migration") p.add_argument( "--dry-run", action="store_true", help="Parse and validate the SQL but roll back instead of committing.", ) return p.parse_args() def main(): args = parse_args() load_dotenv() db_url = os.getenv( "DATABASE_URL", "postgresql://wedding_admin:Aa123456@localhost:5432/wedding_guests", ) if not MIGRATION_FILE.exists(): print(f"❌ Migration file not found: {MIGRATION_FILE}") sys.exit(1) sql = MIGRATION_FILE.read_text(encoding="utf-8") print(f"{'[DRY-RUN] ' if args.dry_run else ''}Connecting to database …") try: conn = psycopg2.connect(db_url) except Exception as exc: print(f"❌ Cannot connect: {exc}") sys.exit(1) conn.autocommit = False cursor = conn.cursor() # Capture NOTICE messages from PL/pgSQL RAISE NOTICE import warnings conn.notices = [] def _notice_handler(diag): msg = diag.message_primary or str(diag) conn.notices.append(msg) print(f" [DB] {msg}") conn.add_notice_handler(_notice_handler) try: print("Running migration …") cursor.execute(sql) # Print the summary SELECT result try: row = cursor.fetchone() if row: print( f"\n📊 Summary after migration:\n" f" users : {row[0]}\n" f" events : {row[1]}\n" f" guests_v2 : {row[2]}\n" ) except Exception: pass if args.dry_run: conn.rollback() print("✅ Dry-run complete — rolled back (no changes written).") else: conn.commit() print("✅ Migration committed successfully.") except Exception as exc: conn.rollback() print(f"\n❌ Migration failed — rolled back.\n Error: {exc}") sys.exit(1) finally: cursor.close() conn.close() if __name__ == "__main__": main()