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:
@@ -0,0 +1,10 @@
|
|||||||
|
# trex-core slot-core daemon configuration (copy to .env; .env is gitignored).
|
||||||
|
# Real OS environment variables take precedence over values in .env.
|
||||||
|
|
||||||
|
# Required: psycopg DSN for the PostgreSQL single-writer store.
|
||||||
|
TREX_POSTGRESQL_DSN=dbname=trex_core host=/var/run/postgresql
|
||||||
|
|
||||||
|
# Optional (defaults shown).
|
||||||
|
TREX_TOTAL_SLOTS=10
|
||||||
|
TREX_API_HOST=127.0.0.1
|
||||||
|
TREX_API_PORT=8080
|
||||||
@@ -3,3 +3,4 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
/.env
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# BAT-003 Implementation
|
||||||
|
|
||||||
|
- Status: completed
|
||||||
|
- Batch: BAT-003
|
||||||
|
|
||||||
|
## Plan Summary
|
||||||
|
- REQ-010: 데몬 기동 시 `.env` best-effort 로드(OS env 우선). minor-change batch-lite.
|
||||||
|
|
||||||
|
## Changed Files
|
||||||
|
- pyproject.toml: `python-dotenv>=1.0` 의존성 추가.
|
||||||
|
- trex_core/runtime/config.py: `_load_dotenv()`(best-effort, `override=False`, `find_dotenv(usecwd=True)`) 추가 + `from_env()` 시작부 호출.
|
||||||
|
- .gitignore: `/.env` 추가.
|
||||||
|
- .env.example: 키 목록 참고용 신규(추적 대상).
|
||||||
|
- tests/runtime/test_config_dotenv.py: 로드/우선순위/부재/필수누락 4 테스트.
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
1. config 에 best-effort dotenv 로더 추가(미설치/부재 시 no-op).
|
||||||
|
2. pyproject 의존성 + .gitignore + .env.example.
|
||||||
|
3. 단위테스트 작성, `pip install -e .` 후 실행.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
- `pytest tests/runtime/test_config_dotenv.py`: 4 passed.
|
||||||
|
- 전체 `pytest`: **78 passed** (기존 74 + 신규 4), ruff clean.
|
||||||
|
- gitignore: `.env` IGNORED, `.env.example` trackable.
|
||||||
|
- 스모크: `./.env` 만으로 `DaemonConfig.from_env()` 가 dsn/total/port 로드(OS env 미export) 확인.
|
||||||
|
|
||||||
|
## Remaining Risks
|
||||||
|
- 없음(저위험). dotenv 미설치/.env 부재 시 graceful no-op로 회귀 없음(AC-3 테스트).
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# BAT-003: 데몬 설정 .env 로딩 (minor-change)
|
||||||
|
|
||||||
|
- Status: release-candidate
|
||||||
|
- Profile: batch-lite
|
||||||
|
- Source Discovery: dcy-002 (REQ-010 은 데몬 설정 편의 개선)
|
||||||
|
- Included REQ: REQ-010
|
||||||
|
- Excluded REQ: 없음
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
- REQ-010 단일 저위험 Configuration 요구사항을 minor-change fast path 로 처리한다. 데몬 기동 시 `.env` 를 로드(실 OS env 우선)하고, `.env` 는 gitignore, `.env.example` 제공.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
- In Scope:
|
||||||
|
- python-dotenv 의존성 추가, `DaemonConfig.from_env()` 에 `.env` best-effort 로드.
|
||||||
|
- `.gitignore` 에 `.env` 추가, `.env.example` 신규.
|
||||||
|
- 설정 로드/우선순위/부재 회귀 단위테스트.
|
||||||
|
- Out of Scope:
|
||||||
|
- 다른 설정 방식(TOML/CLI/시크릿 매니저) — 본 변경과 무관.
|
||||||
|
|
||||||
|
## Documents
|
||||||
|
- Planning: [planning.md](./planning.md)
|
||||||
|
- Design: none (batch-lite, no architecture impact)
|
||||||
|
- Implementation: [implementation.md](./implementation.md)
|
||||||
|
- Verification: [verification.md](./verification.md)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- minor-change: 구조/인터페이스/런타임 흐름 변경 없음(기동 시 env 소스 추가뿐). design 불요.
|
||||||
|
- dotenv 미설치/.env 부재 시 graceful no-op → 회귀 없음.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# BAT-003 Planning
|
||||||
|
|
||||||
|
- Status: confirmed
|
||||||
|
- Batch: BAT-003
|
||||||
|
- Profile: batch-lite
|
||||||
|
|
||||||
|
## Included REQ
|
||||||
|
- REQ-010 (Configuration): 데몬 설정 .env 파일 로딩
|
||||||
|
|
||||||
|
## Delivery Plan
|
||||||
|
1. pyproject.toml 에 `python-dotenv` 의존성 추가.
|
||||||
|
2. `trex_core/runtime/config.py` 의 `DaemonConfig.from_env()` 시작부에서 best-effort `load_dotenv()`(미설치/부재 시 no-op, OS env override 안 함).
|
||||||
|
3. `.gitignore` 에 `.env` 추가, `.env.example` 신규(키 목록 참고용).
|
||||||
|
4. `tests/runtime/test_config_dotenv.py`: .env 로드 / OS env 우선 / .env 부재 회귀.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- Internal: REQ-005(DaemonConfig) 위 확장.
|
||||||
|
- External: python-dotenv.
|
||||||
|
|
||||||
|
## Design Gate
|
||||||
|
- Design Required Before Implementation: no
|
||||||
|
- Reason: 구조/인터페이스/런타임 흐름 변경 없음. 기동 시 환경변수 소스(.env)만 추가하는 국소 설정 변경이라 batch-lite + design 생략(minor-change).
|
||||||
|
|
||||||
|
## Milestones
|
||||||
|
1. M1 — config 에 .env 로딩 + deps + gitignore/.env.example.
|
||||||
|
2. M2 — 단위테스트 green(로드/우선순위/부재).
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
- R-1 (Low): .env 가 실 OS env 를 덮어쓰면 운영 사고. 대응: `load_dotenv(override=False)` 기본(실 env 우선)으로 AC-2 검증.
|
||||||
|
- R-2 (Low): dotenv 미설치 환경 회귀. 대응: best-effort import(no-op) + AC-3 회귀 테스트.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# BAT-003 Verification
|
||||||
|
|
||||||
|
- Status: confirmed
|
||||||
|
- Batch: BAT-003
|
||||||
|
|
||||||
|
## Acceptance Mapping
|
||||||
|
- REQ-010:
|
||||||
|
- AC1 .env 로드 → `test_loads_values_from_dotenv` + 스모크(dsn/total/port). **PASS**
|
||||||
|
- AC2 OS env 우선 → `test_os_env_takes_precedence_over_dotenv`(override=False). **PASS**
|
||||||
|
- AC3 .env 부재 회귀 → `test_no_dotenv_uses_os_env_only` + `test_missing_dsn_raises`. **PASS**
|
||||||
|
- AC4 gitignore/.env.example → `git check-ignore`: `.env` ignored, `.env.example` trackable. **PASS**
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
- Test: `pytest tests/runtime/test_config_dotenv.py` 4 passed; 전체 78 passed; ruff clean.
|
||||||
|
- Manual: `git check-ignore .env` → ignored, `.env.example` → trackable. `./.env` 단독 로드 스모크.
|
||||||
|
|
||||||
|
## no architecture impact confirmed
|
||||||
|
- batch-lite, planning Design Gate=no. 구조/인터페이스/런타임 흐름 변경 없음(기동 시 env 소스 추가뿐). baseline 문서 영향 없음 → 동기화 evidence 불요.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
- Ready for Release: true
|
||||||
|
- Blocking Issues:
|
||||||
|
- 없음.
|
||||||
@@ -10,3 +10,4 @@
|
|||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| BAT-001 | standard | release-candidate | REQ-001, REQ-002, REQ-003, REQ-004 | dcy-001 | [bat-001](./bat-001_20260603_trex-core-slot-domain-seed/) |
|
| BAT-001 | standard | release-candidate | REQ-001, REQ-002, REQ-003, REQ-004 | dcy-001 | [bat-001](./bat-001_20260603_trex-core-slot-domain-seed/) |
|
||||||
| BAT-002 | standard | release-candidate | REQ-005, REQ-006, REQ-007, REQ-008, REQ-009 | dcy-002 | [bat-002](./bat-002_20260603_slot-core-daemon-http-api/) |
|
| BAT-002 | standard | release-candidate | REQ-005, REQ-006, REQ-007, REQ-008, REQ-009 | dcy-002 | [bat-002](./bat-002_20260603_slot-core-daemon-http-api/) |
|
||||||
|
| BAT-003 | batch-lite | release-candidate | REQ-010 | dcy-002 | [bat-003](./bat-003_20260604_daemon-dotenv-config/) |
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
# req-010: 데몬 설정 .env 파일 로딩
|
||||||
|
|
||||||
|
- Status: Implemented
|
||||||
|
- Type: Configuration
|
||||||
|
- Priority: Medium
|
||||||
|
- Owner: project maintainers
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
- 운영자가 데몬 환경변수(`TREX_POSTGRESQL_DSN` 등)를 `.env` 파일로 관리할 수 있도록, 데몬 기동 시 `.env` 를 로드한다. 실제 OS 환경변수는 `.env` 보다 우선한다(12-factor 호환).
|
||||||
|
|
||||||
|
## Requirement
|
||||||
|
- `DaemonConfig.from_env()` 가 `os.environ` 을 읽기 전에 `.env`(있으면)를 로드한다. python-dotenv 를 의존성에 추가하고, `.env` 는 `.gitignore` 에 등록해 커밋되지 않게 하며, `.env.example` 를 참고용으로 제공한다. `.env` 가 없거나 dotenv 미설치여도 기존 동작(os.environ 단독)을 유지한다(회귀 없음).
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
1. Given CWD 에 `.env`(`TREX_POSTGRESQL_DSN=...`)가 있고 해당 OS env 가 미설정, When `DaemonConfig.from_env()`, Then `.env` 값이 로드되어 `dsn` 에 반영된다.
|
||||||
|
2. Given OS env 와 `.env` 에 같은 키가 다른 값으로 존재, When `from_env()`, Then OS env 값이 우선한다(.env 가 덮어쓰지 않음).
|
||||||
|
3. Given `.env` 가 없음, When `from_env()`, Then 기존처럼 `os.environ` 만으로 동작한다(회귀 없음).
|
||||||
|
4. Given 저장소, When `.gitignore` 확인, Then `.env` 가 무시 대상이며 `.env.example` 은 추적된다.
|
||||||
|
|
||||||
|
## Impacted Area
|
||||||
|
- Docs: 없음(baseline 구조/인터페이스/런타임 흐름 변경 없음 — minor-change)
|
||||||
|
- Modules: pyproject.toml(python-dotenv), trex_core/runtime/config.py(load_dotenv), .gitignore, .env.example(신규)
|
||||||
|
- Tests: tests/runtime/test_config_dotenv.py (로드/우선순위/부재 회귀)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- minor-change fast path: 단일 저위험 Configuration REQ, 구조/인터페이스/런타임 흐름 영향 없음(기동 시 env 소스 추가뿐). batch-lite 로 처리, design 불요.
|
||||||
|
- dotenv 로드는 best-effort(미설치 시 graceful no-op) — 도메인/테스트 경로에 영향 없음.
|
||||||
|
- 맥락: dcy-002 데몬(REQ-005)의 설정 편의 개선. trex-orbit 도 `.env` 사용.
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
### CHG-20260604-01
|
||||||
|
- Date: 2026-06-04
|
||||||
|
- Author: Justin Ko
|
||||||
|
|
||||||
|
#### Change Summary
|
||||||
|
- 최초 생성(Proposed) — 데몬 `.env` 로딩 Configuration 요구사항.
|
||||||
|
|
||||||
|
#### Intent
|
||||||
|
- 환경변수의 파일 기반 관리(.env) 지원을 backlog 로 정규화.
|
||||||
|
|
||||||
|
#### Acceptance Criteria Delta
|
||||||
|
- Added:
|
||||||
|
- .env 로드, OS env 우선, 부재 회귀, gitignore 4개 기준
|
||||||
|
- Updated:
|
||||||
|
- 없음
|
||||||
|
- Removed:
|
||||||
|
- 없음
|
||||||
|
|
||||||
|
#### Impacted Area Delta
|
||||||
|
- Docs:
|
||||||
|
- 없음
|
||||||
|
- Modules:
|
||||||
|
- pyproject.toml, runtime/config.py, .gitignore, .env.example
|
||||||
|
- Tests:
|
||||||
|
- tests/runtime/test_config_dotenv.py
|
||||||
|
|
||||||
|
#### Delivery Trace Delta
|
||||||
|
- Batches:
|
||||||
|
- none
|
||||||
|
- Releases:
|
||||||
|
- none
|
||||||
|
|
||||||
|
#### Revalidation Impact
|
||||||
|
- Reverification Needed: no
|
||||||
|
- Existing Implementation Invalidated: no
|
||||||
|
- Status Recommendation: keep-current
|
||||||
|
- Follow-up Action: minor-change batch-lite 편성
|
||||||
|
|
||||||
|
#### Validation Plan
|
||||||
|
- 설정 로드/우선순위/부재 회귀 단위테스트로 검증.
|
||||||
|
|
||||||
|
### CHG-20260604-02
|
||||||
|
- Date: 2026-06-04
|
||||||
|
- Author: Justin Ko
|
||||||
|
|
||||||
|
#### Change Summary
|
||||||
|
- Status Proposed → Approved (minor-change 저위험 단일 REQ, 구조/인터페이스/런타임 흐름 영향 없음).
|
||||||
|
|
||||||
|
#### Intent
|
||||||
|
- 승인 게이트 충족(필수 필드·구체 AC·blocker 없음). batch-lite 로 즉시 delivery 가능.
|
||||||
|
|
||||||
|
#### Acceptance Criteria Delta
|
||||||
|
- Added:
|
||||||
|
- 없음
|
||||||
|
- Updated:
|
||||||
|
- 없음
|
||||||
|
- Removed:
|
||||||
|
- 없음
|
||||||
|
|
||||||
|
#### Impacted Area Delta
|
||||||
|
- Docs:
|
||||||
|
- docs/srs/index.md (Register Approved)
|
||||||
|
- Modules:
|
||||||
|
- 없음
|
||||||
|
- Tests:
|
||||||
|
- 없음
|
||||||
|
|
||||||
|
#### Delivery Trace Delta
|
||||||
|
- Batches:
|
||||||
|
- none (batch-lite 편성 예정)
|
||||||
|
- Releases:
|
||||||
|
- none
|
||||||
|
|
||||||
|
#### Revalidation Impact
|
||||||
|
- Reverification Needed: no
|
||||||
|
- Existing Implementation Invalidated: no
|
||||||
|
- Status Recommendation: keep-current
|
||||||
|
- Follow-up Action: draft-batch 로 batch-lite 생성
|
||||||
|
|
||||||
|
#### Validation Plan
|
||||||
|
- batch-lite verification 에서 AC 검증.
|
||||||
|
|
||||||
|
### CHG-20260604-03
|
||||||
|
- Date: 2026-06-04
|
||||||
|
- Author: Justin Ko
|
||||||
|
|
||||||
|
#### Change Summary
|
||||||
|
- Status Approved → Implemented (confirm-req-implemented, 근거 batch BAT-003 release-candidate).
|
||||||
|
|
||||||
|
#### Intent
|
||||||
|
- BAT-003 verification.md 의 AC↔evidence(로드/우선순위/부재 회귀/gitignore, 78 pass)로 모든 AC 충족 → 구현 완료.
|
||||||
|
|
||||||
|
#### Acceptance Criteria Delta
|
||||||
|
- Added:
|
||||||
|
- 없음
|
||||||
|
- Updated:
|
||||||
|
- 없음
|
||||||
|
- Removed:
|
||||||
|
- 없음
|
||||||
|
|
||||||
|
#### Impacted Area Delta
|
||||||
|
- Docs:
|
||||||
|
- docs/srs/index.md (Register Implemented)
|
||||||
|
- Modules:
|
||||||
|
- 없음
|
||||||
|
- Tests:
|
||||||
|
- 없음
|
||||||
|
|
||||||
|
#### Delivery Trace Delta
|
||||||
|
- Batches:
|
||||||
|
- BAT-003 (release-candidate)
|
||||||
|
- Releases:
|
||||||
|
- none
|
||||||
|
|
||||||
|
#### Revalidation Impact
|
||||||
|
- Reverification Needed: no
|
||||||
|
- Existing Implementation Invalidated: no
|
||||||
|
- Status Recommendation: keep-current
|
||||||
|
- Follow-up Action: draft-release 로 release 편성(BAT-001~003)
|
||||||
|
|
||||||
|
#### Validation Plan
|
||||||
|
- BAT-003 verification evidence 재참조로 충분.
|
||||||
+4
-3
@@ -74,7 +74,7 @@
|
|||||||
|
|
||||||
## Next Requirement ID
|
## Next Requirement ID
|
||||||
|
|
||||||
- Current: `REQ-010`
|
- Current: `REQ-011`
|
||||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||||
|
|
||||||
## Requirement Register
|
## Requirement Register
|
||||||
@@ -90,11 +90,12 @@
|
|||||||
| REQ-007 | intent 제출 HTTP API + 멱등 | Interface | Implemented | High | project maintainers | [req-007](./Interface/req-007_intent-submission-api.md) |
|
| REQ-007 | intent 제출 HTTP API + 멱등 | Interface | Implemented | High | project maintainers | [req-007](./Interface/req-007_intent-submission-api.md) |
|
||||||
| REQ-008 | 슬롯 read-only 조회 HTTP API | Interface | Implemented | High | project maintainers | [req-008](./Interface/req-008_slot-readonly-query-api.md) |
|
| REQ-008 | 슬롯 read-only 조회 HTTP API | Interface | Implemented | High | project maintainers | [req-008](./Interface/req-008_slot-readonly-query-api.md) |
|
||||||
| REQ-009 | 데몬/API/영속화 baseline 문서 정합 | Documentation | Implemented | Medium | project maintainers | [req-009](./Documentation/req-009_daemon-api-baseline-alignment.md) |
|
| REQ-009 | 데몬/API/영속화 baseline 문서 정합 | Documentation | Implemented | Medium | project maintainers | [req-009](./Documentation/req-009_daemon-api-baseline-alignment.md) |
|
||||||
|
| REQ-010 | 데몬 설정 .env 파일 로딩 | Configuration | Implemented | Medium | project maintainers | [req-010](./Configuration/req-010_daemon-dotenv-loading.md) |
|
||||||
|
|
||||||
## Recent Change Log Summary
|
## Recent Change Log Summary
|
||||||
|
|
||||||
|
- 2026-06-04 :: REQ-010 Approved → Implemented — BAT-003 verification evidence 기반. (confirm-req-implemented)
|
||||||
|
- 2026-06-04 :: REQ-010 생성 + Approved — 데몬 .env 로딩 (minor-change). (draft-req/confirm-req)
|
||||||
- 2026-06-03 :: REQ-005~009 Approved → Implemented — BAT-002 verification evidence 기반. (confirm-req-implemented)
|
- 2026-06-03 :: REQ-005~009 Approved → Implemented — BAT-002 verification evidence 기반. (confirm-req-implemented)
|
||||||
- 2026-06-03 :: REQ-005~009 Proposed → Approved — 승인 게이트 통과. (confirm-req)
|
- 2026-06-03 :: REQ-005~009 Proposed → Approved — 승인 게이트 통과. (confirm-req)
|
||||||
- 2026-06-03 :: REQ-005~009 최초 생성(Proposed) — dcy-002 FR-1~FR-6 분해. (draft-req)
|
|
||||||
- 2026-06-03 :: REQ-001~004 Approved → Implemented — BAT-001 verification evidence 기반. (confirm-req-implemented)
|
- 2026-06-03 :: REQ-001~004 Approved → Implemented — BAT-001 verification evidence 기반. (confirm-req-implemented)
|
||||||
- 2026-06-03 :: REQ-001~004 Proposed → Approved — 승인 게이트 통과. (confirm-req)
|
|
||||||
@@ -7,6 +7,7 @@ dependencies = [
|
|||||||
"fastapi>=0.110",
|
"fastapi>=0.110",
|
||||||
"uvicorn>=0.29",
|
"uvicorn>=0.29",
|
||||||
"psycopg[binary]>=3.1",
|
"psycopg[binary]>=3.1",
|
||||||
|
"python-dotenv>=1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
"""DaemonConfig .env loading tests (REQ-010)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from trex_core.runtime.config import DaemonConfig
|
||||||
|
|
||||||
|
_KEYS = ("TREX_POSTGRESQL_DSN", "TREX_TOTAL_SLOTS", "TREX_API_HOST", "TREX_API_PORT")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _clean_env(monkeypatch):
|
||||||
|
for k in _KEYS:
|
||||||
|
monkeypatch.delenv(k, raising=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_loads_values_from_dotenv(tmp_path, monkeypatch):
|
||||||
|
"""AC-1: .env in CWD is loaded when the OS env var is unset."""
|
||||||
|
(tmp_path / ".env").write_text(
|
||||||
|
"TREX_POSTGRESQL_DSN=dbname=from_dotenv\nTREX_TOTAL_SLOTS=7\n"
|
||||||
|
)
|
||||||
|
monkeypatch.chdir(tmp_path)
|
||||||
|
cfg = DaemonConfig.from_env()
|
||||||
|
assert cfg.dsn == "dbname=from_dotenv"
|
||||||
|
assert cfg.total_slots == 7
|
||||||
|
|
||||||
|
|
||||||
|
def test_os_env_takes_precedence_over_dotenv(tmp_path, monkeypatch):
|
||||||
|
"""AC-2: real OS env wins over .env (override=False)."""
|
||||||
|
(tmp_path / ".env").write_text("TREX_POSTGRESQL_DSN=dbname=from_dotenv\n")
|
||||||
|
monkeypatch.chdir(tmp_path)
|
||||||
|
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "dbname=from_os_env")
|
||||||
|
cfg = DaemonConfig.from_env()
|
||||||
|
assert cfg.dsn == "dbname=from_os_env"
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_dotenv_uses_os_env_only(tmp_path, monkeypatch):
|
||||||
|
"""AC-3: with no .env, behaviour is unchanged (os.environ only)."""
|
||||||
|
monkeypatch.chdir(tmp_path) # empty dir, no .env
|
||||||
|
monkeypatch.setenv("TREX_POSTGRESQL_DSN", "dbname=os_only")
|
||||||
|
cfg = DaemonConfig.from_env()
|
||||||
|
assert cfg.dsn == "dbname=os_only"
|
||||||
|
assert cfg.total_slots == 10 # default
|
||||||
|
|
||||||
|
|
||||||
|
def test_missing_dsn_raises(tmp_path, monkeypatch):
|
||||||
|
"""No .env and no env var -> required DSN error (regression guard)."""
|
||||||
|
monkeypatch.chdir(tmp_path)
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
DaemonConfig.from_env()
|
||||||
@@ -1,10 +1,24 @@
|
|||||||
"""Daemon configuration resolution (REQ-005)."""
|
"""Daemon configuration resolution (REQ-005, REQ-010)."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from dataclasses import dataclass
|
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)
|
@dataclass(frozen=True)
|
||||||
class DaemonConfig:
|
class DaemonConfig:
|
||||||
dsn: str
|
dsn: str
|
||||||
@@ -14,6 +28,7 @@ class DaemonConfig:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_env() -> "DaemonConfig":
|
def from_env() -> "DaemonConfig":
|
||||||
|
_load_dotenv()
|
||||||
dsn = os.environ.get("TREX_POSTGRESQL_DSN")
|
dsn = os.environ.get("TREX_POSTGRESQL_DSN")
|
||||||
if not dsn:
|
if not dsn:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
|
|||||||
Reference in New Issue
Block a user