Harden location db startup validation

This commit is contained in:
2026-04-19 23:02:43 +02:00
parent 8aeb0723c1
commit 1a2f9c75d9
7 changed files with 266 additions and 17 deletions
+59
View File
@@ -54,6 +54,30 @@ def _location_table_exists(database_path: Path) -> bool:
conn.close()
def _alembic_version_table_exists(database_path: Path) -> bool:
conn = sqlite3.connect(database_path)
try:
row = conn.execute(
"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'alembic_version'"
).fetchone()
return row is not None
finally:
conn.close()
def _fetch_alembic_revision(database_path: Path) -> str:
conn = sqlite3.connect(database_path)
try:
row = conn.execute("SELECT version_num FROM alembic_version").fetchone()
if row is None:
raise LocationDatabaseAdoptionError(
"Alembic version table exists but contains no revision"
)
return row[0]
finally:
conn.close()
def _fetch_location_table_info(database_path: Path) -> list[tuple]:
conn = sqlite3.connect(database_path)
try:
@@ -91,11 +115,44 @@ def validate_legacy_location_db(database_url: str) -> None:
)
def validate_location_runtime_db(database_url: str) -> None:
database_path = _database_path_from_url(database_url)
if not database_path.exists():
raise LocationDatabaseAdoptionError(
"Location DB file was not found. Run 'python scripts/location_db_adopt.py' "
"first to initialize or adopt the location DB before starting the app."
)
if not _alembic_version_table_exists(database_path):
raise LocationDatabaseAdoptionError(
"Location DB exists but is not yet Alembic-managed. Run "
"'python scripts/location_db_adopt.py' first to adopt the legacy DB "
"before starting the app."
)
current_revision = _fetch_alembic_revision(database_path)
if current_revision != LOCATION_BASELINE_REVISION:
raise LocationDatabaseAdoptionError(
"Location DB revision mismatch. Refusing to start the app: "
f"expected {LOCATION_BASELINE_REVISION}, got {current_revision}"
)
def adopt_or_initialize_location_db(database_url: str) -> str:
database_path = _database_path_from_url(database_url)
alembic_config = _make_alembic_config(database_url)
if database_path.exists():
if _alembic_version_table_exists(database_path):
current_revision = _fetch_alembic_revision(database_path)
if current_revision != LOCATION_BASELINE_REVISION:
raise LocationDatabaseAdoptionError(
"Location DB is already Alembic-managed but revision does not match "
f"the expected baseline: expected {LOCATION_BASELINE_REVISION}, "
f"got {current_revision}"
)
return "already_managed"
validate_legacy_location_db(database_url)
command.stamp(alembic_config, LOCATION_BASELINE_REVISION)
return "adopted"
@@ -110,6 +167,8 @@ def main() -> None:
result = adopt_or_initialize_location_db(settings.location_database_url)
if result == "initialized":
print("Initialized a new location DB via Alembic upgrade head.")
elif result == "already_managed":
print("Location DB is already Alembic-managed at the expected baseline revision.")
else:
print("Validated legacy location DB and stamped Alembic baseline successfully.")