From db1c360a86df4b7344bf8a69324b5ce36ca5c3f4 Mon Sep 17 00:00:00 2001 From: vaibhavsrv Date: Sat, 19 Sep 2026 14:36:21 +0530 Subject: [PATCH] fix(gpu): respect dotenv override precedence and export prefix in env reader --- .github/workflows/test-linux.yml | 1 + ods/extensions/services/dashboard-api/gpu.py | 19 ++++-- ods/tests/test_gpu_env_precedence.py | 62 ++++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 ods/tests/test_gpu_env_precedence.py diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 65a2a7a515..c27172dc67 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -402,6 +402,7 @@ jobs: bash tests/test-preflight-resolver-bug.sh bash tests/test-desktop-agent-host-env.sh bash tests/test-pixel-uninstall.sh + python3 tests/test_gpu_env_precedence.py python3 tests/contracts/test-network-exposure-contracts.py python3 tests/contracts/test-remote-provider-egress-policy.py python3 tests/contracts/test-remote-provider-egress-service.py diff --git a/ods/extensions/services/dashboard-api/gpu.py b/ods/extensions/services/dashboard-api/gpu.py index 22da2b12d4..514ed714e5 100644 --- a/ods/extensions/services/dashboard-api/gpu.py +++ b/ods/extensions/services/dashboard-api/gpu.py @@ -549,13 +549,22 @@ def _read_env_var_from_file_state(key: str) -> tuple[bool, str]: """Read a live .env value while preserving present-but-empty assignments.""" install_dir = os.environ.get("ODS_INSTALL_DIR", os.path.expanduser("~/ods")) env_path = Path(install_dir) / ".env" + found = False + result = "" + prefix = f"{key}=" + export_prefix = f"export {key}=" try: - for line in env_path.read_text().splitlines(): - if line.startswith(f"{key}="): - return True, parse_env_value(line[len(key) + 1:]) - except OSError: + for raw in env_path.read_text(encoding="utf-8", errors="replace").splitlines(): + line = raw.strip() + if line.startswith(prefix): + found = True + result = parse_env_value(line[len(prefix):]) + elif line.startswith(export_prefix): + found = True + result = parse_env_value(line[len(export_prefix):]) + except (OSError, UnicodeError): pass - return False, "" + return found, result def _read_env_var_from_file(key: str) -> str: diff --git a/ods/tests/test_gpu_env_precedence.py b/ods/tests/test_gpu_env_precedence.py new file mode 100644 index 0000000000..b31af7d777 --- /dev/null +++ b/ods/tests/test_gpu_env_precedence.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Regression test: gpu module env reader respects dotenv override precedence and export prefix.""" + +from __future__ import annotations + +import os +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "extensions" / "services" / "dashboard-api")) + +import gpu # noqa: E402 + + +def test_gpu_read_env_var_precedence_and_export(): + with tempfile.TemporaryDirectory() as tmpdir: + tmp_path = Path(tmpdir) + env_file = tmp_path / ".env" + + # Baseline: initial assignment overridden by later assignment; second var uses export prefix + env_file.write_text( + """ +# Default configuration +GPU_BACKEND=cpu +# Custom override appended by installer or operator +GPU_BACKEND=nvidia +export GPU_ACCELERATOR=cuda +UNTOUCHED_VAR=kept +# Non-ASCII UTF-8 comment: 🚀 +""", + encoding="utf-8", + ) + + old_dir = os.environ.get("ODS_INSTALL_DIR") + os.environ["ODS_INSTALL_DIR"] = str(tmp_path) + try: + # 1. Precedence: final assignment must override initial assignment + found, backend = gpu._read_env_var_from_file_state("GPU_BACKEND") + assert found is True + assert backend == "nvidia", f"Expected 'nvidia', got {backend!r}" + + # 2. Export prefix: must parse key assigned with export prefix + found_acc, acc = gpu._read_env_var_from_file_state("GPU_ACCELERATOR") + assert found_acc is True + assert acc == "cuda", f"Expected 'cuda', got {acc!r}" + + # 3. Missing key falls back cleanly to found=False + found_missing, missing = gpu._read_env_var_from_file_state("NON_EXISTENT") + assert found_missing is False + assert missing == "" + finally: + if old_dir is None: + os.environ.pop("ODS_INSTALL_DIR", None) + else: + os.environ["ODS_INSTALL_DIR"] = old_dir + + +if __name__ == "__main__": + test_gpu_read_env_var_precedence_and_export() + print("test_gpu_env_precedence: OK")