Files
FreeCADWasmDev/web/tools/artifact-assets.mjs
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

61 lines
2.3 KiB
JavaScript

import fs from "node:fs";
import crypto from "node:crypto";
import path from "node:path";
export function copyArtifactAtomically(source, destination, copy = fs.copyFileSync) {
let lastError;
for (let attempt = 0; attempt < 3; attempt += 1) {
const temporary = `${destination}.${process.pid}.${crypto.randomUUID()}.tmp`;
try {
const before = fs.statSync(source, { bigint: true });
copy(source, temporary);
const after = fs.statSync(source, { bigint: true });
if (before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ino !== after.ino)
throw new Error(`Artifact changed while copying; wait for its build to finish: ${source}`);
if (destination.endsWith(".wasm") && !WebAssembly.validate(fs.readFileSync(temporary)))
throw new Error(`Incomplete or invalid WASM; previous published file retained: ${source}`);
fs.renameSync(temporary, destination);
return;
} catch (error) {
lastError = error;
} finally {
fs.rmSync(temporary, { force: true });
}
}
throw lastError;
}
export function missingArtifactPair(source) {
const match = path.basename(source).match(/^(FreeCADWeb[A-Za-z0-9]+)\.(?:js|wasm|data|worker\.js)$/);
if (!match) return [];
const stem = path.join(path.dirname(source), match[1]);
return [stem + ".js", stem + ".wasm"].filter(file => !fs.existsSync(file));
}
export function pthreadSidecar(source, destination) {
if (!source.endsWith(".js") || source.endsWith(".worker.js")) return null;
const worker = source.slice(0, -3) + ".worker.js";
return fs.existsSync(worker) ? [worker, destination.slice(0, -3) + ".worker.js"] : null;
}
export function artifactRevision(hashes) {
return crypto.createHash("sha256").update(JSON.stringify(hashes)).digest("hex").slice(0, 16);
}
export function writeManifestIfChanged(file, manifest) {
const stable = value => {
const copy = { ...value };
delete copy.generatedAt;
return JSON.stringify(copy);
};
try {
if (stable(JSON.parse(fs.readFileSync(file, "utf8"))) === stable(manifest)) return false;
} catch {
// Missing or incomplete manifests are replaced below.
}
const temporary = file + ".tmp";
fs.writeFileSync(temporary, JSON.stringify(manifest, null, 2) + "\n");
fs.renameSync(temporary, file);
return true;
}