From 3a3da3b4db5b5202c9b159a246061565524b7dce Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 16 Oct 2026 23:23:10 -0700 Subject: [PATCH] fix(bin): report unsupported Windows ARM64 fallback clearly The wrapper calls rejectUnsupportedWindowsArm64Runtime after the prebuilt-binary branch and before bundledBunRuntime. On win32 and arm64 it prints that Windows ARM64 is not supported by the current bundled runtime, that Bun disables TinyCC so OpenTUI cannot load, and the tracking reference oven-sh/bun#28055, then exits with status 1. The entrypoint test stubs that platform, supplies a resolvable fake bun.exe, and expects exit code 1, that stderr text, and that the fake runtime was not started. A hunkdiff patch changeset records the same message. Fixes #1126 --- .changeset/windows-arm64-launcher-error.md | 5 ++ packages/hunk/bin/hunk.cjs | 17 +++++++ test/cli/entrypoint.test.ts | 56 ++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 .changeset/windows-arm64-launcher-error.md diff --git a/.changeset/windows-arm64-launcher-error.md b/.changeset/windows-arm64-launcher-error.md new file mode 100644 index 000000000..a75266ded --- /dev/null +++ b/.changeset/windows-arm64-launcher-error.md @@ -0,0 +1,5 @@ +--- +"hunkdiff": patch +--- + +Report that Windows ARM64 is not supported by the current bundled runtime instead of crashing with a low-context Bun FFI error. diff --git a/packages/hunk/bin/hunk.cjs b/packages/hunk/bin/hunk.cjs index 19e943592..738cede35 100755 --- a/packages/hunk/bin/hunk.cjs +++ b/packages/hunk/bin/hunk.cjs @@ -102,6 +102,21 @@ function bundledBunRuntime() { } } +// Exits on Windows ARM64 before bundled Bun starts OpenTUI. Bun disables TinyCC there +// (oven-sh/bun#28055). Callers honor HUNK_BIN_PATH and any installed prebuilt first. +function rejectUnsupportedWindowsArm64Runtime() { + if (os.platform() !== "win32" || os.arch() !== "arm64") { + return; + } + + console.error( + "hunk: Windows ARM64 is not supported by the current bundled runtime. " + + "Bun disables TinyCC on this platform, so OpenTUI cannot load. " + + "Tracking: oven-sh/bun#28055", + ); + process.exit(1); +} + const forwardedArgs = process.argv.slice(2); if (forwardedArgs.length === 2 && forwardedArgs[0] === "skill" && forwardedArgs[1] === "path") { const skillPath = bundledSkillPath(); @@ -125,6 +140,8 @@ if (prebuiltBinary) { run(prebuiltBinary, forwardedArgs); } +rejectUnsupportedWindowsArm64Runtime(); + const bunBinary = bundledBunRuntime(); if (bunBinary) { const entrypoint = path.join(__dirname, "..", "dist", "npm", "main.js"); diff --git a/test/cli/entrypoint.test.ts b/test/cli/entrypoint.test.ts index b047284e4..d3b3b0216 100644 --- a/test/cli/entrypoint.test.ts +++ b/test/cli/entrypoint.test.ts @@ -272,6 +272,62 @@ describe("CLI entrypoint contracts", () => { } }); + test("bin wrapper reports Windows ARM64 before the bundled Bun fallback", () => { + const tempDir = mkdtempSync(join(tmpdir(), "hunk-wrapper-win-arm64-")); + const packageRoot = join(tempDir, "package"); + const tempWrapperPath = join(packageRoot, "bin", "hunk.cjs"); + const preloadPath = join(tempDir, "os-preload.cjs"); + const bunMarkerPath = join(tempDir, "bun-invoked"); + const fakeBunDir = join(packageRoot, "node_modules", "bun", "bin"); + const fakeBunPath = join(fakeBunDir, "bun.exe"); + + try { + mkdirSync(join(packageRoot, "bin"), { recursive: true }); + mkdirSync(fakeBunDir, { recursive: true }); + copyFileSync(join(process.cwd(), "packages", "hunk", "bin", "hunk.cjs"), tempWrapperPath); + writeFileSync( + join(packageRoot, "node_modules", "bun", "package.json"), + JSON.stringify({ name: "bun", version: "0.0.0" }), + ); + // Resolvable bundled runtime. Records a marker only if the wrapper actually spawns it. + writeFileSync( + fakeBunPath, + `#!/usr/bin/env node\nrequire("node:fs").writeFileSync(${JSON.stringify(bunMarkerPath)}, "invoked\\n");\n`, + { mode: 0o755 }, + ); + writeFileSync( + preloadPath, + 'const os = require("node:os");\nos.platform = () => "win32";\nos.arch = () => "arm64";\n', + ); + + const env = { ...process.env }; + delete env.HUNK_BIN_PATH; + + const proc = Bun.spawnSync(["node", "-r", preloadPath, tempWrapperPath, "diff"], { + cwd: packageRoot, + stdin: "ignore", + stdout: "pipe", + stderr: "pipe", + env, + }); + + const stdout = Buffer.from(proc.stdout).toString("utf8"); + const stderr = Buffer.from(proc.stderr).toString("utf8"); + + expect(proc.exitCode).toBe(1); + expect(stdout).toBe(""); + expect(stderr).toContain("Windows ARM64 is not supported by the current bundled runtime"); + expect(stderr).toContain("oven-sh/bun#28055"); + expect(stderr).not.toContain("bun:ffi"); + expect(stderr).not.toContain("dlopen"); + expect(stderr).not.toContain("Bun v"); + expect(stderr).not.toContain("Unsupported platform for prebuilt Hunk binaries"); + expect(existsSync(bunMarkerPath)).toBe(false); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); + test("general pager mode falls back to plain text for non-diff stdin", () => { const proc = Bun.spawnSync(["bun", "run", "packages/hunk/src/main.tsx", "pager"], { cwd: process.cwd(),