mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 02:12:59 +00:00
fix(release): stream cross-os served artifacts
This commit is contained in:
@@ -6,6 +6,7 @@ import { spawn } from "node:child_process";
|
||||
import {
|
||||
appendFileSync,
|
||||
chmodSync,
|
||||
createReadStream,
|
||||
createWriteStream,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
@@ -3680,11 +3681,11 @@ async function runCommandInvocation(invocation, options) {
|
||||
});
|
||||
}
|
||||
|
||||
async function startStaticFileServer(params) {
|
||||
export async function startStaticFileServer(params) {
|
||||
mkdirSync(dirname(params.logPath), { recursive: true });
|
||||
const logStream = createWriteStream(params.logPath, { flags: "a" });
|
||||
const fileName = String(params.filePath.split(/[/\\]/u).at(-1) ?? "artifact");
|
||||
const fileBytes = readFileSync(params.filePath);
|
||||
const fileStat = statSync(params.filePath);
|
||||
const server = createServer((request, response) => {
|
||||
logStream.write(`${new Date().toISOString()} ${request.method} ${request.url}\n`);
|
||||
if (request.url !== `/${fileName}`) {
|
||||
@@ -3694,8 +3695,20 @@ async function startStaticFileServer(params) {
|
||||
}
|
||||
response.statusCode = 200;
|
||||
response.setHeader("content-type", resolveStaticFileContentType(params.filePath));
|
||||
response.setHeader("content-length", String(fileBytes.length));
|
||||
response.end(fileBytes);
|
||||
response.setHeader("content-length", String(fileStat.size));
|
||||
response.setHeader("connection", "close");
|
||||
const fileStream = createReadStream(params.filePath);
|
||||
fileStream.once("error", (error) => {
|
||||
logStream.write(`${new Date().toISOString()} static-file-read-error ${formatError(error)}\n`);
|
||||
if (response.headersSent) {
|
||||
response.destroy(error);
|
||||
return;
|
||||
}
|
||||
response.removeHeader("content-length");
|
||||
response.statusCode = 500;
|
||||
response.end("failed to read file");
|
||||
});
|
||||
fileStream.pipe(response);
|
||||
});
|
||||
await new Promise((resolvePromise, rejectPromise) => {
|
||||
server.once("error", rejectPromise);
|
||||
|
||||
@@ -65,6 +65,7 @@ import {
|
||||
resolveRequestedSuites,
|
||||
resolveRunnerMatrix,
|
||||
resolveStaticFileContentType,
|
||||
startStaticFileServer,
|
||||
shouldExerciseManagedGatewayLifecycleAfterInstall,
|
||||
shouldRunPackagedUpgradeStatusProbe,
|
||||
shouldRunWindowsInstalledBrowserOverrideImportSmoke,
|
||||
@@ -659,6 +660,42 @@ describe("scripts/openclaw-cross-os-release-checks", () => {
|
||||
expect(resolveStaticFileContentType("openclaw-2026.4.14.tgz")).toBe("application/octet-stream");
|
||||
});
|
||||
|
||||
it("streams release artifacts from the static file server", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "openclaw-cross-os-static-server-"));
|
||||
const filePath = join(dir, "openclaw-2026.4.14.tgz");
|
||||
const logPath = join(dir, "server.log");
|
||||
let server: Awaited<ReturnType<typeof startStaticFileServer>> | undefined;
|
||||
|
||||
try {
|
||||
const payload = Buffer.from(`artifact-head\n${"x".repeat(1024 * 1024)}\nartifact-tail`);
|
||||
writeFileSync(filePath, payload);
|
||||
|
||||
server = await startStaticFileServer({ filePath, logPath });
|
||||
const response = await fetch(server.url);
|
||||
const body = Buffer.from(await response.arrayBuffer());
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("content-length")).toBe(String(payload.length));
|
||||
expect(response.headers.get("content-type")).toBe("application/octet-stream");
|
||||
expect(body.equals(payload)).toBe(true);
|
||||
expect(readFileSync(logPath, "utf8")).toContain(`GET /${filePath.split(/[/\\]/u).at(-1)}`);
|
||||
} finally {
|
||||
await server?.close();
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("does not preload static release artifacts before serving them", () => {
|
||||
const source = readFileSync("scripts/openclaw-cross-os-release-checks.ts", "utf8");
|
||||
const serverSource = source.slice(
|
||||
source.indexOf("export async function startStaticFileServer"),
|
||||
source.indexOf("export function resolveStaticFileContentType"),
|
||||
);
|
||||
|
||||
expect(serverSource).toContain("createReadStream(params.filePath)");
|
||||
expect(serverSource).not.toContain("readFileSync(params.filePath)");
|
||||
});
|
||||
|
||||
it("uses the published installer URLs for native installer lanes", () => {
|
||||
expect(resolvePublishedInstallerUrl("darwin")).toBe("https://openclaw.ai/install.sh");
|
||||
expect(resolvePublishedInstallerUrl("linux")).toBe("https://openclaw.ai/install.sh");
|
||||
|
||||
Reference in New Issue
Block a user