|
14 | 14 | def _has_working_bash() -> bool: |
15 | 15 | """Check whether a functional native bash is available. |
16 | 16 |
|
| 17 | + On Windows, ``subprocess.run(["bash", ...])`` uses CreateProcess, |
| 18 | + which searches System32 *before* PATH — so it may find the WSL |
| 19 | + launcher even when Git-for-Windows bash appears first in PATH via |
| 20 | + ``shutil.which``. We therefore probe with bare ``"bash"`` (the |
| 21 | + same way test helpers invoke it) to get an accurate result. |
| 22 | +
|
17 | 23 | On Windows, only Git-for-Windows bash (MSYS2/MINGW) is accepted. |
18 | | - The WSL launcher (System32\\bash.exe) is rejected because it runs in |
19 | | - a separate Linux filesystem and cannot handle native Windows paths |
20 | | - used by the test fixtures. |
| 24 | + The WSL launcher is rejected because it runs in a separate Linux |
| 25 | + filesystem and cannot handle native Windows paths used by the |
| 26 | + test fixtures. |
21 | 27 |
|
22 | 28 | Set SPECKIT_TEST_BASH=1 to force-enable bash tests regardless. |
23 | 29 | """ |
24 | 30 | if os.environ.get("SPECKIT_TEST_BASH") == "1": |
25 | 31 | return True |
26 | | - bash_path = shutil.which("bash") |
27 | | - if bash_path is None: |
| 32 | + if shutil.which("bash") is None: |
28 | 33 | return False |
29 | | - # On Windows, reject the WSL launcher early (avoids WSL init prompts |
30 | | - # and the 5 s timeout) and only accept MSYS/MINGW/CYGWIN bash. |
31 | | - if sys.platform == "win32": |
32 | | - if "system32" in bash_path.lower(): |
| 34 | + # Probe with bare "bash" — same as the test helpers — so that |
| 35 | + # Windows CreateProcess resolution order is respected. |
| 36 | + try: |
| 37 | + r = subprocess.run( |
| 38 | + ["bash", "-c", "echo ok"], |
| 39 | + capture_output=True, text=True, timeout=5, |
| 40 | + ) |
| 41 | + if r.returncode != 0 or "ok" not in r.stdout: |
33 | 42 | return False |
| 43 | + except (OSError, subprocess.TimeoutExpired): |
| 44 | + return False |
| 45 | + # On Windows, verify we have MSYS/MINGW bash (Git for Windows), |
| 46 | + # not the WSL launcher which can't handle native paths. |
| 47 | + if sys.platform == "win32": |
34 | 48 | try: |
35 | 49 | u = subprocess.run( |
36 | | - [bash_path, "-c", "uname -s"], |
| 50 | + ["bash", "-c", "uname -s"], |
37 | 51 | capture_output=True, text=True, timeout=5, |
38 | 52 | ) |
39 | 53 | kernel = u.stdout.strip().upper() |
40 | 54 | if not any(k in kernel for k in ("MSYS", "MINGW", "CYGWIN")): |
41 | 55 | return False |
42 | 56 | except (OSError, subprocess.TimeoutExpired): |
43 | 57 | return False |
44 | | - try: |
45 | | - r = subprocess.run( |
46 | | - [bash_path, "-c", "echo ok"], |
47 | | - capture_output=True, text=True, timeout=5, |
48 | | - ) |
49 | | - if r.returncode != 0 or "ok" not in r.stdout: |
50 | | - return False |
51 | | - except (OSError, subprocess.TimeoutExpired): |
52 | | - return False |
53 | 58 | return True |
54 | 59 |
|
55 | 60 |
|
|
0 commit comments