diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e5effda..84ecc186 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,10 +85,16 @@ jobs: run: | pkg_dir="$(mktemp -d)" install_dir="$(mktemp -d)" + node_dir="$(dirname "$(command -v node)")" npm pack --pack-destination "$pkg_dir" >/dev/null pkg="$(find "$pkg_dir" -maxdepth 1 -name 'hunkdiff-*.tgz' | head -n1)" npm install -g --prefix "$install_dir" "$pkg" - PATH="$install_dir/bin:$PATH" hunk --help | grep 'Usage: hunk' + PATH="$install_dir/bin:$node_dir:/usr/bin:/bin" + if command -v bun >/dev/null 2>&1; then + echo "bun unexpectedly available on the sanitized PATH" >&2 + exit 1 + fi + hunk --help | grep 'Usage: hunk' build-bin: name: Build compiled binary diff --git a/README.md b/README.md index 541870d5..2de5c065 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,10 @@ Hunk is a terminal diff viewer for reviewing agent-authored changesets with a de npm i -g hunkdiff ``` -For now, the published `hunk` executable still expects [Bun](https://bun.sh) 1.3.10+ to be available on your `PATH` at runtime. +## Requirements + +- Node.js 18+ +- Git for `hunk diff`, `hunk show`, `hunk stash show`, and pager integration ## Quick start diff --git a/bin/hunk.cjs b/bin/hunk.cjs new file mode 100755 index 00000000..79f5eaed --- /dev/null +++ b/bin/hunk.cjs @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const { spawnSync } = require("node:child_process"); +const path = require("node:path"); + +const entrypoint = path.join(__dirname, "..", "dist", "npm", "main.js"); + +let bunBinary; + +try { + bunBinary = require.resolve("bun/bin/bun.exe"); +} catch (error) { + console.error( + "Failed to resolve the bundled Bun runtime. Try reinstalling hunkdiff.", + ); + if (error && error.message) { + console.error(error.message); + } + process.exit(1); +} + +const result = spawnSync(bunBinary, [entrypoint, ...process.argv.slice(2)], { + stdio: "inherit", + env: process.env, +}); + +if (result.error) { + console.error(result.error.message); + process.exit(1); +} + +process.exit(typeof result.status === "number" ? result.status : 1); diff --git a/bun.lock b/bun.lock index ea52d618..98ba5759 100644 --- a/bun.lock +++ b/bun.lock @@ -8,6 +8,7 @@ "@opentui/core": "^0.1.88", "@opentui/react": "^0.1.88", "@pierre/diffs": "^1.1.0", + "bun": "^1.3.10", "commander": "^14.0.3", "diff": "^8.0.3", "parse-diff": "^0.11.1", @@ -95,6 +96,30 @@ "@opentui/react": ["@opentui/react@0.1.88", "", { "dependencies": { "@opentui/core": "0.1.88", "react-reconciler": "^0.32.0" }, "peerDependencies": { "react": ">=19.0.0", "react-devtools-core": "^7.0.1", "ws": "^8.18.0" } }, "sha512-EfEUBlGgCx9LqpbB852L+m7F19NcR8yeUww+C8NytIQeVKJbFIhSLu3r3wjBEGNoYiRk1DtAwWBpG5YaVvkXXw=="], + "@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/8IzqSu4/OWGRs7Fs2ROzGVwJMFTBQkgAp6sAthkBYoN7OiM4rY/CpPVs2X9w9N1W61CHSkEdNKi8HrLZKfK3g=="], + + "@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-TT7eUihnAzxM2tlZesusuC75PAOYKvUBgVU/Nm/lakZ/DpyuqhNkzUfcxSgmmK9IjVWzMmezLIGZl16XGCGJng=="], + + "@oven/bun-darwin-x64-baseline": ["@oven/bun-darwin-x64-baseline@1.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-CYjIHWaQG7T4phfjErHr6BiXRs0K/9DqMeiohJmuYSBF+H2m56vFslOenLCguGYQL9jeiiCZBeoVCpwjxZrMgQ=="], + + "@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-8XMLyRNxHF4jfLajkWt+F8UDxsWbzysyxQVMZKUXwoeGvaxB0rVd07r3YbgDtG8U6khhRFM3oaGp+CQ0whwmdA=="], + + "@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-jBwYCLG5Eb+PqtFrc3Wp2WMYlw1Id75gUcsdP+ApCOpf5oQhHxkFWCjZmcDoioDmEhMWAiM3wtwSrTlPg+sI6Q=="], + + "@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-z3GFCk1UBzDOOiEBHL32lVP7Edi26BhOjKb6bIc0nRyabbRiyON4++GR0zmd/H5zM5S0+UcXFgCGnD+b8avTLw=="], + + "@oven/bun-linux-x64-baseline": ["@oven/bun-linux-x64-baseline@1.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-KZlf1jKtf4jai8xiQv/0XRjxVVhHnw/HtUKtLdOeQpTOQ1fQFhLoz2FGGtVRd0LVa/yiRbSz9HlWIzWlmJClng=="], + + "@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-ADImD4yCHNpqZu718E2chWcCaAHvua90yhmpzzV6fF4zOhwkGGbPCgUWmKyJ83uz+DXaPdYxX0ttDvtolrzx3Q=="], + + "@oven/bun-linux-x64-musl-baseline": ["@oven/bun-linux-x64-musl-baseline@1.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-J+qz4Al05PrNIOdj7xsWVTyx0c/gjUauG5nKV3Rrx0Q+5JO+1pPVlnfNmWbOF9pKG4f3IGad8KXJUfGMORld+Q=="], + + "@oven/bun-windows-aarch64": ["@oven/bun-windows-aarch64@1.3.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UOdkwScHRkGPz+n9ZJU7sTkTvqV7rD1SLCLaru1xH8WRsV7tDorPqNCzEN1msOIiPRK825nvAtEm9UsomO1GsA=="], + + "@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-E51tyWDP1l0CbjZYhiUxhDGPaY8Hf5YBREx0PHBff1LM1/q3qsJ6ZvRUa8YbbOO0Ax9QP6GHjD9vf3n6bXZ7QA=="], + + "@oven/bun-windows-x64-baseline": ["@oven/bun-windows-x64-baseline@1.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-cCsXK9AQ9Zf18QlVnbrFu2IKfr4sf2sfbErkF2jfCzyCO9Bnhl0KRx63zlN+Ni1xU7gcBLAssgcui5R400N2eA=="], + "@pierre/diffs": ["@pierre/diffs@1.1.0", "", { "dependencies": { "@pierre/theme": "0.0.22", "@shikijs/transformers": "^3.0.0", "diff": "8.0.3", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-wbxrzcmanJuHZb81iir09j42uU9AnKxXDtAuEQJbAnti5f2UfYdCQYejawuHZStFrlsMacCZLh/dDHmqvAaQCw=="], "@pierre/theme": ["@pierre/theme@0.0.22", "", {}, "sha512-ePUIdQRNGjrveELTU7fY89Xa7YGHHEy5Po5jQy/18lm32eRn96+tnYJEtFooGdffrx55KBUtOXfvVy/7LDFFhA=="], @@ -145,6 +170,8 @@ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "bun": ["bun@1.3.11", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.3.11", "@oven/bun-darwin-x64": "1.3.11", "@oven/bun-darwin-x64-baseline": "1.3.11", "@oven/bun-linux-aarch64": "1.3.11", "@oven/bun-linux-aarch64-musl": "1.3.11", "@oven/bun-linux-x64": "1.3.11", "@oven/bun-linux-x64-baseline": "1.3.11", "@oven/bun-linux-x64-musl": "1.3.11", "@oven/bun-linux-x64-musl-baseline": "1.3.11", "@oven/bun-windows-aarch64": "1.3.11", "@oven/bun-windows-x64": "1.3.11", "@oven/bun-windows-x64-baseline": "1.3.11" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-AvXWYFO6j/ZQ7bhGm4X6eilq2JHsDVC90ZM32k2B7/srhC2gs3Sdki1QTbwrdRCo8o7eT+167vcB1yzOvPdbjA=="], + "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="], "bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="], diff --git a/package.json b/package.json index f108430b..ec8a77df 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,10 @@ "type": "module", "packageManager": "bun@1.3.10", "bin": { - "hunk": "dist/npm/main.js" + "hunk": "./bin/hunk.cjs" }, "files": [ + "bin", "dist/npm", "README.md", "LICENSE" @@ -44,7 +45,7 @@ "url": "https://github.com/modem-dev/hunk/issues" }, "engines": { - "bun": ">=1.3.10" + "node": ">=18" }, "publishConfig": { "access": "public" @@ -58,6 +59,7 @@ "@opentui/core": "^0.1.88", "@opentui/react": "^0.1.88", "@pierre/diffs": "^1.1.0", + "bun": "^1.3.10", "commander": "^14.0.3", "diff": "^8.0.3", "parse-diff": "^0.11.1", diff --git a/scripts/check-pack.ts b/scripts/check-pack.ts index 8317091d..d8b64b2b 100644 --- a/scripts/check-pack.ts +++ b/scripts/check-pack.ts @@ -43,7 +43,7 @@ if (!pack) { } const publishedPaths = new Set(pack.files.map((file) => file.path)); -const requiredPaths = ["dist/npm/main.js", "README.md", "LICENSE", "package.json"]; +const requiredPaths = ["bin/hunk.cjs", "dist/npm/main.js", "README.md", "LICENSE", "package.json"]; for (const path of requiredPaths) { if (!publishedPaths.has(path)) {