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.
This commit is contained in:
ethernet
2026-09-23 19:47:12 -04:00
parent cde48023ee
commit 7dee4cc068
2 changed files with 42 additions and 1 deletions

View File

@@ -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

View File

@@ -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"