fix(installer): avoid empty-version success on delayed stdout
This commit is contained in:
@@ -140,8 +140,8 @@ describe("getOpenCodeVersion (installer)", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("#given quick successful exit #when getOpenCodeVersion #then clears the watchdog timer", () => {
|
describe("#given quick successful exit #when getOpenCodeVersion #then clears active timers", () => {
|
||||||
it("avoids timer leak after success", async () => {
|
it("avoids timer leaks after success", async () => {
|
||||||
spawnSpy.mockReturnValue(createProc({ output: { stdout: "1.14.33\n" } }))
|
spawnSpy.mockReturnValue(createProc({ output: { stdout: "1.14.33\n" } }))
|
||||||
|
|
||||||
const clearTimeoutSpy = spyOn(globalThis, "clearTimeout")
|
const clearTimeoutSpy = spyOn(globalThis, "clearTimeout")
|
||||||
@@ -149,7 +149,7 @@ describe("getOpenCodeVersion (installer)", () => {
|
|||||||
const result = await getOpenCodeVersion()
|
const result = await getOpenCodeVersion()
|
||||||
|
|
||||||
expect(result).toBe("1.14.33")
|
expect(result).toBe("1.14.33")
|
||||||
expect(clearTimeoutSpy).toHaveBeenCalledTimes(1)
|
expect(clearTimeoutSpy).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
clearTimeoutSpy.mockRestore()
|
clearTimeoutSpy.mockRestore()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,15 +23,16 @@ async function findOpenCodeBinaryWithVersion(): Promise<OpenCodeBinaryResult | n
|
|||||||
|
|
||||||
const outputPromise = new Response(proc.stdout).text()
|
const outputPromise = new Response(proc.stdout).text()
|
||||||
let killTimer: ReturnType<typeof setTimeout> | null = null
|
let killTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
const timedExitCode = await Promise.race([
|
let killGraceTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
proc.exited,
|
const timedExitResult = await Promise.race([
|
||||||
new Promise<number>((resolve) => {
|
proc.exited.then((exitCode) => ({ type: "exit" as const, exitCode })),
|
||||||
|
new Promise<{ type: "timeout" }>((resolve) => {
|
||||||
killTimer = setTimeout(() => {
|
killTimer = setTimeout(() => {
|
||||||
proc.kill("SIGTERM")
|
proc.kill("SIGTERM")
|
||||||
setTimeout(() => {
|
killGraceTimer = setTimeout(() => {
|
||||||
proc.kill("SIGKILL")
|
proc.kill("SIGKILL")
|
||||||
}, OPENCODE_VERSION_KILL_GRACE_MS)
|
}, OPENCODE_VERSION_KILL_GRACE_MS)
|
||||||
resolve(1)
|
resolve({ type: "timeout" })
|
||||||
}, OPENCODE_VERSION_CHECK_TIMEOUT_MS)
|
}, OPENCODE_VERSION_CHECK_TIMEOUT_MS)
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
@@ -40,17 +41,40 @@ async function findOpenCodeBinaryWithVersion(): Promise<OpenCodeBinaryResult | n
|
|||||||
clearTimeout(killTimer)
|
clearTimeout(killTimer)
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = await Promise.race([
|
if (timedExitResult.type === "timeout") {
|
||||||
outputPromise,
|
void outputPromise.catch(() => {})
|
||||||
new Promise<string>((resolve) => {
|
continue
|
||||||
setTimeout(() => {
|
}
|
||||||
resolve("")
|
|
||||||
|
if (killGraceTimer) {
|
||||||
|
clearTimeout(killGraceTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
let outputTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
const outputResult = await Promise.race([
|
||||||
|
outputPromise.then((output) => ({ type: "output" as const, output })),
|
||||||
|
new Promise<{ type: "timeout" }>((resolve) => {
|
||||||
|
outputTimer = setTimeout(() => {
|
||||||
|
resolve({ type: "timeout" })
|
||||||
}, OPENCODE_OUTPUT_WAIT_TIMEOUT_MS)
|
}, OPENCODE_OUTPUT_WAIT_TIMEOUT_MS)
|
||||||
}),
|
}),
|
||||||
]).catch(() => "")
|
]).catch(() => ({ type: "timeout" as const }))
|
||||||
|
|
||||||
if (timedExitCode === 0 && proc.exitCode === 0) {
|
if (outputTimer) {
|
||||||
|
clearTimeout(outputTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputResult.type !== "output") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timedExitResult.exitCode === 0 && proc.exitCode === 0) {
|
||||||
|
const output = outputResult.output
|
||||||
const version = extractSemverFromOutput(output) ?? output.trim()
|
const version = extractSemverFromOutput(output) ?? output.trim()
|
||||||
|
if (version.length === 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
initConfigContext(binary, version)
|
initConfigContext(binary, version)
|
||||||
return { binary, version }
|
return { binary, version }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user