diff --git a/make-pdf/test/e2e/combined-gate.test.ts b/make-pdf/test/e2e/combined-gate.test.ts index 562d46e49..fcaff4265 100644 --- a/make-pdf/test/e2e/combined-gate.test.ts +++ b/make-pdf/test/e2e/combined-gate.test.ts @@ -50,6 +50,7 @@ describe("combined-features copy-paste gate", () => { try { execFileSync(PDF_BIN, ["generate", FIXTURE, outputPdf, "--quiet"], { encoding: "utf8", + timeout: 30_000, env: { ...process.env, BROWSE_BIN }, stdio: ["ignore", "pipe", "pipe"], }); diff --git a/test/code-intelligence.test.ts b/test/code-intelligence.test.ts index 010e85f0c..a0adb51dc 100644 --- a/test/code-intelligence.test.ts +++ b/test/code-intelligence.test.ts @@ -399,7 +399,7 @@ describe("consent unification — deny tier wins (R1)", () => { function makeRepo(dir: string, url: string): string { const repo = path.join(dir, "repo"); fs.mkdirSync(repo, { recursive: true }); - const git = (...a: string[]) => execFileSync("git", a, { cwd: repo }); + const git = (...a: string[]) => execFileSync("git", a, { cwd: repo, timeout: 30_000 }); git("init", "-q", "."); git("remote", "add", "origin", url); return repo; @@ -423,11 +423,11 @@ describe("consent unification — deny tier wins (R1)", () => { const env = { ...process.env, GSTACK_HOME: home }; const repo = makeRepo(home, URL); setConsent(repo, true, env); - execFileSync(POLICY_BIN, ["set", URL, "deny"], { env, encoding: "utf-8" }); + execFileSync(POLICY_BIN, ["set", URL, "deny"], { env, encoding: "utf-8", timeout: 30_000 }); expect(hasConsent(repo, env)).toBe(false); // Flipping the tier back restores the recorded consent — the veto is // live policy, not a destructive rewrite of the consent store. - execFileSync(POLICY_BIN, ["set", URL, "read-write"], { env, encoding: "utf-8" }); + execFileSync(POLICY_BIN, ["set", URL, "read-write"], { env, encoding: "utf-8", timeout: 30_000 }); expect(hasConsent(repo, env)).toBe(true); } finally { fs.rmSync(home, { recursive: true, force: true }); } }); @@ -439,7 +439,7 @@ describe("consent unification — deny tier wins (R1)", () => { const env = { ...process.env, GSTACK_HOME: home }; const repo = makeRepo(home, URL); setConsent(repo, true, env); - execFileSync(POLICY_BIN, ["set", URL, "read-write"], { env, encoding: "utf-8" }); + execFileSync(POLICY_BIN, ["set", URL, "read-write"], { env, encoding: "utf-8", timeout: 30_000 }); fs.chmodSync(path.join(home, "gbrain-repo-policy.json"), 0o000); try { expect(hasConsent(repo, env)).toBe(false); @@ -460,7 +460,7 @@ describe("consent unification — deny tier wins (R1)", () => { const env = { ...process.env, GSTACK_HOME: home }; const repo = makeRepo(home, URL); setConsent(repo, true, env); - execFileSync(POLICY_BIN, ["set", URL, "read-only"], { env, encoding: "utf-8" }); + execFileSync(POLICY_BIN, ["set", URL, "read-only"], { env, encoding: "utf-8", timeout: 30_000 }); // Default op class is write — a caller that doesn't say gets fail-closed. expect(hasConsent(repo, env)).toBe(false); expect(hasConsent(repo, env, "write")).toBe(false); @@ -475,7 +475,7 @@ describe("consent unification — deny tier wins (R1)", () => { const env = { ...process.env, GSTACK_HOME: home }; const repo = makeRepo(home, URL); setConsent(repo, true, env); - execFileSync(POLICY_BIN, ["set", URL, "deny"], { env, encoding: "utf-8" }); + execFileSync(POLICY_BIN, ["set", URL, "deny"], { env, encoding: "utf-8", timeout: 30_000 }); expect(hasConsent(repo, env, "write")).toBe(false); expect(hasConsent(repo, env, "read")).toBe(false); } finally { fs.rmSync(home, { recursive: true, force: true }); } diff --git a/test/founder-resources-optout.test.ts b/test/founder-resources-optout.test.ts index 46993f0bb..8af21a970 100644 --- a/test/founder-resources-optout.test.ts +++ b/test/founder-resources-optout.test.ts @@ -30,6 +30,7 @@ afterEach(() => { fs.rmSync(tmpHome, { recursive: true, force: true }); }); function cfg(args: string[]): string { return execFileSync(CONFIG_BIN, args, { + timeout: 30_000, encoding: "utf-8", env: { ...process.env, GSTACK_HOME: tmpHome }, }).trim(); @@ -47,6 +48,7 @@ describe("founder_resources config key (#538)", () => { test("invalid values are rejected to the default, never persisted as-is", () => { execFileSync(CONFIG_BIN, ["set", "founder_resources", "banana"], { + timeout: 30_000, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, GSTACK_HOME: tmpHome }, }); diff --git a/test/gbrain-exec-invariant.test.ts b/test/gbrain-exec-invariant.test.ts index ae747b65f..205fbf319 100644 --- a/test/gbrain-exec-invariant.test.ts +++ b/test/gbrain-exec-invariant.test.ts @@ -38,6 +38,7 @@ const BANNED_PATTERNS: Array<{ name: string; regex: RegExp }> = [ // tripwire-exempt: grep-needle STRING for this invariant, not a process spawn { name: 'spawnSync("gbrain", ...)', regex: /spawnSync\s*\(\s*["']gbrain["']/g }, { name: 'spawn("gbrain", ...)', regex: /\bspawn\s*\(\s*["']gbrain["']/g }, + // tripwire-exempt: grep-needle STRING for this invariant, not a process spawn { name: 'execFileSync("gbrain", ...)', regex: /execFileSync\s*\(\s*["']gbrain["']/g }, // tripwire-exempt: grep-needle STRING for this invariant, not a process spawn { name: 'execSync("...gbrain...")', regex: /execSync\s*\(\s*["'`][^"'`]*\bgbrain\b/g }, diff --git a/test/gbrain-local-status.test.ts b/test/gbrain-local-status.test.ts index a81059f85..e310362ce 100644 --- a/test/gbrain-local-status.test.ts +++ b/test/gbrain-local-status.test.ts @@ -210,6 +210,7 @@ describe("lib/gbrain-local-status — status classification", () => { ); expect(source).not.toContain('command -v gbrain'); + // tripwire-exempt: string assertion on lib source text, not a call expect(source).toContain('execFileSync("gbrain", ["--version"]'); }); diff --git a/test/gbrain-sync-skip.test.ts b/test/gbrain-sync-skip.test.ts index dabf15a7d..ce8866a2f 100644 --- a/test/gbrain-sync-skip.test.ts +++ b/test/gbrain-sync-skip.test.ts @@ -25,7 +25,7 @@ import { join } from "path"; import { execFileSync, spawnSync } from "child_process"; const SCRIPT = join(import.meta.dir, "..", "bin", "gstack-gbrain-sync.ts"); -const BUN_BIN = execFileSync("sh", ["-c", "command -v bun"], { encoding: "utf-8" }).trim(); +const BUN_BIN = execFileSync("sh", ["-c", "command -v bun"], { encoding: "utf-8", timeout: 30_000 }).trim(); interface FakeEnv { tmp: string; diff --git a/test/gstack-learnings-search.test.ts b/test/gstack-learnings-search.test.ts index 489e52607..ffa0227ae 100644 --- a/test/gstack-learnings-search.test.ts +++ b/test/gstack-learnings-search.test.ts @@ -16,6 +16,7 @@ const otherProjDir = path.join(tmpHome, 'projects', 'other-project'); function run(args: string[]): string { return execFileSync(BIN, args, { + timeout: 30_000, env: { ...process.env, GSTACK_HOME: tmpHome }, cwd: tmpCwd, encoding: 'utf-8', diff --git a/test/gstack-session-kind.test.ts b/test/gstack-session-kind.test.ts index 7fab33871..310daeee0 100644 --- a/test/gstack-session-kind.test.ts +++ b/test/gstack-session-kind.test.ts @@ -22,6 +22,7 @@ function kind(env: Record): string { return execFileSync(BIN, [], { env: { PATH: process.env.PATH ?? '/usr/bin:/bin', ...env }, encoding: 'utf-8', + timeout: 30_000, }).trim(); } diff --git a/test/gstack-skill-start.test.ts b/test/gstack-skill-start.test.ts index 860352dc7..fcc069d64 100644 --- a/test/gstack-skill-start.test.ts +++ b/test/gstack-skill-start.test.ts @@ -31,6 +31,7 @@ let tmpGstackHome: string; function runStart(args: string[] = [], env: Record = {}): string { return execFileSync(START, ['--skill', 'testskill', ...args], { + timeout: 30_000, encoding: 'utf-8', cwd: tmpHome, // no CLAUDE.md/AGENTS.md, not the repo — routing detection stays cold env: { @@ -150,6 +151,7 @@ describe('gstack-skill-start behavior', () => { fs.copyFileSync(START, path.join(fakeBin, 'gstack-skill-start')); fs.chmodSync(path.join(fakeBin, 'gstack-skill-start'), 0o755); const out = execFileSync(path.join(fakeBin, 'gstack-skill-start'), ['--skill', 't'], { + timeout: 30_000, encoding: 'utf-8', cwd: tmpHome, env: { PATH: process.env.PATH!, HOME: tmpHome, GSTACK_HOME: tmpGstackHome }, @@ -317,7 +319,7 @@ describe('gstack-skill-end', () => { const out = execFileSync( END, ['--skill', 't', '--outcome', 'success', '--session-id', 'sid-1', '--tel-start', String(start)], - { encoding: 'utf-8', cwd: tmpHome, env: { PATH: process.env.PATH!, HOME: tmpHome, GSTACK_HOME: tmpGstackHome } }, + { timeout: 30_000, encoding: 'utf-8', cwd: tmpHome, env: { PATH: process.env.PATH!, HOME: tmpHome, GSTACK_HOME: tmpGstackHome } }, ); const m = out.match(/SKILL_END: recorded outcome=success duration_s=(\d+)/); expect(m).not.toBeNull(); @@ -338,6 +340,7 @@ describe('gstack-skill-end', () => { const pending = path.join(tmpGstackHome, 'analytics', '.pending-sid-2'); fs.writeFileSync(pending, 'x'); execFileSync(END, ['--skill', 't', '--outcome', 'abort', '--session-id', 'sid-2', '--tel-start', 'bogus'], { + timeout: 30_000, encoding: 'utf-8', cwd: tmpHome, env: { PATH: process.env.PATH!, HOME: tmpHome, GSTACK_HOME: tmpGstackHome }, diff --git a/test/gstack-version-bump.test.ts b/test/gstack-version-bump.test.ts index 89d525803..2c8a55835 100644 --- a/test/gstack-version-bump.test.ts +++ b/test/gstack-version-bump.test.ts @@ -61,7 +61,7 @@ describe('write (FRESH bump)', () => { test('writes VERSION + package.json.version, preserving other pkg fields', () => { fs.writeFileSync(path.join(dir, 'VERSION'), '1.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.0.0.0', scripts: { t: 'y' } }, null, 2) + '\n'); - const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir, timeout: 30_000 }).toString(); expect(JSON.parse(out)).toEqual({ wrote: '1.1.0.0', packageJson: true, packageJsonPath: 'package.json', packageJsonVersion: '1.1.0', packageLock: false, agentsDigest: null, @@ -76,7 +76,7 @@ describe('write (FRESH bump)', () => { test('rejects a malformed version with exit 2', () => { let code = 0; - try { execFileSync('bun', [BIN, 'write', '--version', '1.2.3.4.5'], { cwd: dir, stdio: 'pipe' }); } + try { execFileSync('bun', [BIN, 'write', '--version', '1.2.3.4.5'], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; } expect(code).toBe(2); }); @@ -84,7 +84,7 @@ describe('write (FRESH bump)', () => { test('VERSION-only repo (no package.json) writes just VERSION', () => { const d2 = fs.mkdtempSync(path.join(os.tmpdir(), 'vbump-noPkg-')); fs.writeFileSync(path.join(d2, 'VERSION'), '0.1.0.0\n'); - const out = execFileSync('bun', [BIN, 'write', '--version', '0.2.0.0'], { cwd: d2 }).toString(); + const out = execFileSync('bun', [BIN, 'write', '--version', '0.2.0.0'], { cwd: d2, timeout: 30_000 }).toString(); expect(JSON.parse(out)).toEqual({ wrote: '0.2.0.0', packageJson: false, packageJsonPath: null, packageJsonVersion: null, packageLock: false, agentsDigest: null, @@ -101,7 +101,7 @@ describe('repair (DRIFT_STALE_PKG)', () => { test('syncs package.json.version up to VERSION, no re-bump', () => { fs.writeFileSync(path.join(dir, 'VERSION'), '2.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.9.0.0' }, null, 2) + '\n'); - const out = execFileSync('bun', [BIN, 'repair'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'repair'], { cwd: dir, timeout: 30_000 }).toString(); expect(JSON.parse(out)).toEqual({ repaired: '2.0.0.0', packageJsonPath: 'package.json', packageJsonVersion: '2.0.0', }); @@ -112,7 +112,7 @@ describe('repair (DRIFT_STALE_PKG)', () => { test('refuses to propagate an invalid VERSION (exit 2)', () => { fs.writeFileSync(path.join(dir, 'VERSION'), 'not-a-version\n'); let code = 0; - try { execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe' }); } + try { execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; } expect(code).toBe(2); }); @@ -131,7 +131,7 @@ describe('write/repair sync npm lockfiles (both version fields, #2567)', () => { fs.writeFileSync(path.join(dir, 'VERSION'), '1.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.0.0' }, null, 2) + '\n'); fs.writeFileSync(path.join(dir, 'package-lock.json'), lock('1.0.0')); - const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir, timeout: 30_000 }).toString(); expect(JSON.parse(out)).toEqual({ wrote: '1.1.0.0', packageJson: true, packageJsonPath: 'package.json', packageJsonVersion: '1.1.0', packageLock: true, agentsDigest: null, @@ -146,7 +146,7 @@ describe('write/repair sync npm lockfiles (both version fields, #2567)', () => { fs.writeFileSync(path.join(dir, 'VERSION'), '2.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.9.0' }, null, 2) + '\n'); fs.writeFileSync(path.join(dir, 'package-lock.json'), lock('1.9.0')); - execFileSync('bun', [BIN, 'repair'], { cwd: dir }); + execFileSync('bun', [BIN, 'repair'], { cwd: dir, timeout: 30_000 }); const l = JSON.parse(fs.readFileSync(path.join(dir, 'package-lock.json'), 'utf-8')); expect(l.version).toBe('2.0.0'); expect(l.packages[''].version).toBe('2.0.0'); @@ -156,7 +156,7 @@ describe('write/repair sync npm lockfiles (both version fields, #2567)', () => { fs.writeFileSync(path.join(dir, 'VERSION'), '3.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '2.9.0' }, null, 2) + '\n'); fs.writeFileSync(path.join(dir, 'package-lock.json'), JSON.stringify({ name: 'x', version: '2.9.0', lockfileVersion: 1 }, null, 2) + '\n'); - execFileSync('bun', [BIN, 'repair'], { cwd: dir }); + execFileSync('bun', [BIN, 'repair'], { cwd: dir, timeout: 30_000 }); const l = JSON.parse(fs.readFileSync(path.join(dir, 'package-lock.json'), 'utf-8')); expect(l.version).toBe('3.0.0'); expect(l.packages).toBeUndefined(); @@ -167,7 +167,7 @@ describe('write/repair sync npm lockfiles (both version fields, #2567)', () => { fs.writeFileSync(path.join(d2, 'VERSION'), '1.0.0.0\n'); fs.writeFileSync(path.join(d2, 'package.json'), JSON.stringify({ name: 'x', version: '1.0.0' }, null, 2) + '\n'); fs.writeFileSync(path.join(d2, 'npm-shrinkwrap.json'), lock('1.0.0').replace('package-lock', 'npm-shrinkwrap')); - const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: d2 }).toString(); + const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: d2, timeout: 30_000 }).toString(); expect(JSON.parse(out).packageLock).toBe(true); const l = JSON.parse(fs.readFileSync(path.join(d2, 'npm-shrinkwrap.json'), 'utf-8')); expect(l.version).toBe('1.1.0'); @@ -183,7 +183,7 @@ describe('write/repair sync npm lockfiles (both version fields, #2567)', () => { fs.writeFileSync(path.join(d3, 'package.json'), JSON.stringify({ name: 'x', version: '1.0.0.0' }, null, 2) + '\n'); fs.writeFileSync(path.join(d3, 'package-lock.json'), '{ not json'); let code = 0; - try { execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: d3, stdio: 'pipe' }); } + try { execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: d3, stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; } expect(code).toBe(3); // VERSION was written before the failure — exactly the half-write the @@ -198,26 +198,26 @@ describe('classify (idempotency over a real git base)', () => { afterAll(() => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* noop */ } }); // Build a tiny repo with an "origin/main" carrying VERSION=1.0.0.0. - const git = (...a: string[]) => execFileSync('git', a, { cwd: dir, stdio: 'pipe' }); + const git = (...a: string[]) => execFileSync('git', a, { cwd: dir, stdio: 'pipe', timeout: 30_000 }); fs.writeFileSync(path.join(dir, 'VERSION'), '1.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.0.0.0' }, null, 2) + '\n'); git('init', '-q', '-b', 'main'); git('config', 'user.email', 't@t'); git('config', 'user.name', 't'); git('add', '-A'); git('commit', '-q', '-m', 'base'); // Fake an "origin/main" remote-tracking ref pointing at this commit. - const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: dir }).toString().trim(); + const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: dir, timeout: 30_000 }).toString().trim(); fs.mkdirSync(path.join(dir, '.git', 'refs', 'remotes', 'origin'), { recursive: true }); fs.writeFileSync(path.join(dir, '.git', 'refs', 'remotes', 'origin', 'main'), head + '\n'); test('reports FRESH before any bump', () => { - const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir, timeout: 30_000 }).toString(); expect(JSON.parse(out).state).toBe('FRESH'); }); test('reports ALREADY_BUMPED after VERSION+pkg move together', () => { fs.writeFileSync(path.join(dir, 'VERSION'), '1.1.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.1.0.0' }, null, 2) + '\n'); - const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir, timeout: 30_000 }).toString(); const parsed = JSON.parse(out); expect(parsed.state).toBe('ALREADY_BUMPED'); expect(parsed.baseVersion).toBe('1.0.0.0'); @@ -246,17 +246,17 @@ describe('package.json as the version source (monorepo, 3-digit, #2501)', () => fs.mkdirSync(path.join(dir, 'frontend'), { recursive: true }); fs.writeFileSync(pkgAbs, JSON.stringify({ name: 'frontend', version: '0.99.2', private: true, scripts: { dev: 'next dev' } }, null, 2) + '\n'); - execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: dir }); - execFileSync('git', ['config', 'user.email', 't@e.com'], { cwd: dir }); - execFileSync('git', ['config', 'user.name', 't'], { cwd: dir }); - execFileSync('git', ['add', '-A'], { cwd: dir }); - execFileSync('git', ['commit', '-qm', 'v0.99.2 base'], { cwd: dir }); - const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: dir }).toString().trim(); + execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: dir, timeout: 30_000 }); + execFileSync('git', ['config', 'user.email', 't@e.com'], { cwd: dir, timeout: 30_000 }); + execFileSync('git', ['config', 'user.name', 't'], { cwd: dir, timeout: 30_000 }); + execFileSync('git', ['add', '-A'], { cwd: dir, timeout: 30_000 }); + execFileSync('git', ['commit', '-qm', 'v0.99.2 base'], { cwd: dir, timeout: 30_000 }); + const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: dir, timeout: 30_000 }).toString().trim(); fs.mkdirSync(path.join(dir, '.git', 'refs', 'remotes', 'origin'), { recursive: true }); fs.writeFileSync(path.join(dir, '.git', 'refs', 'remotes', 'origin', 'main'), head + '\n'); test('classify reads the real version from the package.json version-path', () => { - const out = execFileSync('bun', [BIN, 'classify', '--base', 'main', '--version-path', pkgRel], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'classify', '--base', 'main', '--version-path', pkgRel], { cwd: dir, timeout: 30_000 }).toString(); const parsed = JSON.parse(out); expect(parsed.state).toBe('FRESH'); expect(parsed.baseVersion).toBe('0.99.2'); // was "0.0.0.0" @@ -265,7 +265,7 @@ describe('package.json as the version source (monorepo, 3-digit, #2501)', () => }); test('write updates the package.json in place and creates no VERSION file', () => { - const out = execFileSync('bun', [BIN, 'write', '--version', '0.99.3', '--version-path', pkgRel], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'write', '--version', '0.99.3', '--version-path', pkgRel], { cwd: dir, timeout: 30_000 }).toString(); expect(JSON.parse(out)).toEqual({ wrote: '0.99.3', versionPath: pkgRel, packageJson: true, packageLock: false, agentsDigest: null }); const pkg = JSON.parse(fs.readFileSync(pkgAbs, 'utf-8')); expect(pkg.version).toBe('0.99.3'); @@ -275,7 +275,7 @@ describe('package.json as the version source (monorepo, 3-digit, #2501)', () => }); test('classify reports ALREADY_BUMPED after that write, not a drift state', () => { - const out = execFileSync('bun', [BIN, 'classify', '--base', 'main', '--version-path', pkgRel], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'classify', '--base', 'main', '--version-path', pkgRel], { cwd: dir, timeout: 30_000 }).toString(); const parsed = JSON.parse(out); expect(parsed.state).toBe('ALREADY_BUMPED'); expect(parsed.baseVersion).toBe('0.99.2'); @@ -283,14 +283,14 @@ describe('package.json as the version source (monorepo, 3-digit, #2501)', () => }); test('repair is a no-op: there is no second file to drift from', () => { - const out = execFileSync('bun', [BIN, 'repair', '--version-path', pkgRel], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'repair', '--version-path', pkgRel], { cwd: dir, timeout: 30_000 }).toString(); expect(JSON.parse(out).repaired).toBeNull(); }); test('write refuses a version-path that does not exist', () => { let code = 0; try { - execFileSync('bun', [BIN, 'write', '--version', '1.0.0', '--version-path', 'nope/package.json'], { cwd: dir, stdio: 'pipe' }); + execFileSync('bun', [BIN, 'write', '--version', '1.0.0', '--version-path', 'nope/package.json'], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; } expect(code).toBe(2); }); @@ -315,12 +315,12 @@ describe('.gstack/version-path pin, no --version-path flag (#2462)', () => { }; const commitBase = (d: string): void => { - execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: d }); - execFileSync('git', ['config', 'user.email', 't@e.com'], { cwd: d }); - execFileSync('git', ['config', 'user.name', 't'], { cwd: d }); - execFileSync('git', ['add', '-A'], { cwd: d }); - execFileSync('git', ['commit', '-qm', 'base'], { cwd: d }); - const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: d }).toString().trim(); + execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['config', 'user.email', 't@e.com'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['config', 'user.name', 't'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['add', '-A'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['commit', '-qm', 'base'], { cwd: d, timeout: 30_000 }); + const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: d, timeout: 30_000 }).toString().trim(); fs.mkdirSync(path.join(d, '.git', 'refs', 'remotes', 'origin'), { recursive: true }); fs.writeFileSync(path.join(d, '.git', 'refs', 'remotes', 'origin', 'main'), head + '\n'); }; @@ -332,7 +332,7 @@ describe('.gstack/version-path pin, no --version-path flag (#2462)', () => { commitBase(d); // Move the pinned file past base — NO root VERSION file exists at all. fs.writeFileSync(path.join(d, pinRel), '1.5.0.0\n'); - const out = JSON.parse(execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: d }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: d, timeout: 30_000 }).toString()); // Before the fix: baseVersion read root VERSION → "0.0.0.0" and the // branch misclassified as... current 1.5.0.0 vs base 0.0.0.0. The REAL // base is the pinned file's committed value. @@ -347,7 +347,7 @@ describe('.gstack/version-path pin, no --version-path flag (#2462)', () => { const d = mkPinned(pinRel); fs.writeFileSync(path.join(d, pinRel), JSON.stringify({ name: 'f', version: '0.99.2' }, null, 2) + '\n'); commitBase(d); - const out = JSON.parse(execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: d }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: d, timeout: 30_000 }).toString()); // Before the fix: versionRel="VERSION" → the pinned JSON was read as raw // text → currentVersion "0.0.0.0", pkgExists false, base from a // nonexistent root VERSION. @@ -362,7 +362,7 @@ describe('.gstack/version-path pin, no --version-path flag (#2462)', () => { const pinRel = 'frontend/package.json'; const d = mkPinned(pinRel); fs.writeFileSync(path.join(d, pinRel), JSON.stringify({ name: 'f', version: '0.99.2' }, null, 2) + '\n'); - const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '0.99.3'], { cwd: d }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '0.99.3'], { cwd: d, timeout: 30_000 }).toString()); expect(out).toEqual({ wrote: '0.99.3', versionPath: pinRel, packageJson: true, packageLock: false, agentsDigest: null }); expect(JSON.parse(fs.readFileSync(path.join(d, pinRel), 'utf-8')).version).toBe('0.99.3'); // Before the fix, write treated versionRel as "VERSION" and overwrote the @@ -375,7 +375,7 @@ describe('.gstack/version-path pin, no --version-path flag (#2462)', () => { const pinRel = 'frontend/package.json'; const d = mkPinned(pinRel); fs.writeFileSync(path.join(d, pinRel), JSON.stringify({ name: 'f', version: '0.99.2' }, null, 2) + '\n'); - const out = JSON.parse(execFileSync('bun', [BIN, 'repair'], { cwd: d }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'repair'], { cwd: d, timeout: 30_000 }).toString()); expect(out.repaired).toBeNull(); fs.rmSync(d, { recursive: true, force: true }); }); @@ -386,7 +386,7 @@ describe('.gstack/version-path pin, no --version-path flag (#2462)', () => { fs.writeFileSync(path.join(d, 'OTHER_VERSION'), '2.0.0.0\n'); commitBase(d); const out = JSON.parse( - execFileSync('bun', [BIN, 'classify', '--base', 'main', '--version-path', 'OTHER_VERSION'], { cwd: d }).toString(), + execFileSync('bun', [BIN, 'classify', '--base', 'main', '--version-path', 'OTHER_VERSION'], { cwd: d, timeout: 30_000 }).toString(), ); expect(out.currentVersion).toBe('2.0.0.0'); expect(out.baseVersion).toBe('2.0.0.0'); @@ -414,7 +414,7 @@ describe('subdirectory manifest (no root package.json, #2531)', () => { const d = mk(); fs.writeFileSync(path.join(d, 'web', 'package.json'), JSON.stringify({ name: 'w', version: '0.1.0' }, null, 2) + '\n'); - const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '0.2.0.0'], { cwd: d }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '0.2.0.0'], { cwd: d, timeout: 30_000 }).toString()); expect(out.packageJson).toBe(true); expect(out.packageJsonPath).toBe('web/package.json'); expect(out.packageJsonVersion).toBe('0.2.0'); @@ -428,7 +428,7 @@ describe('subdirectory manifest (no root package.json, #2531)', () => { fs.writeFileSync(path.join(d, 'web', 'package.json'), JSON.stringify({ version: '0.1.0' }, null, 2) + '\n'); fs.writeFileSync(path.join(d, 'app', 'package.json'), JSON.stringify({ version: '0.1.0' }, null, 2) + '\n'); const out = JSON.parse(execFileSync('bun', - [BIN, 'write', '--version', '0.3.0.0', '--package-json-path', 'app/package.json'], { cwd: d }).toString()); + [BIN, 'write', '--version', '0.3.0.0', '--package-json-path', 'app/package.json'], { cwd: d, timeout: 30_000 }).toString()); expect(out.packageJsonPath).toBe('app/package.json'); expect(JSON.parse(fs.readFileSync(path.join(d, 'app', 'package.json'), 'utf-8')).version).toBe('0.3.0'); // the pinned one is untouched @@ -440,16 +440,16 @@ describe('subdirectory manifest (no root package.json, #2531)', () => { const d = mk(); fs.writeFileSync(path.join(d, 'web', 'package.json'), JSON.stringify({ name: 'w', version: '0.1.0' }, null, 2) + '\n'); - execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: d }); - execFileSync('git', ['config', 'user.email', 't@e.com'], { cwd: d }); - execFileSync('git', ['config', 'user.name', 't'], { cwd: d }); - execFileSync('git', ['add', '-A'], { cwd: d }); - execFileSync('git', ['commit', '-qm', 'base'], { cwd: d }); - const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: d }).toString().trim(); + execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['config', 'user.email', 't@e.com'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['config', 'user.name', 't'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['add', '-A'], { cwd: d, timeout: 30_000 }); + execFileSync('git', ['commit', '-qm', 'base'], { cwd: d, timeout: 30_000 }); + const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: d, timeout: 30_000 }).toString().trim(); fs.mkdirSync(path.join(d, '.git', 'refs', 'remotes', 'origin'), { recursive: true }); fs.writeFileSync(path.join(d, '.git', 'refs', 'remotes', 'origin', 'main'), head + '\n'); - const out = JSON.parse(execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: d }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: d, timeout: 30_000 }).toString()); // 0.1.0 IS the npm-valid translation of 0.1.0.0 — in sync, no drift. expect(out.state).toBe('FRESH'); expect(out.pkgExists).toBe(true); @@ -462,7 +462,7 @@ describe('subdirectory manifest (no root package.json, #2531)', () => { const d = mk(); fs.writeFileSync(path.join(d, 'web', 'package.json'), JSON.stringify({ name: 'w', version: '0.0.9' }, null, 2) + '\n'); - const out = JSON.parse(execFileSync('bun', [BIN, 'repair'], { cwd: d }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'repair'], { cwd: d, timeout: 30_000 }).toString()); expect(out).toEqual({ repaired: '0.1.0.0', packageJsonPath: 'web/package.json', packageJsonVersion: '0.1.0' }); expect(JSON.parse(fs.readFileSync(path.join(d, 'web', 'package.json'), 'utf-8')).version).toBe('0.1.0'); fs.rmSync(d, { recursive: true, force: true }); @@ -504,7 +504,7 @@ describe('path containment: pins and flags cannot escape the repo', () => { function runFail(args: string[]): { code: number; stderr: string } { try { - execFileSync('bun', [BIN, ...args], { cwd: dir, stdio: 'pipe' }); + execFileSync('bun', [BIN, ...args], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); return { code: 0, stderr: '' }; } catch (e: any) { return { code: e.status, stderr: (e.stderr || '').toString() }; @@ -562,7 +562,7 @@ describe('path containment: pins and flags cannot escape the repo', () => { fs.writeFileSync(outerLock, JSON.stringify({ version: '1.0.0', packages: { '': { version: '1.0.0' } } }, null, 2) + '\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.0.0' }, null, 2) + '\n'); fs.symlinkSync(outerLock, path.join(dir, 'package-lock.json')); - const res = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir, stdio: 'pipe' }); + const res = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); expect(JSON.parse(res.toString()).packageLock).toBe(false); expect(JSON.parse(fs.readFileSync(outerLock, 'utf-8')).version).toBe('1.0.0'); }); @@ -572,7 +572,7 @@ describe('path containment: pins and flags cannot escape the repo', () => { fs.mkdirSync(path.join(dir, 'frontend'), { recursive: true }); fs.writeFileSync(path.join(dir, 'frontend', 'package.json'), JSON.stringify({ name: 'x', version: '1.0.0' }, null, 2) + '\n'); fs.writeFileSync(path.join(dir, '.gstack', 'version-path'), 'frontend/package.json\n'); - const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'write', '--version', '1.1.0'], { cwd: dir, timeout: 30_000 }).toString(); expect(JSON.parse(out).wrote).toBe('1.1.0'); expect(JSON.parse(fs.readFileSync(path.join(dir, 'frontend', 'package.json'), 'utf-8')).version).toBe('1.1.0'); }); @@ -602,7 +602,7 @@ describe('#2600: repair must not write fabricated 0.0.0.0 when VERSION is missin let code = 0; let stderr = ''; try { - execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe' }); + execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; stderr = (e.stderr || '').toString(); @@ -621,7 +621,7 @@ describe('#2600: repair must not write fabricated 0.0.0.0 when VERSION is missin fs.writeFileSync(path.join(dir, 'VERSION'), '2.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '1.9.0' }, null, 2) + '\n'); - const out = execFileSync('bun', [BIN, 'repair'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'repair'], { cwd: dir, timeout: 30_000 }).toString(); const result = JSON.parse(out); expect(result.repaired).toBe('2.0.0.0'); @@ -639,7 +639,7 @@ describe('#2600: repair must not write fabricated 0.0.0.0 when VERSION is missin let code = 0; let stderr = ''; try { - execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe' }); + execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; stderr = (e.stderr || '').toString(); @@ -660,7 +660,7 @@ describe('#2600: repair must not write fabricated 0.0.0.0 when VERSION is missin fs.writeFileSync(path.join(dir, 'VERSION'), '0.0.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '0.5.0' }, null, 2) + '\n'); - const out = execFileSync('bun', [BIN, 'repair'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'repair'], { cwd: dir, timeout: 30_000 }).toString(); const result = JSON.parse(out); expect(result.repaired).toBe('0.0.0.0'); expect(result.packageJsonVersion).toBe('0.0.0'); @@ -673,7 +673,7 @@ describe('#2600: repair must not write fabricated 0.0.0.0 when VERSION is missin fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '0.5.0' }, null, 2) + '\n'); let code = 0; - try { execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe' }); } + try { execFileSync('bun', [BIN, 'repair'], { cwd: dir, stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; } expect(code).toBe(2); expect(JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8')).version).toBe('0.5.0'); @@ -692,7 +692,7 @@ describe('#2600: repair must not write fabricated 0.0.0.0 when VERSION is missin let code = 0; let stderr = ''; try { - execFileSync('bun', [BIN, 'repair'], { cwd: path.join(rootDir, 'app'), stdio: 'pipe' }); + execFileSync('bun', [BIN, 'repair'], { cwd: path.join(rootDir, 'app'), stdio: 'pipe', timeout: 30_000 }); } catch (e: any) { code = e.status; stderr = (e.stderr || '').toString(); @@ -717,13 +717,13 @@ describe('#2600: classify must surface versionFileExists=false when VERSION is m function makeRepoDir(): string { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'vbump-2600-classify-')); dirs.push(dir); - const git = (...a: string[]) => execFileSync('git', a, { cwd: dir, stdio: 'pipe' }); + const git = (...a: string[]) => execFileSync('git', a, { cwd: dir, stdio: 'pipe', timeout: 30_000 }); git('init', '-q', '-b', 'main'); git('config', 'user.email', 't@t'); git('config', 'user.name', 't'); // Commit with no VERSION file fs.writeFileSync(path.join(dir, 'README.md'), 'test\n'); git('add', '-A'); git('commit', '-q', '-m', 'base'); - const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: dir }).toString().trim(); + const head = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: dir, timeout: 30_000 }).toString().trim(); fs.mkdirSync(path.join(dir, '.git', 'refs', 'remotes', 'origin'), { recursive: true }); fs.writeFileSync(path.join(dir, '.git', 'refs', 'remotes', 'origin', 'main'), head + '\n'); return dir; @@ -734,7 +734,7 @@ describe('#2600: classify must surface versionFileExists=false when VERSION is m // No package.json: pkgExists=false, pkgAgrees=true, current===base → FRESH. // (A package.json with a non-zero version would cause DRIFT_UNEXPECTED.) - const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir, timeout: 30_000 }).toString(); const result = JSON.parse(out); expect(result.versionFileExists).toBe(false); @@ -748,7 +748,7 @@ describe('#2600: classify must surface versionFileExists=false when VERSION is m fs.writeFileSync(path.join(dir, 'VERSION'), '0.2.0.0\n'); fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'x', version: '0.2.0.0' }, null, 2) + '\n'); - const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir }).toString(); + const out = execFileSync('bun', [BIN, 'classify', '--base', 'main'], { cwd: dir, timeout: 30_000 }).toString(); const result = JSON.parse(out); expect(result.versionFileExists).toBe(true); @@ -784,7 +784,7 @@ describe('write --regen-digest regenerates the gstack agents digest (explicit op const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'vbump-digest-')); fs.writeFileSync(path.join(dir, 'VERSION'), '1.0.0.0\n'); stubGenerator(dir); - const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0', '--regen-digest'], { cwd: dir }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0', '--regen-digest'], { cwd: dir, timeout: 30_000 }).toString()); expect(out.agentsDigest).toBe(true); expect(fs.readFileSync(path.join(dir, 'agents-digest', 'gstack-AGENTS.md'), 'utf-8')) .toBe('# gstack digest v1.1.0.0\n'); @@ -795,7 +795,7 @@ describe('write --regen-digest regenerates the gstack agents digest (explicit op const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'vbump-digest-noflag-')); fs.writeFileSync(path.join(dir, 'VERSION'), '1.0.0.0\n'); stubGenerator(dir); - const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0'], { cwd: dir, timeout: 30_000 }).toString()); expect(out.agentsDigest).toBe(null); // Digest untouched — the stub would have stamped v1.1.0.0 had it run. expect(fs.readFileSync(path.join(dir, 'agents-digest', 'gstack-AGENTS.md'), 'utf-8')) @@ -811,7 +811,7 @@ describe('write --regen-digest regenerates the gstack agents digest (explicit op fs.writeFileSync(path.join(d2, 'scripts', 'gen-agents-digest.ts'), 'process.exit(1);\n'); fs.writeFileSync(path.join(d2, 'agents-digest', 'gstack-AGENTS.md'), '# gstack digest v1.0.0.0\n'); - const res = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0', '--regen-digest'], { cwd: d2, stdio: 'pipe' }); + const res = execFileSync('bun', [BIN, 'write', '--version', '1.1.0.0', '--regen-digest'], { cwd: d2, stdio: 'pipe', timeout: 30_000 }); const out = JSON.parse(res.toString()); expect(out.wrote).toBe('1.1.0.0'); // the bump itself still lands expect(out.agentsDigest).toBe(false); @@ -834,7 +834,7 @@ describe('write --regen-digest regenerates the gstack agents digest (explicit op path.join(dir, 'agents-digest', 'gstack-AGENTS.md'), ); fs.writeFileSync(path.join(dir, 'VERSION'), '9.9.9.9\n'); - const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '9.9.10.0', '--regen-digest'], { cwd: dir }).toString()); + const out = JSON.parse(execFileSync('bun', [BIN, 'write', '--version', '9.9.10.0', '--regen-digest'], { cwd: dir, timeout: 30_000 }).toString()); expect(out.agentsDigest).toBe(true); const first = fs.readFileSync(path.join(dir, 'agents-digest', 'gstack-AGENTS.md'), 'utf-8').split('\n')[0]; expect(first).toContain('v9.9.10.0'); diff --git a/test/memory-ingest-include-gitignored.test.ts b/test/memory-ingest-include-gitignored.test.ts index 51b9d7ae9..901f38a69 100644 --- a/test/memory-ingest-include-gitignored.test.ts +++ b/test/memory-ingest-include-gitignored.test.ts @@ -69,6 +69,7 @@ describe("gstack-memory-ingest: gbrain import must not be filtered by .gitignore try { const git = (args: string[], cwd: string, env?: NodeJS.ProcessEnv) => execFileSync("git", args, { + timeout: 30_000, cwd, encoding: "utf-8", env: { ...process.env, ...env }, @@ -113,7 +114,7 @@ describe("gstack-memory-ingest: gbrain import must not be filtered by .gitignore const dir = mkdtempSync(join(tmpdir(), "gstack-ingest-gitignore-")); try { const git = (...args: string[]) => - execFileSync("git", args, { cwd: dir, encoding: "utf-8" }); + execFileSync("git", args, { timeout: 30_000, cwd: dir, encoding: "utf-8" }); git("init", "-q", "."); const staging = join(dir, ".staging-ingest-12345-1700000000000", "learnings"); diff --git a/test/mktemp-portability.test.ts b/test/mktemp-portability.test.ts index e9c2811e3..602a072ea 100644 --- a/test/mktemp-portability.test.ts +++ b/test/mktemp-portability.test.ts @@ -23,6 +23,7 @@ function trackedTmplFiles(): string[] { const out = execFileSync("git", ["ls-files", "*.tmpl", "**/*.tmpl"], { cwd: ROOT, encoding: "utf-8", + timeout: 30_000, }); return out.split("\n").filter(Boolean); } @@ -50,8 +51,9 @@ describe("mktemp portability (#2091)", () => { const tmp = process.env.TMPDIR || "/tmp"; const created = execFileSync("mktemp", [`${tmp.replace(/\/$/, "")}/gstack-portability-XXXXXX`], { encoding: "utf-8", + timeout: 30_000, }).trim(); expect(created.length).toBeGreaterThan(0); - execFileSync("rm", ["-f", created]); + execFileSync("rm", ["-f", created], { timeout: 30_000 }); }); }); diff --git a/test/resolvers-gbrain-put-rewrite.test.ts b/test/resolvers-gbrain-put-rewrite.test.ts index 75a0d2225..cdfb1d2de 100644 --- a/test/resolvers-gbrain-put-rewrite.test.ts +++ b/test/resolvers-gbrain-put-rewrite.test.ts @@ -31,6 +31,7 @@ function listTrackedSkillMd(): string[] { const out = execFileSync("git", ["ls-files", "*SKILL.md"], { cwd: REPO_ROOT, encoding: "utf-8", + timeout: 30_000, }); return out.split("\n").filter((line) => line.trim().length > 0); } diff --git a/test/session-update-autostash.test.ts b/test/session-update-autostash.test.ts index 341ddc118..b61b659a2 100644 --- a/test/session-update-autostash.test.ts +++ b/test/session-update-autostash.test.ts @@ -15,7 +15,7 @@ const ROOT = path.resolve(import.meta.dir, '..'); const SCRIPT = path.join(ROOT, 'bin', 'gstack-session-update'); function git(cwd: string, ...args: string[]): string { - return execFileSync('git', args, { cwd, encoding: 'utf8' }).trim(); + return execFileSync('git', args, { cwd, encoding: 'utf8', timeout: 30_000 }).trim(); } function makeFixture() { diff --git a/test/ship-test-detection-markers.test.ts b/test/ship-test-detection-markers.test.ts index fb0c80f88..e842642a4 100644 --- a/test/ship-test-detection-markers.test.ts +++ b/test/ship-test-detection-markers.test.ts @@ -44,13 +44,13 @@ function detect(files: Record): string { } const script = path.join(dir, '.detect.sh'); fs.writeFileSync(script, detectionScript()); - const git = (...args: string[]) => execFileSync('git', args, { cwd: dir }); + const git = (...args: string[]) => execFileSync('git', args, { timeout: 30_000, cwd: dir }); git('init', '-q', '.'); git('add', '-A'); git('-c', 'user.email=t@t', '-c', 'user.name=t', 'commit', '-qm', 'fixture'); // The block's last line is a `[ -f marker ] && echo`, so a clean project // exits 1 by design — read stdout, don't trust the status. - return execFileSync('bash', [script], { cwd: dir, encoding: 'utf-8' }); + return execFileSync('bash', [script], { timeout: 30_000, cwd: dir, encoding: 'utf-8' }); } catch (err: unknown) { const e = err as { stdout?: string }; if (typeof e.stdout === 'string') return e.stdout; diff --git a/test/tasks-section-jq.test.ts b/test/tasks-section-jq.test.ts index 78f8de9cf..a3f61b593 100644 --- a/test/tasks-section-jq.test.ts +++ b/test/tasks-section-jq.test.ts @@ -41,7 +41,7 @@ function runJq(program: string, inputLines: string[], branch: string, commits: s const out = execFileSync( "jq", ["-c", "--arg", "branch", branch, "--arg", "commits", commits, program], - { input: inputLines.join("\n"), encoding: "utf-8" }, + { input: inputLines.join("\n"), encoding: "utf-8", timeout: 30_000 }, ); return out.split("\n").filter(Boolean); } diff --git a/test/timeline.test.ts b/test/timeline.test.ts index 6d30a939e..a7cfa3f13 100644 --- a/test/timeline.test.ts +++ b/test/timeline.test.ts @@ -50,7 +50,7 @@ function runReadArgs(args: string[] = []): string { timeout: 15000, }; try { - return execFileSync(path.join(BIN, 'gstack-timeline-read'), args, execOpts).trim(); + return execFileSync(path.join(BIN, 'gstack-timeline-read'), args, execOpts).trim(); // timeout via execOpts } catch { return ''; }