diff --git a/packages/cli/src/utils/version-utils.ts b/packages/cli/src/utils/version-utils.ts index e9749e878..25a257633 100644 --- a/packages/cli/src/utils/version-utils.ts +++ b/packages/cli/src/utils/version-utils.ts @@ -20,11 +20,27 @@ export function getVersion() { export async function checkNewVersion() { const currVersion = getVersion(); let latestVersion: string; + // race against a ref'd, always-settling timer: if the fetch's connection dies in a + // way that strands its promise (leaving no active handle), the pending `await` would + // otherwise drain the event loop and silently exit the process with code 0 mid-command; + // the timer both keeps the loop alive and guarantees this await settles + let timer: NodeJS.Timeout | undefined; try { - latestVersion = await getLatestVersion(); + const fetchPromise = getLatestVersion(); + // if the timer wins the race, a later settlement of the fetch must not surface + // as an unhandled rejection + fetchPromise.catch(() => {}); + latestVersion = await Promise.race([ + fetchPromise, + new Promise((_, reject) => { + timer = setTimeout(() => reject(new Error('version check timed out')), CHECK_VERSION_TIMEOUT + 1000); + }), + ]); } catch { // noop return; + } finally { + clearTimeout(timer); } if (latestVersion && currVersion && semver.gt(latestVersion, currVersion)) { diff --git a/packages/cli/test/utils.ts b/packages/cli/test/utils.ts index 85d85559b..799d16838 100644 --- a/packages/cli/test/utils.ts +++ b/packages/cli/test/utils.ts @@ -117,5 +117,14 @@ export async function createProject( export function runCli(command: string, cwd: string) { const cli = path.join(__dirname, '../dist/index.mjs'); - execSync(`node ${cli} ${command}`, { cwd }); + try { + return execSync(`node ${cli} ${command}`, { cwd, encoding: 'utf8' }); + } catch (err: any) { + // surface the child's output in the test log — without this, CLI failures show up + // as bare assertion errors with no clue about what the CLI actually did + console.error(`[runCli] "${command}" failed in ${cwd} (status=${err.status}, signal=${err.signal})`); + console.error(`[runCli] stdout:\n${err.stdout}`); + console.error(`[runCli] stderr:\n${err.stderr}`); + throw err; + } }