From a44465d96e261dca713b1288ad82162587d140c2 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Fri, 22 May 2026 16:17:18 -0700 Subject: [PATCH] Improve binary spawn errors --- cli/release/index.js | 83 ++++++++++++++++++++++++++++------- freebuff/cli/release/index.js | 83 ++++++++++++++++++++++++++++------- 2 files changed, 134 insertions(+), 32 deletions(-) diff --git a/cli/release/index.js b/cli/release/index.js index f84e6940c8..bf1eead545 100644 --- a/cli/release/index.js +++ b/cli/release/index.js @@ -490,10 +490,7 @@ async function checkForUpdates(runningProcess, exitListener) { await downloadBinary(latestVersion) - const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), { - stdio: 'inherit', - detached: false, - }) + const newChild = spawnInstalledBinary({ detached: false }) newChild.on('exit', (code, signal) => { resetTerminal() @@ -501,11 +498,6 @@ async function checkForUpdates(runningProcess, exitListener) { process.exit(signal ? 1 : (code || 0)) }) - newChild.on('error', (err) => { - console.error('Failed to start codebuff:', err.message) - process.exit(1) - }) - return new Promise(() => {}) } } catch (error) { @@ -561,13 +553,77 @@ function printCrashDiagnostics(code, signal) { console.error('') } -async function main() { - await ensureBinaryExists() +function getInstalledBinaryStatus() { + try { + const stats = fs.statSync(CONFIG.binaryPath) + return stats.isFile() ? `yes (${formatBytes(stats.size)})` : 'no' + } catch { + return 'no' + } +} + +function printSpawnFailure(err) { + resetTerminal() + const code = err && err.code ? ` (${err.code})` : '' + + console.error(`Failed to start ${packageName}: ${err.message}${code}`) + console.error('') + console.error('System info:') + console.error(` Platform: ${process.platform} ${process.arch}`) + console.error(` Node: ${process.version}`) + console.error(` Binary: ${CONFIG.binaryPath}`) + console.error(` Exists: ${getInstalledBinaryStatus()}`) + + if (process.platform === 'win32') { + console.error('') + console.error( + 'On Windows, this can happen when Windows Security or antivirus blocks', + ) + console.error( + 'or quarantines the downloaded executable, or when the binary requires', + ) + console.error('CPU instructions that are not available on this machine.') + } + + console.error('') + console.error('Try deleting the downloaded files and running again:') + console.error(` ${CONFIG.configDir}`) + console.error('') +} + +function spawnInstalledBinary(options = {}) { + if (!fs.existsSync(CONFIG.binaryPath)) { + try { + if (fs.existsSync(CONFIG.metadataPath)) fs.unlinkSync(CONFIG.metadataPath) + } catch { + // best effort + } + const error = new Error( + `downloaded binary is missing at ${CONFIG.binaryPath}`, + ) + error.code = 'BINARY_MISSING' + printSpawnFailure(error) + process.exit(1) + } const child = spawn(CONFIG.binaryPath, process.argv.slice(2), { stdio: 'inherit', + ...options, + }) + + child.on('error', (err) => { + printSpawnFailure(err) + process.exit(1) }) + return child +} + +async function main() { + await ensureBinaryExists() + + const child = spawnInstalledBinary() + const exitListener = (code, signal) => { resetTerminal() printCrashDiagnostics(code, signal) @@ -576,11 +632,6 @@ async function main() { child.on('exit', exitListener) - child.on('error', (err) => { - console.error('Failed to start codebuff:', err.message) - process.exit(1) - }) - setTimeout(() => { checkForUpdates(child, exitListener) }, 100) diff --git a/freebuff/cli/release/index.js b/freebuff/cli/release/index.js index 044d86ebc5..ca853b83fb 100644 --- a/freebuff/cli/release/index.js +++ b/freebuff/cli/release/index.js @@ -477,10 +477,7 @@ async function checkForUpdates(runningProcess, exitListener) { await downloadBinary(latestVersion) - const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), { - stdio: 'inherit', - detached: false, - }) + const newChild = spawnInstalledBinary({ detached: false }) newChild.on('exit', (code, signal) => { resetTerminal() @@ -488,11 +485,6 @@ async function checkForUpdates(runningProcess, exitListener) { process.exit(signal ? 1 : (code || 0)) }) - newChild.on('error', (err) => { - console.error('Failed to start freebuff:', err.message) - process.exit(1) - }) - return new Promise(() => {}) } } catch (error) { @@ -548,13 +540,77 @@ function printCrashDiagnostics(code, signal) { console.error('') } -async function main() { - await ensureBinaryExists() +function getInstalledBinaryStatus() { + try { + const stats = fs.statSync(CONFIG.binaryPath) + return stats.isFile() ? `yes (${formatBytes(stats.size)})` : 'no' + } catch { + return 'no' + } +} + +function printSpawnFailure(err) { + resetTerminal() + const code = err && err.code ? ` (${err.code})` : '' + + console.error(`Failed to start ${packageName}: ${err.message}${code}`) + console.error('') + console.error('System info:') + console.error(` Platform: ${process.platform} ${process.arch}`) + console.error(` Node: ${process.version}`) + console.error(` Binary: ${CONFIG.binaryPath}`) + console.error(` Exists: ${getInstalledBinaryStatus()}`) + + if (process.platform === 'win32') { + console.error('') + console.error( + 'On Windows, this can happen when Windows Security or antivirus blocks', + ) + console.error( + 'or quarantines the downloaded executable, or when the binary requires', + ) + console.error('CPU instructions that are not available on this machine.') + } + + console.error('') + console.error('Try deleting the downloaded files and running again:') + console.error(` ${CONFIG.configDir}`) + console.error('') +} + +function spawnInstalledBinary(options = {}) { + if (!fs.existsSync(CONFIG.binaryPath)) { + try { + if (fs.existsSync(CONFIG.metadataPath)) fs.unlinkSync(CONFIG.metadataPath) + } catch { + // best effort + } + const error = new Error( + `downloaded binary is missing at ${CONFIG.binaryPath}`, + ) + error.code = 'BINARY_MISSING' + printSpawnFailure(error) + process.exit(1) + } const child = spawn(CONFIG.binaryPath, process.argv.slice(2), { stdio: 'inherit', + ...options, + }) + + child.on('error', (err) => { + printSpawnFailure(err) + process.exit(1) }) + return child +} + +async function main() { + await ensureBinaryExists() + + const child = spawnInstalledBinary() + const exitListener = (code, signal) => { resetTerminal() printCrashDiagnostics(code, signal) @@ -563,11 +619,6 @@ async function main() { child.on('exit', exitListener) - child.on('error', (err) => { - console.error('Failed to start freebuff:', err.message) - process.exit(1) - }) - setTimeout(() => { checkForUpdates(child, exitListener) }, 100)