Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions tests/tools/test_base_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
init_session() failure handling, and the CWD marker contract.
"""

import sys
from unittest.mock import MagicMock

from tools.environments.base import BaseEnvironment
Expand Down Expand Up @@ -194,3 +195,44 @@ def test_unique_per_instance(self):
env1 = _TestableEnv()
env2 = _TestableEnv()
assert env1._cwd_marker != env2._cwd_marker


class TestWindowsToMsysPath:
def test_noop_on_non_windows(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "linux")
assert (
BaseEnvironment._windows_to_msys_path(r"C:\Users\x")
== r"C:\Users\x"
)

def test_converts_drive_path_on_windows(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "win32")
assert (
BaseEnvironment._windows_to_msys_path(r"C:\Users\NVIDIA")
== "/c/Users/NVIDIA"
)

def test_converts_forward_slash_variant(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "win32")
assert (
BaseEnvironment._windows_to_msys_path("D:/Projects/foo")
== "/d/Projects/foo"
)

def test_noop_on_already_msys_path(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "win32")
assert (
BaseEnvironment._windows_to_msys_path("/c/Users/NVIDIA")
== "/c/Users/NVIDIA"
)

def test_noop_on_empty_string(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "win32")
assert BaseEnvironment._windows_to_msys_path("") == ""

def test_preserves_trailing_slash(self, monkeypatch):
monkeypatch.setattr(sys, "platform", "win32")
assert (
BaseEnvironment._windows_to_msys_path("C:\\Users\\NVIDIA\\")
== "/c/Users/NVIDIA/"
)
30 changes: 27 additions & 3 deletions tools/environments/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
import json
import logging
import os
import re
import select
import shlex
import subprocess
import sys
import threading
import time
import uuid
Expand Down Expand Up @@ -359,7 +361,8 @@ def init_session(self):
# Restore configured cwd after login shell profile scripts, which may
# change the working directory (e.g. bashrc `cd ~`). Without this,
# pwd -P captures the profile's directory, not terminal.cwd.
_quoted_cwd = shlex.quote(self.cwd)
_cwd_for_bash = self._windows_to_msys_path(self.cwd)
_quoted_cwd = shlex.quote(_cwd_for_bash)
Comment thread
Icather marked this conversation as resolved.
# Quote the snapshot / cwd-file paths so Git Bash on Windows handles
# ``C:/Users/...``-shaped paths without glob-splitting the colon or
# tripping on drive letters. On POSIX this is a no-op (no colons /
Expand All @@ -376,7 +379,7 @@ def init_session(self):
f"echo 'shopt -s expand_aliases' >> {_quoted_snap}\n"
f"echo 'set +e' >> {_quoted_snap}\n"
f"echo 'set +u' >> {_quoted_snap}\n"
f"builtin cd {_quoted_cwd} 2>/dev/null || true\n"
f"builtin cd -- {_quoted_cwd} 2>/dev/null || true\n"
f"pwd -P > {_quoted_cwd_file} 2>/dev/null || true\n"
f"printf '\\n{self._cwd_marker}%s{self._cwd_marker}\\n' \"$(pwd -P)\"\n"
)
Expand All @@ -403,6 +406,26 @@ def init_session(self):
# Command wrapping
# ------------------------------------------------------------------

@staticmethod
def _windows_to_msys_path(cwd: str) -> str:
"""Convert a Windows native path to Git Bash / MSYS form for ``cd``.

``C:\\Users\\x`` → ``/c/Users/x``. No-op on non-Windows hosts or
paths that are already in MSYS format.

``_msys_to_windows_path`` (in ``local.py``) handles the reverse
translation for ``os.path.isdir`` / ``subprocess.Popen(cwd=...)``.
This helper closes the gap for the bash-script side.
"""
if sys.platform != "win32" or not cwd:
return cwd
m = re.match(r'^([a-zA-Z]):[\\/](.*)$', cwd)
if not m:
return cwd
drive = m.group(1).lower()
rest = m.group(2).replace('\\', '/')
return f"/{drive}/{rest}"
Comment thread
Icather marked this conversation as resolved.

@staticmethod
def _quote_cwd_for_cd(cwd: str) -> str:
"""Quote a ``cd`` target while preserving ``~`` expansion."""
Expand Down Expand Up @@ -441,7 +464,8 @@ def _wrap_command(self, command: str, cwd: str) -> str:

# Preserve bare ``~`` expansion, but rewrite ``~/...`` through
# ``$HOME`` so suffixes with spaces remain a single shell word.
quoted_cwd = self._quote_cwd_for_cd(cwd)
_cwd_for_bash = self._windows_to_msys_path(cwd)
quoted_cwd = self._quote_cwd_for_cd(_cwd_for_bash)
# ``--`` keeps hyphen-prefixed directory names from being parsed as options.
parts.append(f"builtin cd -- {quoted_cwd} || exit 126")

Expand Down