feat(config): config 디렉터리 기반 설정 관리 (--config-path/--env-file, trex-orb-v1 정합, BAT-026)

REQ-048/010 change-req. `--config` 를 이름으로 해석 + `--config-path <dir>`
(기본 ./config) + resolve_config_toml(슬래시 포함 값은 하위호환 명시 경로).
`.env` 를 config-dir 결정적 해석(--env-file 우선, 없으면 <config_dir>/.env,
CWD 무관) + config/.env.example + .gitignore config/* 허용목록. 운영자 CLI 도
--config-path/--env-file 동일 적용. from_env() legacy 루트 .env 폴백 유지로
완전 하위호환.

단위 23 + 전체 375 passed, ruff clean, 라이브 스모크(name·path 동일).
REQ-048/010 → Implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 23:45:08 +09:00
co-authored by Claude Opus 4.8
parent 6e8e79660c
commit 8b2c26c04c
14 changed files with 531 additions and 32 deletions
+22 -3
View File
@@ -30,7 +30,12 @@ from zoneinfo import ZoneInfo
from trex_core.adapters.messaging import topology
from trex_core.adapters.portfolio.pause_store import PostgreSQLPauseStore
from trex_core.runtime.config import DaemonConfig, DaemonInstanceConfig
from trex_core.runtime.config import (
DaemonConfig,
DaemonInstanceConfig,
_load_dotenv,
resolve_config_toml,
)
_DEFAULT_TIMEOUT = 10.0
@@ -42,7 +47,8 @@ def _resolve_instance(args) -> str:
if getattr(args, "instance", None):
return args.instance
if getattr(args, "config", None):
return DaemonInstanceConfig.from_toml(args.config).instance_key
toml = resolve_config_toml(args.config, getattr(args, "config_path", None))
return DaemonInstanceConfig.from_toml(toml).instance_key
return topology.DEFAULT_INSTANCE_KEY
@@ -248,8 +254,12 @@ def _fail(message: str, as_json: bool) -> None:
# arg parsing
# --------------------------------------------------------------------------- #
def _add_common(p) -> None:
p.add_argument("--config", help="per-instance TOML (canonical instance routing)")
p.add_argument("--config", help="per-instance TOML name (resolved within --config-path) or path")
p.add_argument("--config-path", dest="config_path", default=None,
help="config directory (default ./config) for --config + .env")
p.add_argument("--instance", help="instance key (shortcut; overrides --config)")
p.add_argument("--env-file", dest="env_file", default=None,
help="explicit .env path; overrides <config-dir>/.env")
p.add_argument("--json", action="store_true", help="machine-readable JSON output")
p.add_argument("--timeout", type=float, default=_DEFAULT_TIMEOUT)
@@ -313,6 +323,15 @@ def main(argv: list[str] | None = None, *, rpc=None, confirm=None) -> int:
correlation_id, timeout) -> dict`, `confirm(prompt) -> bool`.
"""
args = _build_parser().parse_args(argv)
# REQ-010 CHG-20260609-01: load the config-dir .env (or --env-file) before
# reading env. from_env() also runs the legacy root-.env fallback; the
# config-dir load wins (loaded first, override=False).
_toml = (
resolve_config_toml(args.config, getattr(args, "config_path", None))
if getattr(args, "config", None)
else None
)
_load_dotenv(_toml, getattr(args, "env_file", None))
cfg = DaemonConfig.from_env()
confirm = confirm or _default_confirm