fix(desktop): cancel bootstrap manifest work during quit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
|
||||
import { test } from 'vitest'
|
||||
|
||||
import { runBootstrap } from './bootstrap-runner'
|
||||
|
||||
for (const boundary of ['resolution', 'manifest'] as const) {
|
||||
test.skipIf(process.platform === 'win32')(`quit during ${boundary} cancels bootstrap before stages`, async () => {
|
||||
const home = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-bootstrap-quit-'))
|
||||
const controller = new AbortController()
|
||||
const marker = path.join(home, 'manifest-started')
|
||||
let manifestPid: number | undefined
|
||||
|
||||
fs.mkdirSync(path.join(home, 'scripts'))
|
||||
fs.writeFileSync(
|
||||
path.join(home, 'scripts/install.sh'),
|
||||
`#!/bin/bash\nprintf started > "$HERMES_HOME/manifest-started"\nprintf 'manifest-pid=%s\\n' "$$"\nwhile :; do :; done\n`
|
||||
)
|
||||
|
||||
try {
|
||||
const result = await runBootstrap({
|
||||
installStamp: null,
|
||||
activeRoot: path.join(home, 'agent'),
|
||||
sourceRepoRoot: home,
|
||||
hermesHome: home,
|
||||
abortSignal: controller.signal,
|
||||
onEvent: event => {
|
||||
if (boundary === 'resolution' && event.line?.includes('using local')) {
|
||||
controller.abort()
|
||||
}
|
||||
|
||||
const match = event.line?.match(/^manifest-pid=(\d+)$/)
|
||||
|
||||
if (match) {
|
||||
manifestPid = Number(match[1])
|
||||
controller.abort()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
assert.equal(result.ok, false)
|
||||
assert.equal(result.cancelled, true)
|
||||
assert.equal(fs.existsSync(marker), boundary === 'manifest')
|
||||
|
||||
if (manifestPid) {
|
||||
assert.throws(() => process.kill(manifestPid!, 0))
|
||||
}
|
||||
} finally {
|
||||
controller.abort()
|
||||
|
||||
if (manifestPid) {
|
||||
try {
|
||||
process.kill(manifestPid, 'SIGKILL')
|
||||
} catch {
|
||||
/* already exited */
|
||||
}
|
||||
}
|
||||
|
||||
fs.rmSync(home, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -690,7 +690,17 @@ function buildPosixPinArgs({ installStamp, activeRoot, hermesHome, pinCommit = t
|
||||
return args
|
||||
}
|
||||
|
||||
async function fetchManifest({ scriptPath, installerKind, emit, hermesHome, activeRoot, installStamp, pinCommit }) {
|
||||
async function fetchManifest({
|
||||
scriptPath,
|
||||
installerKind,
|
||||
emit,
|
||||
hermesHome,
|
||||
activeRoot,
|
||||
installStamp,
|
||||
pinCommit,
|
||||
abortSignal
|
||||
}) {
|
||||
abortSignal?.throwIfAborted()
|
||||
const isPosix = installerKind === 'posix'
|
||||
|
||||
const args = isPosix
|
||||
@@ -700,6 +710,7 @@ async function fetchManifest({ scriptPath, installerKind, emit, hermesHome, acti
|
||||
const result = await (isPosix ? spawnBash : spawnPowerShell)(scriptPath, args, {
|
||||
emit,
|
||||
stageName: '__manifest__',
|
||||
abortSignal,
|
||||
hermesHome
|
||||
})
|
||||
|
||||
@@ -928,6 +939,8 @@ async function runBootstrap(opts) {
|
||||
|
||||
// 1. Resolve the platform installer.
|
||||
const scriptInfo = await resolveInstallScript({ installStamp, sourceRepoRoot, hermesHome, emit })
|
||||
abortSignal?.throwIfAborted()
|
||||
|
||||
const installerKind = scriptInfo.kind || 'powershell'
|
||||
|
||||
// 2. Fetch manifest
|
||||
@@ -938,9 +951,12 @@ async function runBootstrap(opts) {
|
||||
hermesHome,
|
||||
activeRoot,
|
||||
installStamp,
|
||||
pinCommit
|
||||
pinCommit,
|
||||
abortSignal
|
||||
})
|
||||
|
||||
abortSignal?.throwIfAborted()
|
||||
|
||||
emit({
|
||||
type: 'manifest',
|
||||
stages: manifest.stages,
|
||||
@@ -1007,12 +1023,18 @@ async function runBootstrap(opts) {
|
||||
|
||||
return { ok: true, marker }
|
||||
} catch (err) {
|
||||
if (abortSignal?.aborted) {
|
||||
emit({ type: 'failed', error: 'bootstrap cancelled by user' })
|
||||
|
||||
return { ok: false, cancelled: true }
|
||||
}
|
||||
|
||||
emit({ type: 'failed', error: err.message || String(err) })
|
||||
|
||||
return { ok: false, error: err.message || String(err) }
|
||||
} finally {
|
||||
try {
|
||||
runLog.stream.end()
|
||||
await new Promise<void>(resolve => runLog.stream.end(resolve))
|
||||
} catch {
|
||||
void 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user