fix(setup): link lib/ beside bin/ in runtime sidecars (#2305)

Fixes #2305.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Greg Jackson 2026-08-01 19:10:14 +01:00
parent a3259400a3
commit 85f3bd7a70
2 changed files with 60 additions and 0 deletions

9
setup
View File

@ -796,6 +796,9 @@ create_codex_runtime_root() {
if [ -d "$gstack_dir/bin" ]; then
_link_or_copy "$gstack_dir/bin" "$codex_gstack/bin"
fi
if [ -d "$gstack_dir/lib" ]; then
_link_or_copy "$gstack_dir/lib" "$codex_gstack/lib"
fi
if [ -d "$gstack_dir/browse/dist" ]; then
_link_or_copy "$gstack_dir/browse/dist" "$codex_gstack/browse/dist"
fi
@ -836,6 +839,9 @@ create_factory_runtime_root() {
if [ -d "$gstack_dir/bin" ]; then
_link_or_copy "$gstack_dir/bin" "$factory_gstack/bin"
fi
if [ -d "$gstack_dir/lib" ]; then
_link_or_copy "$gstack_dir/lib" "$factory_gstack/lib"
fi
if [ -d "$gstack_dir/browse/dist" ]; then
_link_or_copy "$gstack_dir/browse/dist" "$factory_gstack/browse/dist"
fi
@ -874,6 +880,9 @@ create_opencode_runtime_root() {
if [ -d "$gstack_dir/bin" ]; then
_link_or_copy "$gstack_dir/bin" "$opencode_gstack/bin"
fi
if [ -d "$gstack_dir/lib" ]; then
_link_or_copy "$gstack_dir/lib" "$opencode_gstack/lib"
fi
if [ -d "$gstack_dir/browse/dist" ]; then
_link_or_copy "$gstack_dir/browse/dist" "$opencode_gstack/browse/dist"
fi

View File

@ -0,0 +1,51 @@
/**
* Static invariant: runtime sidecar roots (codex, factory, opencode) that link
* bin/ MUST also link lib/, because bin/ scripts import from ../lib/ via
* relative paths (e.g. gstack-learnings-log imports lib/jsonl-store.ts).
*
* Without lib/ beside bin/ in the sidecar, those imports resolve to nothing and
* the script crashes at runtime.
*
* Issue: #2305
*/
import { describe, test, expect } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
const SETUP = fs.readFileSync(path.join(import.meta.dir, '..', 'setup'), 'utf-8');
describe('setup links lib/ beside bin/ in runtime sidecars', () => {
const SIDECAR_VARS = ['codex_gstack', 'factory_gstack', 'opencode_gstack'];
for (const sidecar of SIDECAR_VARS) {
test(`${sidecar} links lib/ when bin/ is linked`, () => {
const binPattern = new RegExp(
`_link_or_copy "\\$gstack_dir/bin" "\\$${sidecar}/bin"`,
);
const libPattern = new RegExp(
`_link_or_copy "\\$gstack_dir/lib" "\\$${sidecar}/lib"`,
);
const hasBin = binPattern.test(SETUP);
const hasLib = libPattern.test(SETUP);
expect(hasBin).toBe(true);
expect(hasLib).toBe(true);
});
}
test('lib/ link uses _link_or_copy (not raw ln)', () => {
const libLines = SETUP.split('\n').filter(
(l) => l.includes('/lib"') && /\bln\s+-/.test(l),
);
expect(libLines).toEqual([]);
});
test('lib/ link is guarded by -d check', () => {
const libGuards = SETUP.split('\n').filter((l) =>
l.includes('if [ -d "$gstack_dir/lib" ]'),
);
expect(libGuards.length).toBeGreaterThanOrEqual(3);
});
});