diff --git a/CHANGELOG.md b/CHANGELOG.md index b70a5b5..935aa66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ## 0.1.5 — 2026-09-07 +- **`${VAR}` inside comment lines is not a reference.** The renderer's `.env.template` header literally says + "substitutes every ${VAR}", which the refusal check counted as an unresolved variable + (`ENV_INCOMPLETE: unresolved: VAR`) — the first bundle on env-qa-02 was refused for it. + - **config: `tenancy.port` is the service's intercept port (443), not the in-pod 8081.** With 8081 the SDK found no intercept and the dial failed (`service not available`, then a bare `TypeError` from the SDK's fallback). `install.sh` now writes 443; `config.example.yaml` updated. diff --git a/monky_deployd/bundle.py b/monky_deployd/bundle.py index 45000b5..ce8d08b 100644 --- a/monky_deployd/bundle.py +++ b/monky_deployd/bundle.py @@ -144,12 +144,18 @@ def parse(data: bytes, *, max_bytes: int = 4 * 1024 * 1024) -> Bundle: # --- refusal checks (pure; names only, never values) ----------------------------------------- +def _code_lines(text: str) -> str: + """Drop comment lines: a `# … ${VAR} …` remark in .env.template (the renderer writes one) + is not a reference. Compose/dotenv comments start with `#` after optional whitespace.""" + return "\n".join(ln for ln in text.splitlines() if not ln.lstrip().startswith("#")) + + def referenced_vars(text: str) -> set[str]: - return {m.group(1) for m in _VAR_RE.finditer(text)} + return {m.group(1) for m in _VAR_RE.finditer(_code_lines(text))} def defaulted_vars(text: str) -> set[str]: - return {m.group(1) for m in _VAR_DEFAULTED_RE.finditer(text)} + return {m.group(1) for m in _VAR_DEFAULTED_RE.finditer(_code_lines(text))} def unresolved_vars(bundle: Bundle, provided: set[str]) -> list[str]: diff --git a/tests/test_bundle.py b/tests/test_bundle.py index 6591c3c..e4871e8 100644 --- a/tests/test_bundle.py +++ b/tests/test_bundle.py @@ -79,3 +79,16 @@ def test_disk_need_bytes_spellings(): assert b.parse(tar_bytes(make_files(meta={"agent": {"disk_need_bytes": 5}}))).disk_need_bytes == 5 assert b.parse(tar_bytes(make_files(meta={"disk": {"need_bytes": 7}}))).disk_need_bytes == 7 assert b.parse(tar_bytes(make_files())).disk_need_bytes == 0 + + +def test_placeholders_in_comment_lines_are_not_references(): + """The renderer's .env.template header says '... substitutes every ${VAR} ...' — that must not + become an unresolved 'VAR' (env-qa-02 pilot: ENV_INCOMPLETE: unresolved: VAR).""" + from monky_deployd.bundle import defaulted_vars, referenced_vars + + text = ( + "# The on-box agent substitutes every ${VAR} from OpenBao per secrets.manifest.json.\n" + " # ${ALSO_COMMENT}\nGEMINI_API_KEY=${GEMINI_API_KEY}\nPG=${PGPASSWORD:-x}\n" + ) + assert referenced_vars(text) == {"GEMINI_API_KEY", "PGPASSWORD"} + assert defaulted_vars(text) == {"PGPASSWORD"}