"""Fail-closed checks required before starting managed runtime processes.""" from dataclasses import dataclass from sqlalchemy import create_engine from sqlalchemy.pool import NullPool from app.core.config import Settings, get_settings from app.core.database.migrations import ( current_alembic_revisions, expected_alembic_heads, ) from app.core.database.safety import validate_platform_migration_target from app.modules.feishu_users.constants import parse_admin_identities class RuntimePreflightError(RuntimeError): """Raised when the configured runtime is not safe to start.""" @dataclass(frozen=True, slots=True) class RuntimePreflightResult: revisions: tuple[str, ...] transport: str def run_preflight(settings: Settings | None = None) -> RuntimePreflightResult: """Validate the platform target, migration head, and enabled transport.""" runtime_settings = settings or get_settings() validate_platform_migration_target( runtime_settings.database_url, runtime_settings.legacy_database_url, ) if runtime_settings.feishu_event_transport == "long_connection" and ( not runtime_settings.feishu_app_id or not runtime_settings.feishu_app_secret ): raise RuntimePreflightError( "FEISHU_APP_ID and FEISHU_APP_SECRET are required for long_connection" ) if runtime_settings.feishu_admin_identities: try: parse_admin_identities(runtime_settings.feishu_admin_identities) except ValueError as exc: raise RuntimePreflightError(str(exc)) from exc expected = expected_alembic_heads() if len(expected) != 1: raise RuntimePreflightError("Runtime requires exactly one Alembic head") engine = None try: engine = create_engine(runtime_settings.database_url, poolclass=NullPool) with engine.connect() as connection: current = current_alembic_revisions(connection) except Exception as exc: raise RuntimePreflightError( "Platform database schema could not be inspected" ) from exc finally: if engine is not None: engine.dispose() if current is None: raise RuntimePreflightError( "Platform database is not Alembic-versioned; run the approved migration first" ) if current != expected: raise RuntimePreflightError( "Platform database is not at the current Alembic head" ) return RuntimePreflightResult( revisions=current, transport=runtime_settings.feishu_event_transport, ) def main() -> None: try: result = run_preflight() except (RuntimeError, ValueError) as exc: raise SystemExit(f"Runtime preflight failed: {exc}") from exc print( "Runtime preflight passed: " f"schema={result.revisions[0]}, transport={result.transport}" ) if __name__ == "__main__": main()