0ee9723b52
resolveUpdaterBinary() picked up a staged hermes-setup on every platform, so a macOS binary predating the update hand-off protocol took over the update, held the marker, and had its `hermes update` child refuse its own parent. The in-app Update button then failed for good, with no route -- update, re-download or reinstall -- back to a capable binary (#74836). Move the decision into a pure resolveStagedUpdaterBinary() helper in updater-process.ts and return null off Windows. The installer self-copies into HERMES_HOME on every platform (paths::installer_dest, bootstrap::copy_self_to_hermes_home), so finding that binary on macOS or Linux is expected rather than leftover junk: declining to hand it an update is a policy decision, and the comments now say so instead of describing the binary as Windows-specific. Cover the resolver in updater-process.test.ts: Windows accepts a staged hermes-setup.exe, macOS/Linux return null even when hermes-setup exists, and Windows returns null when nothing is staged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
110 lines
2.8 KiB
TypeScript
110 lines
2.8 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import type { SpawnOptions } from 'node:child_process'
|
|
import path from 'node:path'
|
|
|
|
import { test } from 'vitest'
|
|
|
|
import { resolveStagedUpdaterBinary, spawnUpdaterProcess } from './updater-process'
|
|
|
|
test('spawnUpdaterProcess hides the updater console and detaches the child on Windows', () => {
|
|
const calls: Array<{ args: string[]; command: string; options: SpawnOptions }> = []
|
|
let unrefCalls = 0
|
|
|
|
const child = {
|
|
pid: 4242,
|
|
unref: () => {
|
|
unrefCalls += 1
|
|
}
|
|
}
|
|
|
|
const result = spawnUpdaterProcess(
|
|
'hermes-setup.exe',
|
|
['--update', '--branch', 'main'],
|
|
{ cwd: 'C:\\Hermes', detached: true, stdio: 'ignore' },
|
|
{
|
|
isWindows: true,
|
|
spawnProcess: (command, args, options) => {
|
|
calls.push({ args, command, options })
|
|
|
|
return child
|
|
}
|
|
}
|
|
)
|
|
|
|
assert.equal(result, child)
|
|
assert.equal(unrefCalls, 1)
|
|
assert.deepEqual(calls, [
|
|
{
|
|
args: ['--update', '--branch', 'main'],
|
|
command: 'hermes-setup.exe',
|
|
options: { cwd: 'C:\\Hermes', detached: true, stdio: 'ignore', windowsHide: true }
|
|
}
|
|
])
|
|
})
|
|
|
|
test('spawnUpdaterProcess preserves updater options off Windows', () => {
|
|
let capturedOptions: SpawnOptions | undefined
|
|
|
|
spawnUpdaterProcess(
|
|
'hermes-setup',
|
|
['--update'],
|
|
{ detached: true, stdio: 'ignore' },
|
|
{
|
|
isWindows: false,
|
|
spawnProcess: (_command, _args, options) => {
|
|
capturedOptions = options
|
|
|
|
return { unref: () => {} }
|
|
}
|
|
}
|
|
)
|
|
|
|
assert.deepEqual(capturedOptions, { detached: true, stdio: 'ignore' })
|
|
})
|
|
|
|
test('resolveStagedUpdaterBinary hands Windows the staged installer it finds', () => {
|
|
const home = 'C:\\Users\\hermes\\AppData\\Local\\hermes'
|
|
const staged = path.join(home, 'hermes-setup.exe')
|
|
const probed: string[] = []
|
|
|
|
const resolved = resolveStagedUpdaterBinary(home, {
|
|
fileExists: (candidate) => {
|
|
probed.push(candidate)
|
|
|
|
return candidate === staged
|
|
},
|
|
isWindows: true
|
|
})
|
|
|
|
assert.equal(resolved, staged)
|
|
assert.deepEqual(probed, [staged])
|
|
})
|
|
|
|
test('resolveStagedUpdaterBinary returns null off Windows even when hermes-setup is staged (#74836)', () => {
|
|
const home = '/Users/hermes/.hermes'
|
|
let probes = 0
|
|
|
|
const resolved = resolveStagedUpdaterBinary(home, {
|
|
// The installer stages hermes-setup on macOS/Linux too, so "it exists" is
|
|
// the normal case — and precisely the one that must not win.
|
|
fileExists: () => {
|
|
probes += 1
|
|
|
|
return true
|
|
},
|
|
isWindows: false
|
|
})
|
|
|
|
assert.equal(resolved, null)
|
|
assert.equal(probes, 0)
|
|
})
|
|
|
|
test('resolveStagedUpdaterBinary returns null on Windows when nothing is staged', () => {
|
|
const resolved = resolveStagedUpdaterBinary('C:\\Users\\hermes\\AppData\\Local\\hermes', {
|
|
fileExists: () => false,
|
|
isWindows: true
|
|
})
|
|
|
|
assert.equal(resolved, null)
|
|
})
|