Merge pull request #2873 from Charanis/codex/webui-launcher-env-pr
[1/7] Preserve WebUI launcher environment
This commit is contained in:
commit
d42253bca3
4 changed files with 32 additions and 3 deletions
12
bootstrap.py
12
bootstrap.py
|
|
@ -28,8 +28,8 @@ def _load_repo_dotenv() -> None:
|
||||||
``python3 bootstrap.py`` directly behaves identically to ``./start.sh``.
|
``python3 bootstrap.py`` directly behaves identically to ``./start.sh``.
|
||||||
Variables are set unconditionally (matching shell source semantics), so a
|
Variables are set unconditionally (matching shell source semantics), so a
|
||||||
value in .env overrides one already present in the shell environment.
|
value in .env overrides one already present in the shell environment.
|
||||||
To keep a CLI-supplied value, unset it from .env or launch via start.sh
|
``ctl.sh`` sets HERMES_WEBUI_PRESERVE_ENV=1 when it has already resolved
|
||||||
and override there.
|
launcher-specific values such as HERMES_HOME or HERMES_WEBUI_STATE_DIR.
|
||||||
|
|
||||||
Only loads the webui repo .env — not ~/.hermes/.env, which the server
|
Only loads the webui repo .env — not ~/.hermes/.env, which the server
|
||||||
loads independently at startup for provider credentials.
|
loads independently at startup for provider credentials.
|
||||||
|
|
@ -41,6 +41,12 @@ def _load_repo_dotenv() -> None:
|
||||||
if not env_path.exists():
|
if not env_path.exists():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
|
preserve_existing = os.getenv("HERMES_WEBUI_PRESERVE_ENV", "").strip().lower() in {
|
||||||
|
"1",
|
||||||
|
"true",
|
||||||
|
"yes",
|
||||||
|
"on",
|
||||||
|
}
|
||||||
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
|
||||||
line = raw_line.strip()
|
line = raw_line.strip()
|
||||||
if not line or line.startswith("#") or "=" not in line:
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
|
@ -52,6 +58,8 @@ def _load_repo_dotenv() -> None:
|
||||||
k = k[7:].strip()
|
k = k[7:].strip()
|
||||||
v = v.strip().strip('"').strip("'")
|
v = v.strip().strip('"').strip("'")
|
||||||
if k:
|
if k:
|
||||||
|
if preserve_existing and k in os.environ:
|
||||||
|
continue
|
||||||
os.environ[k] = v
|
os.environ[k] = v
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
|
|
|
||||||
4
ctl.sh
4
ctl.sh
|
|
@ -219,7 +219,9 @@ start_cmd() {
|
||||||
: >> "${LOG_FILE}"
|
: >> "${LOG_FILE}"
|
||||||
(
|
(
|
||||||
cd "${REPO_ROOT}"
|
cd "${REPO_ROOT}"
|
||||||
exec "${python_exe}" "${REPO_ROOT}/bootstrap.py" --no-browser --foreground --host "${CTL_HOST}" "${CTL_PORT}" ${CTL_BOOTSTRAP_ARGS[@]+"${CTL_BOOTSTRAP_ARGS[@]}"}
|
trap '' HUP
|
||||||
|
export HERMES_WEBUI_PRESERVE_ENV=1
|
||||||
|
exec nohup "${python_exe}" "${REPO_ROOT}/bootstrap.py" --no-browser --foreground --host "${CTL_HOST}" "${CTL_PORT}" ${CTL_BOOTSTRAP_ARGS[@]+"${CTL_BOOTSTRAP_ARGS[@]}"}
|
||||||
) >> "${LOG_FILE}" 2>&1 &
|
) >> "${LOG_FILE}" 2>&1 &
|
||||||
pid=$!
|
pid=$!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,18 @@ class TestLoadRepoDotenv:
|
||||||
self._run(tmp_path, "HERMES_WEBUI_HOST=0.0.0.0\n")
|
self._run(tmp_path, "HERMES_WEBUI_HOST=0.0.0.0\n")
|
||||||
assert os.environ.get("HERMES_WEBUI_HOST") == "0.0.0.0"
|
assert os.environ.get("HERMES_WEBUI_HOST") == "0.0.0.0"
|
||||||
|
|
||||||
|
def test_preserve_existing_env_keeps_ctl_overrides(self, tmp_path):
|
||||||
|
"""ctl.sh can ask bootstrap.py to keep wrapper-provided env values."""
|
||||||
|
os.environ["HERMES_WEBUI_PRESERVE_ENV"] = "1"
|
||||||
|
os.environ["HERMES_HOME"] = "/runtime/hermesOne"
|
||||||
|
os.environ["HERMES_WEBUI_PASSWORD"] = ""
|
||||||
|
self._run(
|
||||||
|
tmp_path,
|
||||||
|
"HERMES_HOME=/repo/default\nHERMES_WEBUI_PASSWORD=repo-password\n",
|
||||||
|
)
|
||||||
|
assert os.environ.get("HERMES_HOME") == "/runtime/hermesOne"
|
||||||
|
assert os.environ.get("HERMES_WEBUI_PASSWORD") == ""
|
||||||
|
|
||||||
def test_does_not_set_empty_values(self, tmp_path):
|
def test_does_not_set_empty_values(self, tmp_path):
|
||||||
"""A key whose value is empty after stripping is not set to a non-empty string."""
|
"""A key whose value is empty after stripping is not set to a non-empty string."""
|
||||||
os.environ.pop("HERMES_EMPTY_KEY", None)
|
os.environ.pop("HERMES_EMPTY_KEY", None)
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,13 @@ def test_start_writes_pid_under_hermes_home_runs_foreground_no_browser_and_logs(
|
||||||
assert not pid_file.exists()
|
assert not pid_file.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_uses_nohup_so_daemon_survives_launcher_exit():
|
||||||
|
ctl_text = CTL.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "trap '' HUP" in ctl_text
|
||||||
|
assert 'exec nohup "${python_exe}"' in ctl_text
|
||||||
|
|
||||||
|
|
||||||
def test_start_loads_dotenv_but_inline_overrides_win(tmp_path):
|
def test_start_loads_dotenv_but_inline_overrides_win(tmp_path):
|
||||||
repo_root = tmp_path / "repo"
|
repo_root = tmp_path / "repo"
|
||||||
repo_root.mkdir()
|
repo_root.mkdir()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue