test(support-bundle): run the launcher dotenv tests on Windows hosts (#6544)

`_local_launcher_home()` was the last caller of `shutil.which("bash")` left
behind by #5404, so on Windows it started the WSL launcher instead of Git Bash
and all 30 tests built on that helper failed while decoding the stub's UTF-16
output. Route it through `require_script_bash()` like the other shell-script
tests.

Git Bash also answers with MSYS paths (Windows TEMP is mounted at `/tmp`), so
the home printed by the launcher could not be used from Python. Convert with
`cygpath -w` inside the shell, where values that look like shell syntax stay
intact, and keep `printf` for hosts that ship no `cygpath`.

The unquoted `~/...` case additionally needs `USERPROFILE`: Git Bash expands
`~` from `HOME`, while Python's `expanduser()` reads `USERPROFILE` on Windows.

Windows: `tests/test_support_bundle.py` goes from 30 failed / 59 passed to
89 passed. POSIX hosts keep their previous behavior.

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
Jason Cat 2026-10-09 17:33:18 +08:00 committed by GitHub
parent cee0d93b60
commit 68b65885f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -4,7 +4,6 @@ from __future__ import annotations
import json
import os
import shutil
import subprocess
import sys
import zipfile
@ -12,6 +11,7 @@ from pathlib import Path
import pytest
import support_bundle
from support.shell import require_script_bash
@pytest.fixture(autouse=True)
@ -955,9 +955,7 @@ def test_thread_summary_honors_project_root_override(tmp_path, monkeypatch, sour
def _local_launcher_home(project):
"""Execute the launcher's actual dotenv/default blocks without starting services."""
bash = shutil.which("bash")
if bash is None:
pytest.skip("Bash is required to exercise the local launcher")
bash = require_script_bash()
serve = (Path(support_bundle.__file__).parent / "serve.sh").read_text(encoding="utf-8")
dotenv_block = serve.split("# ── Load .env", 1)[1].split("\n\n", 1)[1].split("_pick_python()", 1)[0]
defaults = serve[serve.index("# Runtime path defaults.") :].split("# Extra flags", 1)[0]
@ -965,7 +963,10 @@ def _local_launcher_home(project):
[
bash,
"-c",
'set -e\ncd "$REPO_ROOT"\n' + dotenv_block + defaults + '\nprintf "%s" "$DEER_FLOW_HOME"',
# Git Bash answers with POSIX paths, and its mount table (for example Windows TEMP
# at /tmp) is not reproducible from Python, so convert inside the shell. Passing the
# value through argv instead would let MSYS rewrite shell syntax inside it.
'set -e\ncd "$REPO_ROOT"\n' + dotenv_block + defaults + '\nif command -v cygpath >/dev/null 2>&1; then cygpath -w "$DEER_FLOW_HOME"; else printf "%s" "$DEER_FLOW_HOME"; fi',
],
cwd=project,
env={**os.environ, "REPO_ROOT": str(project)},
@ -974,7 +975,7 @@ def _local_launcher_home(project):
encoding="utf-8",
check=True,
)
return Path(result.stdout)
return Path(result.stdout.strip())
@pytest.mark.parametrize("invocation_dir", [".", "backend"])
@ -1009,6 +1010,10 @@ def test_dotenv_tilde_matches_launcher_quote_semantics(tmp_path, monkeypatch, va
user_home = tmp_path / "user-home"
user_home.mkdir()
monkeypatch.setenv("HOME", str(user_home))
if os.name == "nt":
# Git Bash expands "~" from HOME, but Python's expanduser() reads USERPROFILE
# on Windows; pin both so the two resolution paths stay comparable.
monkeypatch.setenv("USERPROFILE", str(user_home))
(project / ".env").write_text(f"{variable}={value}\n", encoding="utf-8")
if variable == "DEER_FLOW_HOME":
home = _local_launcher_home(project)