refactor(databases): state the databases.csv contract once

The schema lived three times in this repo alone: the seed and the backup each spelled out the column list and the semicolon, and _validate_database_value existed twice under one name with different strictness - the seed checked the character set, the backup only checked for empty. A hand-edited file therefore bypassed the only real validation on its way into a dump command.

baudolo.databases now holds the columns, the delimiter, the cluster marker, the validator and a read_rows() for consumers that do not want pandas. Values come back verbatim: a password may begin or end with a space, so stripping belongs to the caller that compares, never to the reader. DatabasesCsvError subclasses ValueError, so callers that predate the module keep catching what they caught.

The backup's call sites switch over in the next commit, which rewrites them anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-17 16:28:15 +02:00
parent df1c65ccac
commit 03013b6c76
5 changed files with 206 additions and 43 deletions

View File

@@ -7,6 +7,8 @@ import sys
import pandas
from pandas.errors import EmptyDataError
from baudolo.databases import COLUMNS, DELIMITER
from .db import backup_database
from .docker import has_tool, image_id
@@ -81,7 +83,7 @@ def _empty_databases_df() -> pandas.DataFrame:
This allows the backup to continue without DB dumps when the CSV is missing
or empty (pandas EmptyDataError).
"""
return pandas.DataFrame(columns=["instance", "database", "username", "password"])
return pandas.DataFrame(columns=list(COLUMNS))
def load_databases_df(csv_path: str) -> pandas.DataFrame:
@@ -93,7 +95,9 @@ def load_databases_df(csv_path: str) -> pandas.DataFrame:
- Valid CSV -> return dataframe
"""
try:
return pandas.read_csv(csv_path, sep=";", keep_default_na=False, dtype=str)
return pandas.read_csv(
csv_path, sep=DELIMITER, keep_default_na=False, dtype=str
)
except FileNotFoundError:
print(
f"WARNING: databases.csv not found: {csv_path}. Continuing without database dumps.",