Deliver BAT-003: daemon .env loading (REQ-010, minor-change)
DaemonConfig.from_env() now best-effort loads a .env file (find_dotenv usecwd, load_dotenv override=False) so operators can manage daemon env vars via .env, with real OS env taking precedence. Add python-dotenv dependency, gitignore .env, ship .env.example, and add config tests (load / OS-env precedence / absence regression / missing-DSN guard). Verification: 78 passed, ruff clean, .env ignored + .env.example tracked, and a .env-only DaemonConfig smoke. Processed via the minor-change fast path: REQ-010 (Configuration) -> Implemented, BAT-003 (batch-lite, no design) -> release-candidate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,24 @@
|
||||
"""Daemon configuration resolution (REQ-005)."""
|
||||
"""Daemon configuration resolution (REQ-005, REQ-010)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
def _load_dotenv() -> None:
|
||||
"""Best-effort load of a .env file into os.environ (REQ-010).
|
||||
|
||||
Real OS environment variables take precedence over .env (override=False),
|
||||
matching 12-factor expectations. If python-dotenv is not installed or no
|
||||
.env exists, this is a no-op (no regression).
|
||||
"""
|
||||
try:
|
||||
from dotenv import find_dotenv, load_dotenv
|
||||
except ImportError: # pragma: no cover - optional at runtime
|
||||
return
|
||||
load_dotenv(find_dotenv(usecwd=True), override=False)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DaemonConfig:
|
||||
dsn: str
|
||||
@@ -14,6 +28,7 @@ class DaemonConfig:
|
||||
|
||||
@staticmethod
|
||||
def from_env() -> "DaemonConfig":
|
||||
_load_dotenv()
|
||||
dsn = os.environ.get("TREX_POSTGRESQL_DSN")
|
||||
if not dsn:
|
||||
raise RuntimeError(
|
||||
|
||||
Reference in New Issue
Block a user