fix: make Windows updater helper launch reliable
ForgeFlow quality gate / quality (push) Canceled after 0s
ForgeFlow quality gate / quality (push) Canceled after 0s
This commit is contained in:
@@ -6,16 +6,27 @@ import path from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { createHash } from "node:crypto";
|
||||
import { execFile } from "node:child_process";
|
||||
import { execFile, spawn } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
const require = createRequire(import.meta.url);
|
||||
const execFileAsync = promisify(execFile);
|
||||
const {
|
||||
UpdateService,
|
||||
waitForUpdaterStarted,
|
||||
windowsUpdaterSpawnOptions,
|
||||
} = require("../src/main/update-service.cjs");
|
||||
|
||||
test("Windows updater uses a hidden non-detached PowerShell child", () => {
|
||||
assert.deepEqual(windowsUpdaterSpawnOptions("C:\\updates"), {
|
||||
detached: false,
|
||||
stdio: "ignore",
|
||||
windowsHide: true,
|
||||
cwd: "C:\\updates",
|
||||
});
|
||||
});
|
||||
|
||||
test("update check pins version to an exact branch commit", async () => {
|
||||
const temp = await mkdtemp(path.join(os.tmpdir(), "forgeflow-update-test-"));
|
||||
const saved = [];
|
||||
@@ -358,6 +369,47 @@ test("binary helper confirms startup through real Windows PowerShell", { skip: p
|
||||
assert.match(await readFile(logPath, "utf8"), /Handshake-only verification completed successfully/);
|
||||
await rm(temp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test("binary helper confirms startup through the production Node spawn options", { skip: process.platform !== "win32" }, async () => {
|
||||
const temp = await mkdtemp(path.join(os.tmpdir(), "forgeflow-binary-node-spawn-"));
|
||||
const statusPath = path.join(temp, "status.json");
|
||||
const logPath = path.join(temp, "helper.log");
|
||||
const powershell = path.join(process.env.SystemRoot || process.env.WINDIR, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
||||
const scriptPath = fileURLToPath(new URL("../scripts/apply-binary-update.ps1", import.meta.url));
|
||||
const updateId = "binary-node-spawn";
|
||||
await writeFile(statusPath, JSON.stringify({ state: "launching", updateId }));
|
||||
const child = spawn(powershell, [
|
||||
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", scriptPath,
|
||||
"-BinaryPath", path.join(temp, "unused.exe"), "-ExpectedSha256", "0".repeat(64),
|
||||
"-ExpectedVersion", "9.9.9", "-CurrentExecutable", path.join(temp, "unused-current.exe"),
|
||||
"-Portable", "False", "-ParentPid", String(process.pid), "-LogPath", logPath,
|
||||
"-StatusPath", statusPath, "-UpdateId", updateId, "-HandshakeOnly",
|
||||
], windowsUpdaterSpawnOptions(temp));
|
||||
const childState = { exited: false, code: null, error: null };
|
||||
child.once("error", (error) => { childState.error = error; });
|
||||
child.once("exit", (code) => { childState.exited = true; childState.code = code; });
|
||||
const status = await waitForUpdaterStarted(statusPath, {
|
||||
timeoutMs: 5000,
|
||||
pollMs: 25,
|
||||
childState,
|
||||
expectedUpdateId: updateId,
|
||||
logPath,
|
||||
});
|
||||
assert.equal(status.state, "started");
|
||||
let log = "";
|
||||
for (let attempt = 0; attempt < 40 && !log.includes("Handshake-only verification completed successfully"); attempt += 1) {
|
||||
await delay(25);
|
||||
log = await readFile(logPath, "utf8").catch(() => "");
|
||||
}
|
||||
assert.match(log, /Handshake-only verification completed successfully/);
|
||||
if (child.exitCode === null) {
|
||||
await new Promise((resolve, reject) => {
|
||||
child.once("exit", resolve);
|
||||
child.once("error", reject);
|
||||
});
|
||||
}
|
||||
await rm(temp, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
||||
});
|
||||
test("early helper exit reports the helper log instead of only an exit code", async () => {
|
||||
const temp = await mkdtemp(
|
||||
path.join(os.tmpdir(), "forgeflow-update-log-tail-"),
|
||||
|
||||
Reference in New Issue
Block a user