diff --git a/package.json b/package.json index 3851a78bd7..783b27508a 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "make-pdf": "./make-pdf/dist/pdf" }, "scripts": { - "build": "bun run vendor:xterm && bun run gen:skill-docs --host all; bun build --compile browse/src/cli.ts --outfile browse/dist/browse && bun build --compile browse/src/find-browse.ts --outfile browse/dist/find-browse && bun build --compile design/src/cli.ts --outfile design/dist/design && bun build --compile make-pdf/src/cli.ts --outfile make-pdf/dist/pdf && bun build --compile bin/gstack-global-discover.ts --outfile bin/gstack-global-discover && bash browse/scripts/build-node-server.sh && ( git rev-parse HEAD 2>/dev/null || true ) > browse/dist/.version && ( git rev-parse HEAD 2>/dev/null || true ) > design/dist/.version && ( git rev-parse HEAD 2>/dev/null || true ) > make-pdf/dist/.version && chmod +x browse/dist/browse browse/dist/find-browse design/dist/design make-pdf/dist/pdf bin/gstack-global-discover && (rm -f .*.bun-build || true)", + "build": "bun run vendor:xterm && bun run gen:skill-docs --host all; bun build --compile browse/src/cli.ts --outfile browse/dist/browse && bun build --compile browse/src/find-browse.ts --outfile browse/dist/find-browse && bun build --compile design/src/cli.ts --outfile design/dist/design && bun build --compile make-pdf/src/cli.ts --outfile make-pdf/dist/pdf && bun build --compile bin/gstack-global-discover.ts --outfile bin/gstack-global-discover && bash browse/scripts/build-node-server.sh && bash scripts/stamp-versions.sh && chmod +x browse/dist/browse browse/dist/find-browse design/dist/design make-pdf/dist/pdf bin/gstack-global-discover", "vendor:xterm": "mkdir -p extension/lib && cp node_modules/xterm/lib/xterm.js extension/lib/xterm.js && cp node_modules/xterm/css/xterm.css extension/lib/xterm.css && cp node_modules/xterm-addon-fit/lib/xterm-addon-fit.js extension/lib/xterm-addon-fit.js", "dev:make-pdf": "bun run make-pdf/src/cli.ts", "dev:design": "bun run design/src/cli.ts", diff --git a/scripts/stamp-versions.sh b/scripts/stamp-versions.sh new file mode 100644 index 0000000000..8d05c57e89 --- /dev/null +++ b/scripts/stamp-versions.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Stamp git HEAD into per-binary .version files and clean up bun build +# temp artifacts. +# +# Extracted from package.json `build` script because Bun Shell on Windows +# doesn't handle `( ... ) > file` (subshell + stdout redirect) reliably +# (oven-sh/bun#11066, #11968) -- chained `&&` builds break midway, +# leaving stale binaries with no version stamp. Real bash (Git Bash, +# WSL, macOS, Linux) handles the same syntax without issue, so isolating +# it in a `.sh` file keeps the build cross-platform. + +set -e + +GSTACK_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$GSTACK_DIR" + +for dir in browse design make-pdf; do + ( git rev-parse HEAD 2>/dev/null || true ) > "$dir/dist/.version" +done + +rm -f .*.bun-build 2>/dev/null || true diff --git a/test/build-script-shell-compat.test.ts b/test/build-script-shell-compat.test.ts index ee13fb709e..7f79128444 100644 --- a/test/build-script-shell-compat.test.ts +++ b/test/build-script-shell-compat.test.ts @@ -28,13 +28,26 @@ describe('package.json build scripts — POSIX shell compat (D-1460)', () => { expect(offending).toEqual([]); }); - test('every `> path/.version` redirect is preceded by a subshell, not a brace group', () => { - // The original PR #1460 target: package.json line 12 had three of these. + test('build script does version stamping (inline subshells or via bash helper)', () => { + // PR #1460 added inline `( ... ) > .version` subshells. Later, those + // were extracted to `scripts/stamp-versions.sh` because Bun Shell on + // Windows didn't handle `( ... ) > file` reliably (oven-sh/bun#11066). + // Either form is acceptable; what matters is (a) version stamping + // happens, and (b) any inline form still uses subshell, not brace group. + // We intentionally do a loose substring match on the helper path rather + // than pin the exact invocation form (`bash xxx`, `bash ./xxx`, + // `bash -e xxx`, etc.) -- pinning the form turns this into a formatting + // contract that blocks legitimate refactors. The file-existence guard + // catches rename/move regressions. const build = PKG.scripts.build ?? ''; - const versionRedirects = [...build.matchAll(/(\([^)]*\)|\{[^}]*\})\s*>\s*\S+\/\.version/g)]; - expect(versionRedirects.length).toBeGreaterThan(0); - for (const m of versionRedirects) { + const inlineRedirects = [...build.matchAll(/(\([^)]*\)|\{[^}]*\})\s*>\s*\S+\/\.version/g)]; + const referencesHelper = build.includes('scripts/stamp-versions.sh'); + expect(inlineRedirects.length > 0 || referencesHelper).toBe(true); + for (const m of inlineRedirects) { expect(m[1].startsWith('(')).toBe(true); } + if (referencesHelper) { + expect(fs.existsSync(path.join(ROOT, 'scripts/stamp-versions.sh'))).toBe(true); + } }); });