"""Health-related CLI commands.""" from __future__ import annotations import typer from app.cli.core.utils import print_json from app.db.migrations import ( bootstrap_or_upgrade_to_head, current_revision, head_revision, ) from app.repositories.factory import build_portfolio_db_config from app.repositories.postgres import create_database_if_missing, database_exists def initialize_database(*, settings, maintenance_db: str = "postgres") -> dict[str, object]: config = build_portfolio_db_config(settings) existed_before = database_exists(config, maintenance_db=maintenance_db) created = False if not existed_before: created = create_database_if_missing(config, maintenance_db=maintenance_db) bootstrap_or_upgrade_to_head(settings) return { "database": config.dbname, "host": config.host, "port": config.port, "created": created, "schema_initialized": True, "migration_applied": "head", } def register_health_commands(app: typer.Typer) -> None: @app.command("init-db") def init_db( ctx: typer.Context, maintenance_db: str = typer.Option("postgres", help="Database to connect to for CREATE DATABASE."), ) -> None: app_context = ctx.obj try: result = initialize_database(settings=app_context.settings, maintenance_db=maintenance_db) except Exception as exc: app_context.console.print(f"init-db failed: {exc}") raise typer.Exit(code=1) from exc else: if app_context.output_format == "json": print_json(app_context.console, result) else: app_context.console.print(f"database: {result['database']}") app_context.console.print(f"host: {result['host']}") app_context.console.print(f"port: {result['port']}") app_context.console.print(f"created: {result['created']}") app_context.console.print("schema_initialized: True") app_context.console.print("migration_applied: head") @app.command("migrate-db") def migrate_db(ctx: typer.Context) -> None: app_context = ctx.obj try: bootstrap_or_upgrade_to_head(app_context.settings) result = { "database": app_context.settings.active_db_name, "host": app_context.settings.active_db_host, "port": app_context.settings.active_db_port, "migration_applied": "head", } except Exception as exc: app_context.console.print(f"migrate-db failed: {exc}") raise typer.Exit(code=1) from exc else: if app_context.output_format == "json": print_json(app_context.console, result) else: app_context.console.print(f"database: {result['database']}") app_context.console.print(f"host: {result['host']}") app_context.console.print(f"port: {result['port']}") app_context.console.print("migration_applied: head") @app.command("health-check") def health_check(ctx: typer.Context) -> None: app_context = ctx.obj repository = app_context.repository_factory(app_context.settings) try: health = { "app_name": app_context.settings.app_name, "environment": app_context.settings.environment, "db_enabled": app_context.settings.db_enabled, "active_db_host": app_context.settings.active_db_host, "active_db_port": app_context.settings.active_db_port, "repository": repository.__class__.__name__, } if hasattr(repository, "ping"): repository.ping() if app_context.settings.db_enabled: database_revision = current_revision(app_context.settings) expected_revision = head_revision(app_context.settings) health["database_revision"] = database_revision health["expected_revision"] = expected_revision if database_revision != expected_revision: raise RuntimeError( f"database revision {database_revision!r} is not packaged head {expected_revision!r}" ) except Exception as exc: app_context.console.print(f"health-check failed: {exc}") raise typer.Exit(code=1) from exc else: if app_context.output_format == "json": print_json(app_context.console, health) else: for key, value in health.items(): app_context.console.print(f"{key}: {value}") finally: if callable(getattr(type(repository), "close", None)): repository.close()