From 960c21baf9556696bede1e668f2150b0732c31b3 Mon Sep 17 00:00:00 2001 From: usr-bin-roygbiv Date: Mon, 3 Aug 2026 06:49:19 +0000 Subject: [PATCH 1/5] fix(utils): honor current PATH in which lookups --- packages/utils/src/which.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/utils/src/which.ts b/packages/utils/src/which.ts index e1facff59..718e629c0 100644 --- a/packages/utils/src/which.ts +++ b/packages/utils/src/which.ts @@ -183,8 +183,8 @@ export interface WhichOptions extends Bun.WhichOptions { // Darwin-specific "which" shim: consult Xcode/CLT toolchain directories after $PATH. // Uses cached directory listings instead of per-command existsSync or xcrun subprocesses. -function darwinWhich(command: string, _options?: Bun.WhichOptions): string | null { - const regular = Bun.which(command); +function darwinWhich(command: string, options?: Bun.WhichOptions): string | null { + const regular = Bun.which(command, options); if (regular) return regular; if (isXcodeBin(command)) { return getMacosToolPaths().get(command) ?? null; @@ -214,17 +214,21 @@ function cacheKey(command: string, options?: Bun.WhichOptions): CacheKey { */ export function $which(command: string, options?: WhichOptions): string | null { const cachePolicy = options?.cache ?? WhichCachePolicy.Cached; + const lookupOptions = + options?.PATH !== undefined || process.env.PATH === undefined + ? options + : { ...options, PATH: process.env.PATH }; let key: CacheKey | undefined; if (cachePolicy !== WhichCachePolicy.Bypass) { - key = cacheKey(command, options); + key = cacheKey(command, lookupOptions); if (cachePolicy !== WhichCachePolicy.Fresh) { const cached = toolCache.get(key); if (cached !== undefined) return cached; } } - const result = whichFresh(command, options); + const result = whichFresh(command, lookupOptions); if (key != null && cachePolicy !== WhichCachePolicy.ReadOnly) { toolCache.set(key, result); } From 55a5574bddb91ab2f5ba30b15ed17a0a3f99feeb Mon Sep 17 00:00:00 2001 From: usr-bin-roygbiv Date: Mon, 3 Aug 2026 12:45:29 +0000 Subject: [PATCH 2/5] Revert "fix(utils): honor current PATH in which lookups" This reverts commit 960c21baf9556696bede1e668f2150b0732c31b3. --- packages/utils/src/which.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/utils/src/which.ts b/packages/utils/src/which.ts index 718e629c0..e1facff59 100644 --- a/packages/utils/src/which.ts +++ b/packages/utils/src/which.ts @@ -183,8 +183,8 @@ export interface WhichOptions extends Bun.WhichOptions { // Darwin-specific "which" shim: consult Xcode/CLT toolchain directories after $PATH. // Uses cached directory listings instead of per-command existsSync or xcrun subprocesses. -function darwinWhich(command: string, options?: Bun.WhichOptions): string | null { - const regular = Bun.which(command, options); +function darwinWhich(command: string, _options?: Bun.WhichOptions): string | null { + const regular = Bun.which(command); if (regular) return regular; if (isXcodeBin(command)) { return getMacosToolPaths().get(command) ?? null; @@ -214,21 +214,17 @@ function cacheKey(command: string, options?: Bun.WhichOptions): CacheKey { */ export function $which(command: string, options?: WhichOptions): string | null { const cachePolicy = options?.cache ?? WhichCachePolicy.Cached; - const lookupOptions = - options?.PATH !== undefined || process.env.PATH === undefined - ? options - : { ...options, PATH: process.env.PATH }; let key: CacheKey | undefined; if (cachePolicy !== WhichCachePolicy.Bypass) { - key = cacheKey(command, lookupOptions); + key = cacheKey(command, options); if (cachePolicy !== WhichCachePolicy.Fresh) { const cached = toolCache.get(key); if (cached !== undefined) return cached; } } - const result = whichFresh(command, lookupOptions); + const result = whichFresh(command, options); if (key != null && cachePolicy !== WhichCachePolicy.ReadOnly) { toolCache.set(key, result); } From 71a4c69369ed1f63f8fe2d62a4fbc499cae735b3 Mon Sep 17 00:00:00 2001 From: usr-bin-roygbiv Date: Mon, 3 Aug 2026 12:45:43 +0000 Subject: [PATCH 3/5] test(utils): reproduce stale PATH lookup cache --- packages/utils/test/which.test.ts | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 packages/utils/test/which.test.ts diff --git a/packages/utils/test/which.test.ts b/packages/utils/test/which.test.ts new file mode 100644 index 000000000..e054975c2 --- /dev/null +++ b/packages/utils/test/which.test.ts @@ -0,0 +1,37 @@ +import { afterEach, describe, expect, it } from "bun:test"; +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { $which } from "../src/which"; + +describe("$which", () => { + const originalPath = process.env.PATH; + const tempDirs: string[] = []; + + afterEach(() => { + process.env.PATH = originalPath; + for (const dir of tempDirs.splice(0)) { + fs.rmSync(dir, { recursive: true, force: true }); + } + }); + + it.skipIf(process.platform === "win32")("uses the current process PATH for each cached lookup", () => { + const firstDir = fs.mkdtempSync(path.join(os.tmpdir(), "omp-which-first-")); + const secondDir = fs.mkdtempSync(path.join(os.tmpdir(), "omp-which-second-")); + tempDirs.push(firstDir, secondDir); + + const command = `omp-which-${process.pid}`; + const firstExecutable = path.join(firstDir, command); + const secondExecutable = path.join(secondDir, command); + fs.writeFileSync(firstExecutable, "#!/bin/sh\n"); + fs.writeFileSync(secondExecutable, "#!/bin/sh\n"); + fs.chmodSync(firstExecutable, 0o755); + fs.chmodSync(secondExecutable, 0o755); + + process.env.PATH = firstDir; + expect($which(command)).toBe(firstExecutable); + + process.env.PATH = secondDir; + expect($which(command)).toBe(secondExecutable); + }); +}); From a5f5f53c357b9a3019ae2445f24e41067c07b968 Mon Sep 17 00:00:00 2001 From: usr-bin-roygbiv Date: Mon, 3 Aug 2026 06:49:19 +0000 Subject: [PATCH 4/5] fix(utils): honor current PATH in which lookups --- packages/utils/src/which.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/utils/src/which.ts b/packages/utils/src/which.ts index e1facff59..718e629c0 100644 --- a/packages/utils/src/which.ts +++ b/packages/utils/src/which.ts @@ -183,8 +183,8 @@ export interface WhichOptions extends Bun.WhichOptions { // Darwin-specific "which" shim: consult Xcode/CLT toolchain directories after $PATH. // Uses cached directory listings instead of per-command existsSync or xcrun subprocesses. -function darwinWhich(command: string, _options?: Bun.WhichOptions): string | null { - const regular = Bun.which(command); +function darwinWhich(command: string, options?: Bun.WhichOptions): string | null { + const regular = Bun.which(command, options); if (regular) return regular; if (isXcodeBin(command)) { return getMacosToolPaths().get(command) ?? null; @@ -214,17 +214,21 @@ function cacheKey(command: string, options?: Bun.WhichOptions): CacheKey { */ export function $which(command: string, options?: WhichOptions): string | null { const cachePolicy = options?.cache ?? WhichCachePolicy.Cached; + const lookupOptions = + options?.PATH !== undefined || process.env.PATH === undefined + ? options + : { ...options, PATH: process.env.PATH }; let key: CacheKey | undefined; if (cachePolicy !== WhichCachePolicy.Bypass) { - key = cacheKey(command, options); + key = cacheKey(command, lookupOptions); if (cachePolicy !== WhichCachePolicy.Fresh) { const cached = toolCache.get(key); if (cached !== undefined) return cached; } } - const result = whichFresh(command, options); + const result = whichFresh(command, lookupOptions); if (key != null && cachePolicy !== WhichCachePolicy.ReadOnly) { toolCache.set(key, result); } From 1bb5b445fe4319136911567e2f225d3fe2f185b4 Mon Sep 17 00:00:00 2001 From: usr-bin-roygbiv Date: Mon, 3 Aug 2026 13:03:35 +0000 Subject: [PATCH 5/5] style(utils): format effective PATH lookup --- packages/utils/src/which.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/utils/src/which.ts b/packages/utils/src/which.ts index 718e629c0..327c60fb4 100644 --- a/packages/utils/src/which.ts +++ b/packages/utils/src/which.ts @@ -215,9 +215,7 @@ function cacheKey(command: string, options?: Bun.WhichOptions): CacheKey { export function $which(command: string, options?: WhichOptions): string | null { const cachePolicy = options?.cache ?? WhichCachePolicy.Cached; const lookupOptions = - options?.PATH !== undefined || process.env.PATH === undefined - ? options - : { ...options, PATH: process.env.PATH }; + options?.PATH !== undefined || process.env.PATH === undefined ? options : { ...options, PATH: process.env.PATH }; let key: CacheKey | undefined; if (cachePolicy !== WhichCachePolicy.Bypass) {