Skip to content

fix(build): prefer usable POSIX shells for Windows bundling#45860

Open
henrytien wants to merge 1 commit intoopenclaw:mainfrom
henrytien:fix/windows-bash-bundling
Open

fix(build): prefer usable POSIX shells for Windows bundling#45860
henrytien wants to merge 1 commit intoopenclaw:mainfrom
henrytien:fix/windows-bash-bundling

Conversation

@henrytien
Copy link
Copy Markdown

Summary

  • Problem: pnpm build on Windows could fail during canvas:a2ui:bundle because bare bash could resolve to the WSL shim (C:\Windows\System32\bash.exe) instead of a usable POSIX shell.
  • Why it matters: source builds fail before the actual TypeScript build starts, even on machines that already have a usable shell such as MSYS2 or Git Bash.
  • What changed: route the A2UI bundle step through a small Node wrapper that prefers usable Windows POSIX shells and forwards the active node / roaming pnpm paths.
  • What did NOT change (scope boundary): no runtime auth/model behavior, no gateway protocol changes, and no changes to the A2UI bundle contents themselves.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Windows source builds now avoid resolving canvas:a2ui:bundle through the WSL bash.exe shim when a usable MSYS2/Git Bash shell is available.
  • No config or runtime defaults changed.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: Windows 11 / Windows 10 shell environment
  • Runtime/container: local source checkout
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. On Windows, use a source checkout where bash resolves to the WSL shim before a usable POSIX shell.
  2. Run pnpm build.
  3. Observe the build fail in canvas:a2ui:bundle before the TypeScript build starts.

Expected

  • pnpm build should use a usable POSIX shell on Windows and complete successfully when MSYS2/Git Bash is installed.

Actual

  • pnpm build could invoke the WSL shim and fail before bundling/build completion.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • Ran pnpm vitest run test/run-bash.test.ts
    • Ran pnpm build
    • Confirmed the bundle step used the wrapper and the build completed successfully on Windows
  • Edge cases checked:
    • preferred shell candidates win over PATH scanning
    • Windows shim/Cygwin-style paths are skipped during shell resolution
    • wrapper PATH includes roaming pnpm and active node locations
  • What you did not verify:
    • did not test on Linux/macOS
    • did not verify behavior on machines without any usable POSIX shell installed

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly:
    • revert this PR
    • or temporarily restore the previous canvas:a2ui:bundle script in package.json
  • Files/config to restore:
    • package.json
    • scripts/run-bash.mjs
    • test/run-bash.test.ts
  • Known bad symptoms reviewers should watch for:
    • wrapper fails to find a usable POSIX shell on Windows
    • unexpected shell selection differences on uncommon Windows setups

Risks and Mitigations

  • Risk:
    • shell selection could prefer a different Windows POSIX shell than a contributor expects
    • Mitigation:
      • prefer explicit MSYS2 / Git Bash candidates first
      • add tests for shell selection and PATH construction

@openclaw-barnacle openclaw-barnacle bot added scripts Repository scripts size: M labels Mar 14, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 12a48f61c5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +84 to +87
candidates.push(path.join(entry, "sh.exe"));
candidates.push(path.join(entry, "bash.exe"));
candidates.push(path.join(entry, "sh"));
candidates.push(path.join(entry, "bash"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prefer bash over sh when resolving POSIX shell

The new shell resolver adds sh candidates before bash, so after package.json routes canvas:a2ui:bundle through this wrapper, common Linux/macOS environments pick /bin/sh first. That breaks scripts/bundle-a2ui.sh because it relies on Bash-specific behavior (set -o pipefail, arrays, ${BASH_SOURCE[0]}), causing pnpm build to fail before the TypeScript build step whenever /bin/sh is not Bash (e.g., dash).

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 14, 2026

Greptile Summary

This PR fixes Windows pnpm build failures in the canvas:a2ui:bundle step by replacing the bare bash invocation with a small Node.js wrapper (scripts/run-bash.mjs) that proactively searches well-known MSYS2 and Git Bash locations before falling back to PATH scanning, while explicitly skipping the WSL shim (System32), WindowsApps, and Cygwin directories.

Key observations:

  • The shell-selection logic (preferredWindowsShells, resolvePosixShell) is well-structured and the test coverage directly validates the two most critical behaviors (preferred-candidate ordering and shim-directory filtering).
  • Error message inconsistency: line 97 tells the user to "Install Git Bash, MSYS2, or Cygwin and retry", but Cygwin PATH entries (e.g. C:\cygwin64\bin) are explicitly skipped by the \cygwin substring filter. A user who follows that advice and has only Cygwin will hit the same error again. The message should be updated to remove Cygwin.
  • The result.status ?? 1 fallback is logically correct (0 on success, 1 for signal-killed subprocesses), but a brief log of result.signal when present would improve build-failure diagnostics.
  • There are no tests for non-Windows behavior; the author acknowledged this and the Linux/macOS code path is a trivial PATH scan, so the risk is low.

Confidence Score: 3/5

  • Safe to merge after fixing the misleading Cygwin mention in the error message; no runtime, auth, or protocol changes.
  • The approach is sound and the fix is appropriately scoped. The one actionable bug — the error message recommending Cygwin when Cygwin paths are filtered — would actively mislead Windows users with only Cygwin installed. Everything else is a minor improvement suggestion. Score reflects a small but real user-visible error in guidance text.
  • scripts/run-bash.mjs — specifically the error message on line 97 and the missing signal-name diagnostic on line 122.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: scripts/run-bash.mjs
Line: 96-98

Comment:
**Error message recommends Cygwin but Cygwin paths are filtered**

The error message on line 97 tells users to "Install Git Bash, MSYS2, or Cygwin" but `resolvePosixShell` explicitly skips any PATH entry whose normalized form contains `\cygwin` (line 78). A typical Cygwin 64-bit installation adds `C:\cygwin64\bin` to PATH, which contains `\cygwin` as a substring and is therefore silently skipped. A user who follows this advice and installs Cygwin will get the exact same error again.

The message should only recommend shells that will actually be found:

```suggestion
  console.error("Unable to find a usable POSIX shell on PATH.");
  console.error("Install Git Bash or MSYS2 and retry.");
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: scripts/run-bash.mjs
Line: 122

Comment:
**Default exit code 1 on signal-killed subprocess**

`result.status ?? 1` correctly returns 0 when the subprocess exits normally and 1 when it is killed by a signal (where `result.status` is `null`). However, `result.signal` carries the signal name in that case (e.g. `"SIGTERM"`), and the caller receives no indication of what happened. Consider logging the signal name before returning so build output is easier to diagnose:

```suggestion
  if (result.signal) {
    console.error(`Shell process killed by signal: ${result.signal}`);
  }
  return result.status ?? 1;
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 12a48f6

Comment on lines +96 to +98
console.error("Unable to find a usable POSIX shell on PATH.");
console.error("Install Git Bash, MSYS2, or Cygwin and retry.");
return 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message recommends Cygwin but Cygwin paths are filtered

The error message on line 97 tells users to "Install Git Bash, MSYS2, or Cygwin" but resolvePosixShell explicitly skips any PATH entry whose normalized form contains \cygwin (line 78). A typical Cygwin 64-bit installation adds C:\cygwin64\bin to PATH, which contains \cygwin as a substring and is therefore silently skipped. A user who follows this advice and installs Cygwin will get the exact same error again.

The message should only recommend shells that will actually be found:

Suggested change
console.error("Unable to find a usable POSIX shell on PATH.");
console.error("Install Git Bash, MSYS2, or Cygwin and retry.");
return 1;
console.error("Unable to find a usable POSIX shell on PATH.");
console.error("Install Git Bash or MSYS2 and retry.");
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/run-bash.mjs
Line: 96-98

Comment:
**Error message recommends Cygwin but Cygwin paths are filtered**

The error message on line 97 tells users to "Install Git Bash, MSYS2, or Cygwin" but `resolvePosixShell` explicitly skips any PATH entry whose normalized form contains `\cygwin` (line 78). A typical Cygwin 64-bit installation adds `C:\cygwin64\bin` to PATH, which contains `\cygwin` as a substring and is therefore silently skipped. A user who follows this advice and installs Cygwin will get the exact same error again.

The message should only recommend shells that will actually be found:

```suggestion
  console.error("Unable to find a usable POSIX shell on PATH.");
  console.error("Install Git Bash or MSYS2 and retry.");
```

How can I resolve this? If you propose a fix, please make it concise.

return 1;
}

return result.status ?? 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default exit code 1 on signal-killed subprocess

result.status ?? 1 correctly returns 0 when the subprocess exits normally and 1 when it is killed by a signal (where result.status is null). However, result.signal carries the signal name in that case (e.g. "SIGTERM"), and the caller receives no indication of what happened. Consider logging the signal name before returning so build output is easier to diagnose:

Suggested change
return result.status ?? 1;
if (result.signal) {
console.error(`Shell process killed by signal: ${result.signal}`);
}
return result.status ?? 1;
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/run-bash.mjs
Line: 122

Comment:
**Default exit code 1 on signal-killed subprocess**

`result.status ?? 1` correctly returns 0 when the subprocess exits normally and 1 when it is killed by a signal (where `result.status` is `null`). However, `result.signal` carries the signal name in that case (e.g. `"SIGTERM"`), and the caller receives no indication of what happened. Consider logging the signal name before returning so build output is easier to diagnose:

```suggestion
  if (result.signal) {
    console.error(`Shell process killed by signal: ${result.signal}`);
  }
  return result.status ?? 1;
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scripts Repository scripts size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant