"""Post-init_db bootstrap: run or stamp Alembic migrations. On a FRESH Postgres install, init_db's create_all has already built the full schema from the current models. Running `alembic upgrade head` would fail because the older migrations try ADD COLUMN on columns that already exist. So we detect the fresh-install case (alembic_version table is missing or empty) and `stamp head` instead. On an EXISTING install, the alembic_version table has a revision and `upgrade head` applies only the new deltas. """ import subprocess import sys from sqlalchemy import create_engine, text, inspect from app.config import settings def run(): # Use a sync engine for this one-shot script. sync_url = settings.database_url.replace("+asyncpg", "").replace("+aiosqlite", "") engine = create_engine(sync_url) with engine.connect() as conn: inspector = inspect(engine) tables = inspector.get_table_names() if "alembic_version" not in tables: # Fresh install — create_all built everything. Stamp head. print("Fresh install detected — stamping alembic head") subprocess.run( [sys.executable, "-m", "alembic", "stamp", "head"], check=True, ) else: row = conn.execute(text("SELECT version_num FROM alembic_version")).first() if row is None: print("Empty alembic_version — stamping head") subprocess.run( [sys.executable, "-m", "alembic", "stamp", "head"], check=True, ) else: print(f"Existing install at revision {row[0]} — running alembic upgrade head") subprocess.run( [sys.executable, "-m", "alembic", "upgrade", "head"], check=True, ) engine.dispose() if __name__ == "__main__": run()