From 4273ed0e5cdeea2e90f7f5ff2873a21ff24789f8 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Sat, 11 Apr 2026 03:35:04 +0000 Subject: [PATCH] fix: use Railway native PG env vars for Alembic migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior approach (ADMIN_DATABASE_URL first) broke PR preview environments: fresh Railway PostgreSQL instances have no resolutionflow_admin role yet, so the admin URL fails before the create_db_roles migration can run (bootstrap deadlock). New priority order in _alembic_sync_url(): 1. PGHOST/PGUSER/PGPASSWORD/PGDATABASE — Railway auto-links these from the PostgreSQL service per-environment, giving correct superuser creds for every env including fresh PR preview DBs where no custom roles exist yet. 2. ADMIN_DATABASE_URL (resolutionflow_admin, BYPASSRLS, asyncpg→sync) — local dev and stable envs where the role already exists. 3. DATABASE_URL_SYNC — legacy fallback. Co-Authored-By: Claude Sonnet 4.6 --- backend/alembic/env.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 62b009f5..5610b382 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -29,19 +29,35 @@ from app.models.session_branch import SessionBranch # noqa: F401 from app.models.fork_point import ForkPoint # noqa: F401 from app.models.session_handoff import SessionHandoff # noqa: F401 from app.models.session_resolution_output import SessionResolutionOutput # noqa: F401 +import os + from app.core.config import settings def _alembic_sync_url() -> str: """Return a psycopg2-compatible sync URL for Alembic. - Prefers ADMIN_DATABASE_URL (resolutionflow_admin, BYPASSRLS) converted to - a sync driver. Falls back to DATABASE_URL_SYNC for local dev where the - admin URL may not be configured separately. + Priority order: + 1. Railway native PG env vars (PGHOST/PGPASSWORD/PGUSER/PGDATABASE) — these + are auto-linked per-environment by the Railway PostgreSQL service, so they + always carry the correct superuser credentials for the current environment + (production, PR preview, etc.), including fresh DBs with no custom roles yet. + 2. ADMIN_DATABASE_URL (resolutionflow_admin, BYPASSRLS) converted to a sync + driver — used for local dev and stable environments where the role exists. + 3. DATABASE_URL_SYNC — last resort / legacy fallback. """ + pg_host = os.getenv("PGHOST") + pg_user = os.getenv("PGUSER") + pg_password = os.getenv("PGPASSWORD") + pg_db = os.getenv("PGDATABASE") + pg_port = os.getenv("PGPORT", "5432") + if all([pg_host, pg_user, pg_password, pg_db]): + return f"postgresql://{pg_user}:{pg_password}@{pg_host}:{pg_port}/{pg_db}" + admin_url = settings.ADMIN_DATABASE_URL if admin_url and "+asyncpg" in admin_url: return admin_url.replace("postgresql+asyncpg://", "postgresql://") + return settings.DATABASE_URL_SYNC