feat(container): harden v0.1.1 publication
This commit is contained in:
@@ -5,9 +5,13 @@ from __future__ import annotations
|
||||
import typer
|
||||
|
||||
from app.cli.core.utils import print_json
|
||||
from app.db.migrations import bootstrap_or_upgrade_to_head
|
||||
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 PostgresPortfolioRepository, create_database_if_missing, database_exists
|
||||
from app.repositories.postgres import create_database_if_missing, database_exists
|
||||
|
||||
|
||||
def initialize_database(*, settings, maintenance_db: str = "postgres") -> dict[str, object]:
|
||||
@@ -90,6 +94,15 @@ def register_health_commands(app: typer.Typer) -> None:
|
||||
}
|
||||
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
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ if env_file.exists():
|
||||
|
||||
class Settings(BaseSettings):
|
||||
app_name: str = "PortfolioHub"
|
||||
app_version: str = "0.1.0"
|
||||
app_version: str = "0.1.1"
|
||||
environment: str = "development"
|
||||
allowed_origins_raw: str = Field(default="http://localhost:3000", validation_alias="PORTFOLIOHUB_ALLOWED_ORIGINS")
|
||||
bootstrap_username: str = Field(default="", validation_alias="PORTFOLIOHUB_BOOTSTRAP_USERNAME")
|
||||
|
||||
+12
-7
@@ -7,6 +7,7 @@ from importlib import resources
|
||||
import psycopg
|
||||
from alembic import command
|
||||
from alembic.config import Config
|
||||
from alembic.script import ScriptDirectory
|
||||
|
||||
from app.core.config import Settings
|
||||
|
||||
@@ -61,15 +62,19 @@ def bootstrap_or_upgrade_to_head(settings: Settings) -> str:
|
||||
|
||||
|
||||
def current_revision(settings: Settings) -> str | None:
|
||||
output: list[str] = []
|
||||
"""Read the database revision without applying or stamping migrations."""
|
||||
with _connect(settings) as conn, conn.cursor() as cur:
|
||||
cur.execute("SELECT version_num FROM alembic_version")
|
||||
row = cur.fetchone()
|
||||
return str(row[0]) if row else None
|
||||
|
||||
def capture(message: str, *_args, **_kwargs) -> None:
|
||||
if message.strip():
|
||||
output.append(message.strip())
|
||||
|
||||
config = get_alembic_config(settings)
|
||||
command.current(config, verbose=False)
|
||||
return output[-1] if output else None
|
||||
def head_revision(settings: Settings) -> str:
|
||||
"""Return the single packaged Alembic head without touching the database."""
|
||||
heads = ScriptDirectory.from_config(get_alembic_config(settings)).get_heads()
|
||||
if len(heads) != 1:
|
||||
raise RuntimeError(f"expected one Alembic head, found {len(heads)}")
|
||||
return heads[0]
|
||||
|
||||
|
||||
def _connect(settings: Settings) -> psycopg.Connection:
|
||||
|
||||
Reference in New Issue
Block a user