fix(scripts): keep the first proxy error in the package-update verifier (#6362)
The registry fixture sent response headers before reading the upstream body, then always wrote a 500 on error. A body failure hit ERR_HTTP_HEADERS_SENT, which hid the real error and crashed the verifier with its helper still running. The fixture now reads the body first, destroys a response that already started, and the verifier rethrows the first fixture error. Refs #6038. 🤖 Generated with Claude Code (Sonnet 5.5) 🦊 Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
371f3d91ae
commit
0f09630645
2 files changed
+109
-1
No files matched your search
@@ -24,8 +24,14 @@ export async function verifyPackageUpdate({
|
||||
'[mcp_servers.rea]\ncommand = "npx"\nargs = ["-y", "rea-agents@0.0.0", "mcp"]\n';
|
||||
await mkdir(dirname(codexPath), { recursive: true });
|
||||
await writeFile(codexPath, originalConfig);
|
||||
let fixtureFailure;
|
||||
const server = createServer((request, response) => {
|
||||
void serveRegistry(request, response).catch((error) => {
|
||||
fixtureFailure ??= error;
|
||||
if (response.headersSent) {
|
||||
response.destroy();
|
||||
return;
|
||||
}
|
||||
response.writeHead(500);
|
||||
response.end(String(error));
|
||||
});
|
||||
@@ -57,11 +63,12 @@ export async function verifyPackageUpdate({
|
||||
response.end("{}");
|
||||
} else {
|
||||
const upstream = await fetch(`https://registry.npmjs.org${request.url}`);
|
||||
const body = Buffer.from(await upstream.arrayBuffer());
|
||||
response.writeHead(upstream.status, {
|
||||
"Content-Type":
|
||||
upstream.headers.get("content-type") ?? "application/octet-stream",
|
||||
});
|
||||
response.end(Buffer.from(await upstream.arrayBuffer()));
|
||||
response.end(body);
|
||||
}
|
||||
}
|
||||
await new Promise((resolveListen, reject) => {
|
||||
@@ -171,11 +178,14 @@ if (result.status === "failed") process.exitCode = 1;
|
||||
throw new Error(
|
||||
"Updated CLI did not expose update as its sole maintenance command",
|
||||
);
|
||||
if (fixtureFailure !== undefined) throw fixtureFailure;
|
||||
return {
|
||||
installed_version: result.installedVersion,
|
||||
exact_release: true,
|
||||
maintenance_read_only: true,
|
||||
};
|
||||
} catch (error) {
|
||||
throw fixtureFailure ?? error;
|
||||
} finally {
|
||||
server.closeAllConnections();
|
||||
await new Promise((resolveClose, reject) =>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { join, resolve } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const injectedFailure = "injected upstream body failure";
|
||||
|
||||
it("reports the upstream body failure instead of a second header write", async () => {
|
||||
const testRoot = await createTestTempDirectory("rea-update-registry-");
|
||||
const packageRoot = join(testRoot, "package");
|
||||
const workspace = join(testRoot, "workspace");
|
||||
const prefix = join(testRoot, "prefix");
|
||||
const tarball = join(testRoot, "release.tgz");
|
||||
const application = join(packageRoot, "dist", "application");
|
||||
await Promise.all([
|
||||
mkdir(application, { recursive: true }),
|
||||
mkdir(join(packageRoot, "scripts"), { recursive: true }),
|
||||
mkdir(workspace, { recursive: true }),
|
||||
mkdir(prefix, { recursive: true }),
|
||||
]);
|
||||
await Promise.all([
|
||||
writeFile(tarball, "archive"),
|
||||
writeFile(
|
||||
join(packageRoot, "package.json"),
|
||||
JSON.stringify({ name: "rea-agents", version: "6.0.0" }),
|
||||
),
|
||||
writeFile(
|
||||
join(packageRoot, "dist", "generatedPackageMetadata.js"),
|
||||
'export const generated = { version: "6.0.0" };\n',
|
||||
),
|
||||
writeFile(
|
||||
join(packageRoot, "scripts", "rea.mjs"),
|
||||
[
|
||||
'import { readFileSync } from "node:fs";',
|
||||
'const metadata = new URL("../package.json", import.meta.url);',
|
||||
"console.log(JSON.parse(readFileSync(metadata, 'utf8')).version);",
|
||||
"",
|
||||
].join("\n"),
|
||||
),
|
||||
writeFile(
|
||||
join(application, "Update.js"),
|
||||
[
|
||||
"export async function runUpdate() {",
|
||||
" const registry = process.env.npm_config_registry;",
|
||||
" const response = await fetch(`${registry}/unlisted`, {",
|
||||
" signal: AbortSignal.timeout(5000),",
|
||||
" });",
|
||||
" const body = await response.text();",
|
||||
" throw new Error(`registry fixture answered ${response.status}: ${body}`);",
|
||||
"}",
|
||||
"",
|
||||
].join("\n"),
|
||||
),
|
||||
writeFile(
|
||||
join(application, "UpdateRuntime.js"),
|
||||
"export const systemUpdateHost = () => ({});\n",
|
||||
),
|
||||
]);
|
||||
const verifierUrl = pathToFileURL(
|
||||
resolve("scripts/verify-package-update.mjs"),
|
||||
).href;
|
||||
const harness = `
|
||||
globalThis.fetch = async () => ({
|
||||
status: 200,
|
||||
headers: new Headers({ "content-type": "application/json" }),
|
||||
arrayBuffer: async () => {
|
||||
throw new Error(${JSON.stringify(injectedFailure)});
|
||||
},
|
||||
});
|
||||
const { verifyPackageUpdate } = await import(${JSON.stringify(verifierUrl)});
|
||||
let failure;
|
||||
try {
|
||||
await verifyPackageUpdate({
|
||||
prefix: ${JSON.stringify(prefix)},
|
||||
packageRoot: ${JSON.stringify(packageRoot)},
|
||||
tarball: ${JSON.stringify(tarball)},
|
||||
workspace: ${JSON.stringify(workspace)},
|
||||
environment: { PATH: process.env.PATH },
|
||||
});
|
||||
} catch (cause) {
|
||||
failure = cause instanceof Error ? cause.message : String(cause);
|
||||
}
|
||||
process.stdout.write(JSON.stringify({ failure }));
|
||||
`;
|
||||
const { stdout } = await execFileAsync(
|
||||
process.execPath,
|
||||
["--input-type=module", "--eval", harness],
|
||||
{ env: { ...process.env }, maxBuffer: 1_024 * 1_024, timeout: 30_000 },
|
||||
);
|
||||
|
||||
expect(JSON.parse(stdout)).toEqual({ failure: injectedFailure });
|
||||
}, 45_000);
|
||||
Reference in new issue
Block a user