154 lines
5.8 KiB
JavaScript
154 lines
5.8 KiB
JavaScript
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { execFileSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const toolsRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)));
|
|
const projectRoot = path.resolve(toolsRoot, "..");
|
|
const workspaceRoot = path.resolve(projectRoot, "..");
|
|
const distRoot = path.join(projectRoot, "web", "dist");
|
|
const evidencePath = path.join(projectRoot, "docs", "web-license-evidence.json");
|
|
const outputPath = path.join(projectRoot, "docs", "web-license-artifact-map.json");
|
|
|
|
const evidence = JSON.parse(fs.readFileSync(evidencePath, "utf8"));
|
|
const sourceRevision = execFileSync("git", ["-C", path.join(projectRoot, "freecad-source"), "rev-parse", "HEAD"], {
|
|
encoding: "utf8",
|
|
}).trim();
|
|
|
|
const dependencyRows = [...evidence.sourceDependencies, ...evidence.webDependencies];
|
|
const dependencyByProject = new Map(dependencyRows.map((row) => [row.project, row]));
|
|
const artifactRules = [
|
|
{
|
|
match: (relative) => relative === "index.html",
|
|
sources: ["FreeCAD/web/index.html"],
|
|
dependencies: ["FreeCAD Web UI"],
|
|
unresolved: [],
|
|
},
|
|
{
|
|
match: (relative) => /^assets\/index-[^/]+\.js$/.test(relative),
|
|
sources: [
|
|
"FreeCAD/web/src/main.ts",
|
|
"FreeCAD/web/src/storage.ts",
|
|
"FreeCAD/web/src/mesh-cache.ts",
|
|
"FreeCAD/web/src/recovery.ts",
|
|
"FreeCAD/web/src/worker-protocol.ts",
|
|
],
|
|
dependencies: ["FreeCAD Web UI", "three", "lucide"],
|
|
unresolved: [],
|
|
},
|
|
{
|
|
match: (relative) => /^assets\/kernel\.worker-[^/]+\.js$/.test(relative),
|
|
sources: ["FreeCAD/web/src/kernel.worker.ts", "FreeCAD/web/src/worker-protocol.ts"],
|
|
dependencies: ["FreeCAD Web UI"],
|
|
unresolved: [],
|
|
},
|
|
{
|
|
match: (relative) => /^kernel\/FreeCADWebKernel\.(js|wasm)$/.test(relative),
|
|
sources: [
|
|
"FreeCAD/freecad-source/src/WebAssembly/FreeCADWebBridge.cpp",
|
|
"FreeCAD/freecad-source/src/WebAssembly/CMakeLists.txt",
|
|
],
|
|
dependencies: ["FreeCAD Web UI", "Open CASCADE"],
|
|
unresolved: [
|
|
{
|
|
name: "Emscripten",
|
|
version: "3.1.6",
|
|
reason: "build tool generated JavaScript/WASM glue; no runtime package is shipped",
|
|
evidence: ["FreeCAD/docs/freecad-web-bridge-build.log", "FreeCAD/freecad-source/build-web/bridge/build.ninja"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
match: (relative) => relative === "samples/freecad-sample.step",
|
|
sources: ["FreeCAD/freecad-source/data/tests/Step/as1-ac-214_small.stp"],
|
|
dependencies: ["FreeCAD Web UI"],
|
|
unresolved: [],
|
|
},
|
|
];
|
|
|
|
function sha256(filePath) {
|
|
return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
}
|
|
|
|
function filesUnder(root) {
|
|
const files = [];
|
|
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
const entryPath = path.join(root, entry.name);
|
|
if (entry.isDirectory()) files.push(...filesUnder(entryPath));
|
|
else if (entry.isFile()) files.push(entryPath);
|
|
}
|
|
return files.sort();
|
|
}
|
|
|
|
function dependencyRecord(project) {
|
|
const row = dependencyByProject.get(project);
|
|
if (!row) throw new Error(`missing license evidence for dependency: ${project}`);
|
|
return {
|
|
id: `${project}@${row.version}`,
|
|
project: row.project,
|
|
version: row.version,
|
|
licenseLabel: row.licenseLabel,
|
|
evidence: row.evidence,
|
|
sha256: row.sha256,
|
|
status: row.status,
|
|
};
|
|
}
|
|
|
|
if (!fs.existsSync(distRoot)) throw new Error(`artifact directory does not exist: ${distRoot}`);
|
|
|
|
const artifacts = filesUnder(distRoot).map((absolutePath) => {
|
|
const relative = path.relative(distRoot, absolutePath).split(path.sep).join("/");
|
|
const rule = artifactRules.find((candidate) => candidate.match(relative));
|
|
if (!rule) throw new Error(`unmapped release artifact: ${relative}`);
|
|
for (const source of rule.sources) {
|
|
if (!fs.existsSync(path.join(workspaceRoot, source))) throw new Error(`missing source for ${relative}: ${source}`);
|
|
}
|
|
for (const unresolved of rule.unresolved) {
|
|
for (const item of unresolved.evidence) {
|
|
if (!fs.existsSync(path.join(workspaceRoot, item))) throw new Error(`missing unresolved evidence for ${relative}: ${item}`);
|
|
}
|
|
}
|
|
return {
|
|
path: `FreeCAD/web/dist/${relative}`,
|
|
size: fs.statSync(absolutePath).size,
|
|
sha256: sha256(absolutePath),
|
|
sources: rule.sources,
|
|
dependencyRefs: rule.dependencies.map(dependencyRecord),
|
|
unresolvedDependencies: rule.unresolved,
|
|
buildEvidence: relative.startsWith("kernel/")
|
|
? ["FreeCAD/docs/freecad-web-bridge-build.log", "FreeCAD/freecad-source/build-web/bridge/build.ninja"]
|
|
: ["FreeCAD/web/package.json", "FreeCAD/web/package-lock.json"],
|
|
};
|
|
});
|
|
|
|
const result = {
|
|
schema: "freecad-web-license-artifact-map/v1",
|
|
sourceRevision,
|
|
artifactRoot: "FreeCAD/web/dist",
|
|
generatedBy: "FreeCAD/tools/generate_web_license_artifact_map.mjs",
|
|
build: {
|
|
ui: "npm run build (FreeCAD/web/package.json)",
|
|
kernel: "cmake --build FreeCAD/freecad-source/build-web/bridge --target FreeCADWebKernel",
|
|
evidence: [
|
|
"FreeCAD/web/package.json",
|
|
"FreeCAD/web/package-lock.json",
|
|
"FreeCAD/docs/freecad-web-bridge-build.log",
|
|
"FreeCAD/freecad-source/build-web/bridge/build.ninja",
|
|
],
|
|
},
|
|
licenseEvidence: "FreeCAD/docs/web-license-evidence.json",
|
|
artifacts,
|
|
summary: {
|
|
artifactCount: artifacts.length,
|
|
unmappedArtifactCount: 0,
|
|
artifactsWithUnresolvedBuildTools: artifacts.filter((artifact) => artifact.unresolvedDependencies.length > 0).length,
|
|
result: "pass_all_release_files_mapped_unresolved_build_tools_explicit",
|
|
},
|
|
};
|
|
|
|
fs.writeFileSync(outputPath, `${JSON.stringify(result, null, 2)}\n`);
|
|
console.log(`mapped ${artifacts.length} release artifacts`);
|
|
console.log(`unresolved build-tool annotations: ${result.summary.artifactsWithUnresolvedBuildTools}`);
|
|
console.log(`wrote ${outputPath}`);
|