122 lines
5.1 KiB
JavaScript
122 lines
5.1 KiB
JavaScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const toolsRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)));
|
|
const projectRoot = path.resolve(toolsRoot, "..");
|
|
const docsRoot = path.join(projectRoot, "docs");
|
|
const map = JSON.parse(fs.readFileSync(path.join(docsRoot, "web-license-artifact-map.json"), "utf8"));
|
|
const evidence = JSON.parse(fs.readFileSync(path.join(docsRoot, "web-license-evidence.json"), "utf8"));
|
|
|
|
const dependencies = new Map();
|
|
for (const artifact of map.artifacts) {
|
|
for (const dependency of artifact.dependencyRefs) {
|
|
const existing = dependencies.get(dependency.id) ?? {
|
|
...dependency,
|
|
artifactPaths: [],
|
|
statuses: new Set(),
|
|
};
|
|
existing.artifactPaths.push(artifact.path);
|
|
existing.statuses.add(dependency.status);
|
|
dependencies.set(dependency.id, existing);
|
|
}
|
|
}
|
|
|
|
const inventoryDependencies = [...dependencies.values()]
|
|
.sort((left, right) => left.project.localeCompare(right.project))
|
|
.map((dependency) => ({
|
|
id: dependency.id,
|
|
project: dependency.project,
|
|
version: dependency.version,
|
|
licenseLabel: dependency.licenseLabel,
|
|
evidence: dependency.evidence,
|
|
sha256: dependency.sha256,
|
|
status: dependency.status,
|
|
artifactPaths: [...new Set(dependency.artifactPaths)].sort(),
|
|
}));
|
|
|
|
const unresolvedBuildTools = [];
|
|
const unresolvedByKey = new Map();
|
|
for (const artifact of map.artifacts) {
|
|
for (const unresolved of artifact.unresolvedDependencies) {
|
|
const key = `${unresolved.name}@${unresolved.version}`;
|
|
const existing = unresolvedByKey.get(key) ?? { ...unresolved, artifactPaths: [] };
|
|
existing.artifactPaths.push(artifact.path);
|
|
unresolvedByKey.set(key, existing);
|
|
}
|
|
}
|
|
for (const item of unresolvedByKey.values()) {
|
|
unresolvedBuildTools.push({ ...item, artifactPaths: [...new Set(item.artifactPaths)].sort() });
|
|
}
|
|
|
|
const evidenceProjects = new Set([...evidence.sourceDependencies, ...evidence.webDependencies].map((row) => row.project));
|
|
const missingEvidence = inventoryDependencies.filter((dependency) => !evidenceProjects.has(dependency.project));
|
|
const blocked = missingEvidence.map((dependency) => ({
|
|
id: dependency.id,
|
|
reason: "dependency is present in artifact map but has no matching license evidence row",
|
|
}));
|
|
|
|
const inventory = {
|
|
schema: "freecad-web-license-inventory/v1",
|
|
sourceRevision: map.sourceRevision,
|
|
artifactMap: "FreeCAD/docs/web-license-artifact-map.json",
|
|
evidence: "FreeCAD/docs/web-license-evidence.json",
|
|
generatedBy: "FreeCAD/tools/generate_web_license_notices.mjs",
|
|
dependencies: inventoryDependencies,
|
|
unresolvedBuildTools,
|
|
blocked,
|
|
policy: {
|
|
labelsAndHashesAreEvidencePointersOnly: true,
|
|
compatibilityAndLegalReview: "out_of_scope",
|
|
buildToolsAreNotRuntimeDependencies: true,
|
|
},
|
|
summary: {
|
|
notifiedRuntimeDependencies: inventoryDependencies.length,
|
|
blockedDependencies: blocked.length,
|
|
unresolvedBuildTools: unresolvedBuildTools.length,
|
|
result: blocked.length === 0
|
|
? "pass_all_runtime_dependencies_have_notice_and_evidence"
|
|
: "blocked_missing_or_conflicting_evidence",
|
|
},
|
|
};
|
|
|
|
const lines = [
|
|
"# FreeCAD 1.2 Web License and Copyright Notices",
|
|
"",
|
|
`Source revision: \`${map.sourceRevision}\``,
|
|
"",
|
|
"This inventory identifies the license evidence associated with each runtime dependency used by the verified Web distribution. Labels and hashes are pointers to collected text evidence; compatibility and legal conclusions are outside this engineering artifact.",
|
|
"",
|
|
"## Runtime Dependencies",
|
|
"",
|
|
];
|
|
for (const dependency of inventoryDependencies) {
|
|
lines.push(`### ${dependency.project} ${dependency.version}`);
|
|
lines.push("");
|
|
lines.push(`- License marker: ${dependency.licenseLabel}`);
|
|
lines.push(`- Evidence: \`${dependency.evidence}\``);
|
|
lines.push(`- Evidence SHA-256: \`${dependency.sha256}\``);
|
|
lines.push(`- Distributed artifacts: ${dependency.artifactPaths.map((artifact) => `\`${artifact}\``).join(", ")}`);
|
|
lines.push(`- Evidence status: ${dependency.status}`);
|
|
lines.push("");
|
|
}
|
|
|
|
lines.push("## Build Tools and Explicit Scope");
|
|
lines.push("");
|
|
for (const tool of unresolvedBuildTools) {
|
|
lines.push(`- ${tool.name} ${tool.version}: ${tool.reason}. Evidence: ${tool.evidence.map((item) => `\`${item}\``).join(", ")}.`);
|
|
}
|
|
if (unresolvedBuildTools.length === 0) lines.push("- No build-only tools were recorded in the artifact map.");
|
|
lines.push("");
|
|
lines.push("## Review Flags");
|
|
lines.push("");
|
|
if (blocked.length === 0) lines.push("- None. Every runtime dependency in the artifact map has a corresponding evidence row and notice entry.");
|
|
else for (const item of blocked) lines.push(`- BLOCKED ${item.id}: ${item.reason}.`);
|
|
lines.push("");
|
|
|
|
fs.writeFileSync(path.join(docsRoot, "web-license-inventory.json"), `${JSON.stringify(inventory, null, 2)}\n`);
|
|
fs.writeFileSync(path.join(docsRoot, "web-license-notices.md"), `${lines.join("\n")}\n`);
|
|
console.log(`notified ${inventory.summary.notifiedRuntimeDependencies} runtime dependencies`);
|
|
console.log(`blocked dependencies: ${inventory.summary.blockedDependencies}`);
|
|
console.log(`unresolved build tools: ${inventory.summary.unresolvedBuildTools}`);
|