From 7dee4cc068bbfbdb68b3769376808d84eaee4e0d Mon Sep 17 00:00:00 2001 From: ethernet Date: Wed, 23 Sep 2026 19:47:12 -0400 Subject: [PATCH] fix(activate): keep PATH, HOME and the temp dirs in POSIX form under MSYS The pm env is read by a native Windows Python, which the MSYS/Cygwin runtime hands PATH, HOME, TMPDIR, TMP and TEMP in Windows form. activate exported them verbatim, so bash split 'C:\a;C:\b' on ':' and found no commands: scripts/run_tests.sh died with 'env: command not found' right after activation. Convert them back with cygpath, resolved before the export replaces PATH. --- activate | 17 ++++++++++++++++- tests/pm/test_activate_scripts.py | 26 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/activate b/activate index bb00431057..ce5a7938b4 100755 --- a/activate +++ b/activate @@ -105,6 +105,9 @@ for _hermes_k in $_hermes_keys; do eval "__HERMES_PRIOR_$_hermes_k=\${$_hermes_k-__HERMES_UNSET__}" done +# Resolved before the export below replaces PATH. +_hermes_cygpath="$(command -v cygpath 2>/dev/null || :)" + eval "$(printf '%s' "$_hermes_json" | "$_hermes_py" -c ' import json, sys, shlex, re for k, v in json.load(sys.stdin).items(): @@ -114,6 +117,18 @@ for k, v in json.load(sys.stdin).items(): continue print("export %s=%s" % (k, shlex.quote(str(v))))')" +# The MSYS/Cygwin runtime hands a native Windows Python these variables in +# Windows form (`C:\a;C:\b`) and converts them back only for its own children. +# Exported verbatim, bash splits PATH on ':' and finds no commands. +if [ -n "$_hermes_cygpath" ]; then + PATH="$("$_hermes_cygpath" -u -p "$PATH")" + for _hermes_k in HOME TMPDIR TMP TEMP; do + if [ -n "${!_hermes_k-}" ]; then + printf -v "$_hermes_k" '%s' "$("$_hermes_cygpath" -u "${!_hermes_k}")" + fi + done +fi + # This checkout, not whichever `hermes` PATH finds. A function beats PATH, an # alias, and a hashed command, including the MSIX execution alias. __HERMES_WORKTREE="$_hermes_repo" @@ -207,4 +222,4 @@ __HERMES_KEY_LIST="$_hermes_keys" unset _hermes_repo _hermes_os _hermes_arch _hermes_target _hermes_store \ _hermes_py _hermes_json _hermes_keys _hermes_k _hermes_entry _hermes_venvpy \ - _hermes_winarch _hermes_top + _hermes_winarch _hermes_top _hermes_cygpath diff --git a/tests/pm/test_activate_scripts.py b/tests/pm/test_activate_scripts.py index 647f8ebc0b..ca883996da 100644 --- a/tests/pm/test_activate_scripts.py +++ b/tests/pm/test_activate_scripts.py @@ -262,6 +262,32 @@ def test_deactivate_restores_the_prior_shell(tmp_path: Path): assert result.stdout.strip() == "restored" +@pytest.mark.platforms("windows") +def test_activate_leaves_the_shell_paths_in_posix_form(tmp_path: Path): + """The pm env is read by a native Windows Python, which sees PATH, HOME and + the temp variables in Windows form. Exported verbatim, `C:\\a;C:\\b` left + bash without a usable PATH, so every command after activation failed.""" + root = _isolated_checkout(tmp_path) + store, _ = _fake_store(tmp_path) + script = ( + 'prior_home="$HOME" prior_tmp="$TMP" && ' + f'source "{_posix(root / "activate")}" && ' + 'command -v basename >/dev/null && ' + 'case "$PATH" in *";"*|*"\\\\"*) exit 3;; esac && ' + 'test "$HOME" = "$prior_home" && test "$TMP" = "$prior_tmp" && ' + 'echo posix' + ) + result = subprocess.run( + [_bash(), "-c", script], + capture_output=True, + text=True, + cwd=_posix(tmp_path), + env=_bash_env(store), + ) + assert result.returncode == 0, result.stderr + assert result.stdout.strip() == "posix" + + def test_activate_fails_cleanly_without_a_store(tmp_path: Path): env = _bash_env(tmp_path / "empty-store") isolated = _isolated_checkout(tmp_path) / "activate"