Files
workinf_Blender_Wasm/tools/web/archive-deployment-server.mjs
mes123456 7c16b279ae
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M7 workflows and release operations
2026-08-15 17:43:53 -04:00

71 lines
3.0 KiB
JavaScript

import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import {
createDeploymentHttpServer,
listenDeploymentHttpServer,
loadDeploymentContract,
} from "./deployment-http-server.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
function option(name, fallback) {
const index = process.argv.indexOf(name);
return index >= 0 ? process.argv[index + 1] : fallback;
}
function assertSafeArchiveEntries(archive) {
const entries = execFileSync("tar", ["-tzf", archive], { encoding: "utf8" })
.split("\n")
.filter(Boolean);
assert.ok(entries.length > 0, "binary archive is empty");
for (const entry of entries) {
assert.ok(!entry.startsWith("/"), `binary archive contains an absolute path: ${entry}`);
assert.ok(!entry.split("/").includes(".."), `binary archive contains path traversal: ${entry}`);
assert.ok(entry === "blender-web-offline/" || entry.startsWith("blender-web-offline/"), `unexpected binary archive root: ${entry}`);
}
}
const archive = path.resolve(option("--archive", path.join(repoRoot, "release/blender-web-offline.tar.gz")));
const port = Number.parseInt(option("--port", process.env.WEB_TEST_PORT ?? "5189"), 10);
assert.ok(fs.statSync(archive).isFile(), `binary archive is missing: ${archive}`);
assert.ok(Number.isSafeInteger(port) && port > 0 && port < 65536, `invalid deployment port: ${port}`);
assertSafeArchiveEntries(archive);
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), "blender-archive-server-"));
execFileSync("tar", ["--no-same-owner", "--no-same-permissions", "-xzf", archive, "-C", workspace]);
const bundleRoot = path.join(workspace, "blender-web-offline");
const staticRoot = path.join(bundleRoot, "app");
assert.ok(fs.statSync(path.join(staticRoot, "index.html")).isFile(), "extracted binary archive has no app/index.html");
assert.ok(!staticRoot.startsWith(`${repoRoot}${path.sep}`), "archive server must not serve a workspace directory");
const contract = loadDeploymentContract(path.join(bundleRoot, "deployment-contract.json"));
const server = createDeploymentHttpServer({ root: staticRoot, contract });
let closing = false;
async function close(exitCode = 0) {
if (closing) return;
closing = true;
await new Promise((resolve) => server.close(() => resolve()));
fs.rmSync(workspace, { recursive: true, force: true });
process.exit(exitCode);
}
process.once("SIGINT", () => void close(0));
process.once("SIGTERM", () => void close(0));
process.once("uncaughtException", (error) => {
process.stderr.write(`${error.stack ?? error}\n`);
void close(1);
});
process.once("unhandledRejection", (error) => {
process.stderr.write(`${error instanceof Error ? error.stack : String(error)}\n`);
void close(1);
});
const origin = await listenDeploymentHttpServer(server, { port });
process.stdout.write(`archive-deployment-server ${origin} root=${staticRoot} archive=${archive}\n`);