Skip to content

fix(build): use Git Bash wrapper for A2UI bundling on Windows#44211

Open
aniruddhaadak80 wants to merge 2 commits intoopenclaw:mainfrom
aniruddhaadak80:fix/windows-a2ui-bundle-shell
Open

fix(build): use Git Bash wrapper for A2UI bundling on Windows#44211
aniruddhaadak80 wants to merge 2 commits intoopenclaw:mainfrom
aniruddhaadak80:fix/windows-a2ui-bundle-shell

Conversation

@aniruddhaadak80
Copy link
Copy Markdown

Summary

  • Problem: pnpm canvas:a2ui:bundle depended on bash resolving correctly on Windows, which often hits the System32 WSL stub or no executable at all.
  • Why it matters: native Windows builds fail before TypeScript compilation, and that breaks both contributor workflows and the build path used by build / build:strict-smoke.
  • What changed: replaced the package script entry point with a Node wrapper that locates Git Bash on Windows and then runs the existing bundle-a2ui.sh through a real bash executable.
  • What did NOT change (scope boundary): the actual bundling logic in bundle-a2ui.sh is unchanged.

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

  • Native Windows users can run the A2UI bundle step without depending on bash resolving to the WSL stub or on sh.exe being present on PATH.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No, existing script is still invoked; only the launcher changed)
  • Data access scope changed? (No)

Repro + Verification

Environment

  • OS: Windows
  • Runtime/container: local Node/pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Run pnpm canvas:a2ui:bundle on native Windows.
  2. Before the patch, the build depended on direct shell resolution and could fail before the bundle script ran.
  3. Apply the Node wrapper and rerun both the bundle step and the strict smoke build.

Expected

  • The A2UI bundle step should succeed on native Windows when Git Bash is installed in a standard location.

Actual

  • pnpm canvas:a2ui:bundle and pnpm build:strict-smoke both complete successfully with the wrapper in place.

Evidence

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

Human Verification (required)

  • Verified scenarios: ran pnpm canvas:a2ui:bundle and pnpm build:strict-smoke on Windows.
  • What I did not verify: Git Bash installed in non-standard locations beyond the documented environment variables and common Program Files paths.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (Optional only: OPENCLAW_GIT_BASH if Git Bash is installed in a custom location)
  • Migration needed? (No)

Failure Recovery (if this breaks)

  • Revert package.json and scripts/bundle-a2ui.mjs.
  • Known bad symptoms reviewers should watch for: wrapper failing to locate Git Bash in unusual custom installations.

Copilot AI review requested due to automatic review settings March 12, 2026 16:11
@openclaw-barnacle openclaw-barnacle bot added scripts Repository scripts size: S labels Mar 12, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves the reliability of the canvas:a2ui:bundle build step on native Windows by routing the existing bundle-a2ui.sh script through a Node launcher that attempts to locate Git Bash, avoiding Windows environments where bash resolves to the WSL stub or is missing.

Changes:

  • Added a Node wrapper (scripts/bundle-a2ui.mjs) that resolves Git Bash paths on Windows and invokes bundle-a2ui.sh.
  • Updated package.json to run the new Node wrapper for pnpm canvas:a2ui:bundle.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
scripts/bundle-a2ui.mjs New Windows-aware launcher to find Git Bash and execute the existing bundling script.
package.json Switches canvas:a2ui:bundle to use the Node launcher instead of directly calling bash.

Comment on lines +33 to +35
? resolveWindowsBash() ?? "bash"
: "bash";

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

On Windows, falling back to plain "bash" when resolveWindowsBash() returns null can still hit the System32 WSL stub (the original failure mode), so this wrapper may not actually fix the Windows build in that case. Consider either (a) treating a null resolution as a hard error with a clear message, or (b) searching PATH for bash.exe while explicitly excluding %SystemRoot%\System32\bash.exe / WSL stubs.

Suggested change
? resolveWindowsBash() ?? "bash"
: "bash";
? resolveWindowsBash()
: "bash";
if (process.platform === "win32" && !shell) {
console.error(
"A2UI bundling requires Git Bash on Windows. Unable to locate bash.exe. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
);
process.exit(1);
}

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +24
if (candidate && fs.existsSync(candidate)) {
return candidate;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

resolveWindowsBash() only checks existsSync(candidate). If OPENCLAW_GIT_BASH/GIT_BASH_PATH is set to a directory (or non-executable), existsSync will return true and spawnSync will then fail in a less obvious way. Prefer validating with statSync(candidate).isFile() (and optionally checking .exe suffix on win32) before returning it.

Suggested change
if (candidate && fs.existsSync(candidate)) {
return candidate;
if (!candidate) {
continue;
}
try {
const stat = fs.statSync(candidate);
if (!stat.isFile()) {
continue;
}
if (process.platform === "win32") {
const ext = path.extname(candidate).toLowerCase();
if (ext !== ".exe") {
continue;
}
}
return candidate;
} catch {
// Ignore invalid candidates and try the next one.

Copilot uses AI. Check for mistakes.
"A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
);
}
throw result.error;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Throwing result.error here will surface a Node stack trace in npm/pnpm output, which is noisy for a script entrypoint and inconsistent with other repo scripts that print a concise error then exit non-zero (e.g., scripts/test-force.ts). Consider printing result.error.message (plus the Git Bash hint) and calling process.exit(1) instead of throwing.

Suggested change
throw result.error;
console.error(result.error.message);
process.exit(1);

Copilot uses AI. Check for mistakes.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 12, 2026

Greptile Summary

This PR replaces the direct bash scripts/bundle-a2ui.sh invocation in package.json with a Node.js wrapper (scripts/bundle-a2ui.mjs) that resolves Git Bash on Windows before delegating to the existing shell script. The bundling logic itself is unchanged.

The wrapper is well-structured: it checks user-configurable environment variables (OPENCLAW_GIT_BASH, GIT_BASH_PATH) first, then falls back through the standard Windows Program Files locations, and ultimately falls back to "bash" (with a helpful error message if even that fails). Exit codes are correctly forwarded via process.exit(result.status ?? 1).

Minor issues found:

  • scripts/bundle-a2ui.mjs is missing a trailing newline at end of file.
  • The error handler prints a diagnostic message only on Windows; adding an equivalent message for non-Windows platforms (e.g. minimal CI containers where bash might be absent) would improve debuggability across all environments.

Confidence Score: 5/5

  • This PR is safe to merge; it is a targeted, low-risk Windows compatibility fix with no changes to the underlying bundling logic.
  • The change is narrowly scoped: one line in package.json and a new 51-line wrapper script. The wrapper correctly handles platform detection, Git Bash discovery, environment-variable overrides, error propagation, and exit code forwarding. No bundling logic was modified, and the fallback to "bash" preserves existing behavior on non-Windows and on Windows machines where Git Bash is on PATH. The only findings are two minor style suggestions.
  • No files require special attention.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: scripts/bundle-a2ui.mjs
Line: 51

Comment:
**Missing newline at end of file**

The file is missing a trailing newline, as noted by the `\ No newline at end of file` marker in the diff. Most linters and POSIX tools expect files to end with a newline.

```suggestion
process.exit(result.status ?? 1);
```

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/bundle-a2ui.mjs
Line: 42-48

Comment:
**Non-Windows error path lacks diagnostic context**

When `result.error` is set (e.g. `ENOENT`) and the platform is *not* Windows, the error is re-thrown with no accompanying message. On Linux/macOS this would be a misconfigured environment where `bash` is genuinely absent, which is unusual but possible in minimal CI containers. Adding a cross-platform fallback message makes the failure easier to diagnose without changing the throw semantics.

```suggestion
if (result.error) {
  if (process.platform === "win32") {
    console.error(
      "A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
    );
  } else {
    console.error(
      `A2UI bundling failed to spawn '${shell}': ${result.error.message}`,
    );
  }
  throw result.error;
}
```

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

Last reviewed commit: 49cf2b4

throw result.error;
}

process.exit(result.status ?? 1); No newline at end of file
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.

Missing newline at end of file

The file is missing a trailing newline, as noted by the \ No newline at end of file marker in the diff. Most linters and POSIX tools expect files to end with a newline.

Suggested change
process.exit(result.status ?? 1);
process.exit(result.status ?? 1);
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/bundle-a2ui.mjs
Line: 51

Comment:
**Missing newline at end of file**

The file is missing a trailing newline, as noted by the `\ No newline at end of file` marker in the diff. Most linters and POSIX tools expect files to end with a newline.

```suggestion
process.exit(result.status ?? 1);
```

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

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +42 to +48
if (result.error) {
if (process.platform === "win32") {
console.error(
"A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
);
}
throw result.error;
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.

Non-Windows error path lacks diagnostic context

When result.error is set (e.g. ENOENT) and the platform is not Windows, the error is re-thrown with no accompanying message. On Linux/macOS this would be a misconfigured environment where bash is genuinely absent, which is unusual but possible in minimal CI containers. Adding a cross-platform fallback message makes the failure easier to diagnose without changing the throw semantics.

Suggested change
if (result.error) {
if (process.platform === "win32") {
console.error(
"A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
);
}
throw result.error;
if (result.error) {
if (process.platform === "win32") {
console.error(
"A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
);
} else {
console.error(
`A2UI bundling failed to spawn '${shell}': ${result.error.message}`,
);
}
throw result.error;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/bundle-a2ui.mjs
Line: 42-48

Comment:
**Non-Windows error path lacks diagnostic context**

When `result.error` is set (e.g. `ENOENT`) and the platform is *not* Windows, the error is re-thrown with no accompanying message. On Linux/macOS this would be a misconfigured environment where `bash` is genuinely absent, which is unusual but possible in minimal CI containers. Adding a cross-platform fallback message makes the failure easier to diagnose without changing the throw semantics.

```suggestion
if (result.error) {
  if (process.platform === "win32") {
    console.error(
      "A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
    );
  } else {
    console.error(
      `A2UI bundling failed to spawn '${shell}': ${result.error.message}`,
    );
  }
  throw result.error;
}
```

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

@katoue

This comment was marked as spam.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

import { fileURLToPath } from "node:url";

const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const scriptPath = path.join(rootDir, "scripts", "bundle-a2ui.sh");
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

scriptPath is built with path.join, which will use backslashes on Windows. Git Bash/MSYS tools are generally happier with forward slashes (and the previous package.json invocation used scripts/bundle-a2ui.sh with /). Consider normalizing scriptPath to use / (e.g., replace \\ with /) before passing it to bash to avoid path-translation edge cases.

Suggested change
const scriptPath = path.join(rootDir, "scripts", "bundle-a2ui.sh");
const scriptPath = path.join(rootDir, "scripts", "bundle-a2ui.sh").replace(/\\/g, "/");

Copilot uses AI. Check for mistakes.
process.env["ProgramFiles(x86)"] &&
path.join(process.env["ProgramFiles(x86)"], "Git", "bin", "bash.exe"),
process.env["ProgramFiles(x86)"] &&
path.join(process.env["ProgramFiles(x86)"], "Git", "usr", "bin", "bash.exe"),
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The Windows Git Bash lookup misses the common per-user installation location (%LOCALAPPDATA%\\Programs\\Git\\bin\\bash.exe / ...\\usr\\bin\\bash.exe). Without this, many “standard” Git for Windows installs done as “Just for me” won’t be detected and you’ll hit the fallback bash resolution again.

Suggested change
path.join(process.env["ProgramFiles(x86)"], "Git", "usr", "bin", "bash.exe"),
path.join(process.env["ProgramFiles(x86)"], "Git", "usr", "bin", "bash.exe"),
process.env.LOCALAPPDATA &&
path.join(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe"),
process.env.LOCALAPPDATA &&
path.join(process.env.LOCALAPPDATA, "Programs", "Git", "usr", "bin", "bash.exe"),

Copilot uses AI. Check for mistakes.
if (result.error) {
if (process.platform === "win32") {
console.error(
"A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The error text only mentions OPENCLAW_GIT_BASH, but the resolver also accepts GIT_BASH_PATH. To reduce confusion when users try to follow the guidance, consider mentioning both supported env vars (or standardizing on one) in this message.

Suggested change
"A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH to bash.exe if Git is installed in a non-standard location.",
"A2UI bundling requires Git Bash on Windows. Set OPENCLAW_GIT_BASH or GIT_BASH_PATH to the path to bash.exe if Git is installed in a non-standard location.",

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +38
const shell = process.platform === "win32" ? (resolveWindowsBash() ?? "bash") : "bash";

const result = spawnSync(shell, [scriptPath], {
cwd: rootDir,
stdio: "inherit",
env: process.env,
});
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

On Windows, if resolveWindowsBash() returns null you fall back to invoking bash anyway. If that resolves to the System32 WSL stub, the process will typically exit non-zero without setting result.error, so this wrapper won’t emit the helpful Git Bash guidance and the original failure mode remains. Consider failing fast when no Git Bash candidate is found, or (at least) printing the guidance when result.status !== 0 and the wrapper used the fallback bash.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scripts Repository scripts size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants