scripts: update reproduction script with fix documentation
This commit is contained in:
@@ -10,10 +10,11 @@
|
|||||||
* Detection chain in packages/web/server/lib/opencode/env-runtime.js:
|
* Detection chain in packages/web/server/lib/opencode/env-runtime.js:
|
||||||
* Step 1: Check env vars (OPENCODE_BINARY, OPENCODE_PATH, etc.)
|
* Step 1: Check env vars (OPENCODE_BINARY, OPENCODE_PATH, etc.)
|
||||||
* Step 2: searchPathFor('opencode') — walks process.env.PATH
|
* Step 2: searchPathFor('opencode') — walks process.env.PATH
|
||||||
* Step 3: Hardcoded fallback paths (includes /opt/homebrew/bin/opencode,
|
* Step 3a: Hardcoded fallback paths (includes /opt/homebrew/bin/opencode,
|
||||||
* /usr/local/bin/opencode, etc.)
|
* /usr/local/bin/opencode, etc.)
|
||||||
|
* Step 3b: Fast-path: /bin/sh -c 'command -v opencode' (with timeout)
|
||||||
* Step 4: Shell probing — $SHELL -lic 'command -v opencode'
|
* Step 4: Shell probing — $SHELL -lic 'command -v opencode'
|
||||||
* (⚠️ NO TIMEOUT on spawnSync)
|
* (with timeout, fixed)
|
||||||
*
|
*
|
||||||
* On macOS, apps launched from the Dock/Finder inherit a minimal PATH:
|
* On macOS, apps launched from the Dock/Finder inherit a minimal PATH:
|
||||||
* /usr/bin:/bin:/usr/sbin:/sbin
|
* /usr/bin:/bin:/usr/sbin:/sbin
|
||||||
@@ -24,9 +25,9 @@
|
|||||||
* spawnSync($SHELL, ['-il', '-c', 'env -0'], { timeout: 5000 })
|
* spawnSync($SHELL, ['-il', '-c', 'env -0'], { timeout: 5000 })
|
||||||
*
|
*
|
||||||
* If the shell startup is slow (>5s due to nvm, pyenv, etc.), this times
|
* If the shell startup is slow (>5s due to nvm, pyenv, etc.), this times
|
||||||
* out and PATH stays minimal. The server-side probing (in index.js:652)
|
* out and PATH stays minimal. The fast-path (Step 3b) catches standard brew
|
||||||
* also calls spawnSync but with NO timeout at all — potentially hanging
|
* paths even with minimal PATH, and all shell probes now have a 5s timeout
|
||||||
* the module initialization indefinitely.
|
* to prevent blocking startup indefinitely.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
@@ -208,6 +209,30 @@ async function main() {
|
|||||||
if (ok) foundInFallbacks = true;
|
if (ok) foundInFallbacks = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3c-fp. Fast-path (command -v via /bin/sh)
|
||||||
|
console.log('\n 🔹 Step 3c-fp: Fast-path (command -v via /bin/sh)');
|
||||||
|
let fastPathFound = false;
|
||||||
|
try {
|
||||||
|
const fastResult = spawnSync('/bin/sh', ['-c', 'command -v opencode'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
|
timeout: 5000,
|
||||||
|
});
|
||||||
|
if (fastResult.status === 0) {
|
||||||
|
const found = (fastResult.stdout || '').trim().split(/\s+/).pop() || '';
|
||||||
|
if (found && isExecutable(found)) {
|
||||||
|
console.log(` ✓ Found: ${found}`);
|
||||||
|
fastPathFound = true;
|
||||||
|
} else {
|
||||||
|
console.log(` ✗ Not found (${found || 'empty'})`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(` ✗ Status ${fastResult.status} (${fastResult.error?.message || ''})`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(` ⚠ Exception: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
// 3d. Shell probing (last resort)
|
// 3d. Shell probing (last resort)
|
||||||
console.log('\n 🔹 Step 3d: Shell probing (last resort)');
|
console.log('\n 🔹 Step 3d: Shell probing (last resort)');
|
||||||
const shellCandidates = [
|
const shellCandidates = [
|
||||||
@@ -255,7 +280,7 @@ async function main() {
|
|||||||
|
|
||||||
section('Root Cause Analysis');
|
section('Root Cause Analysis');
|
||||||
|
|
||||||
const found = pathResult || foundInFallbacks || shellFound || (whichPath && isExecutable(whichPath));
|
const found = pathResult || foundInFallbacks || fastPathFound || shellFound || (whichPath && isExecutable(whichPath));
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
console.log(`
|
console.log(`
|
||||||
@@ -263,28 +288,36 @@ async function main() {
|
|||||||
this system. However, this does not rule out the bug on all
|
this system. However, this does not rule out the bug on all
|
||||||
macOS configurations.
|
macOS configurations.
|
||||||
|
|
||||||
⚠️ POTENTIAL ISSUES (macOS-specific):
|
🔧 FIXES APPLIED (issue #1720):
|
||||||
|
|
||||||
1. SHELL PROBING HAS NO TIMEOUT (env-runtime.js:366):
|
1. ✅ SHELL PROBING TIMEOUT (env-runtime.js):
|
||||||
The shell probing step at line 366 calls spawnSync without
|
All 4 shell probe spawnSync calls now have a 5-second
|
||||||
a timeout. If the user's shell startup is slow (>5s due to
|
timeout. Prevents indefinite blocking on slow shells.
|
||||||
nvm, pyenv, etc.), this blocks indefinitely.
|
|
||||||
|
|
||||||
2. ELECTRON PROBE HAS 5s TIMEOUT (main.mjs:1023):
|
2. ✅ FAST-PATH PROBE (env-runtime.js):
|
||||||
The Electron main process probes the shell with a 5-second
|
Added /bin/sh -c 'command -v opencode' before the full
|
||||||
timeout. If the shell takes >5s, PATH is NOT augmented.
|
login shell probe. Catches brew binaries at ~10ms instead
|
||||||
|
of potentially seconds. (Standard brew paths are also
|
||||||
|
covered by hardcoded fallbacks.)
|
||||||
|
|
||||||
3. SERVER-SIDE SHELL PROBE HAS NO TIMEOUT (env-runtime.js:205):
|
3. ✅ TOOLCHAIN_SEGMENTS (path-utils.js):
|
||||||
The server's getLoginShellEnvSnapshot() also has no timeout,
|
Added '/usr/local/' to TOOLCHAIN_SEGMENTS so a PATH
|
||||||
potentially blocking module initialization at index.js:652.
|
containing /usr/local/bin is recognized as user-configured.
|
||||||
|
|
||||||
4. FILE-SYSTEM PERMISSION ISSUE:
|
4. ✅ VS CODE FALLBACK ORDER (opencode.ts):
|
||||||
On macOS with Full Disk Protection, the Electron sandbox
|
Brew fallback order now matches server: /opt/homebrew
|
||||||
may restrict access to files outside the app container.
|
(Apple Silicon) before /usr/local (Intel).
|
||||||
|
|
||||||
5. BREW INSTALLATION PATH NOT COVERED:
|
⚠️ REMAINING ISSUES (not addressed by this fix):
|
||||||
If brew is installed at a custom prefix (not /opt/homebrew
|
|
||||||
or /usr/local), the hardcoded fallbacks won't match.
|
1. FILE-SYSTEM PERMISSION ISSUE:
|
||||||
|
On macOS with Full Disk Protection, the Electron sandbox
|
||||||
|
may restrict access to files outside the app container.
|
||||||
|
|
||||||
|
2. CUSTOM BREW PREFIX:
|
||||||
|
If brew is installed at a custom prefix (not /opt/homebrew
|
||||||
|
or /usr/local), the hardcoded fallbacks won't match — but
|
||||||
|
the fast-path (command -v via sh) may still find it.
|
||||||
`);
|
`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`
|
console.log(`
|
||||||
@@ -308,14 +341,14 @@ async function main() {
|
|||||||
|
|
||||||
console.log(`
|
console.log(`
|
||||||
──────────────────────────────────────────────────────────
|
──────────────────────────────────────────────────────────
|
||||||
Recommended fixes:
|
Fixes applied:
|
||||||
1. Add a timeout to ALL spawnSync calls in the detection chain
|
1. ✅ All 4 shell probe spawnSync calls now have a 5s timeout
|
||||||
(especially env-runtime.js lines 205, 366)
|
(SHELL_PROBE_TIMEOUT_MS, added at env-runtime.js module level)
|
||||||
2. Consider using 'which opencode' or 'command -v opencode'
|
2. ✅ Fast-path via /bin/sh -c 'command -v opencode' added
|
||||||
as a standalone fallback with a short timeout
|
before login shell probing in all 3 resolvers
|
||||||
3. The existing hardcoded paths DO cover standard brew paths,
|
3. ✅ /usr/local/ added to TOOLCHAIN_SEGMENTS in path-utils.js
|
||||||
so if the binary is at /opt/homebrew/bin/opencode or
|
4. ✅ Brew fallback order fixed in VS Code extension
|
||||||
/usr/local/bin/opencode, detection should work
|
(/opt/homebrew before /usr/local)
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user