135 lines
5.0 KiB
JavaScript
135 lines
5.0 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { execFileSync } from "node:child_process";
|
|
|
|
const root = process.cwd();
|
|
const sourceRoot = path.join(root, "freecad-source");
|
|
const docsRoot = path.join(root, "docs");
|
|
const outputDir = path.join(docsRoot, "p6");
|
|
const manifestPath = path.join(outputDir, "baseline.json");
|
|
const logPath = path.join(outputDir, "baseline.log");
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
|
|
const log = [];
|
|
function command(file, args, cwd = root) {
|
|
const rendered = [file, ...args].join(" ");
|
|
try {
|
|
const stdout = execFileSync(file, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
log.push(`$ (cd ${cwd} && ${rendered})\n${stdout}`);
|
|
return { command: rendered, stdout, exitCode: 0 };
|
|
} catch (error) {
|
|
const stdout = String(error.stdout ?? "").trim();
|
|
const stderr = String(error.stderr ?? "").trim();
|
|
log.push(`$ (cd ${cwd} && ${rendered})\nexit=${error.status ?? 1}\n${stdout}\n${stderr}`.trim());
|
|
return { command: rendered, stdout, stderr, exitCode: error.status ?? 1 };
|
|
}
|
|
}
|
|
|
|
function sha256(file) {
|
|
const hash = crypto.createHash("sha256");
|
|
hash.update(fs.readFileSync(file));
|
|
return hash.digest("hex");
|
|
}
|
|
|
|
function existing(paths) {
|
|
return paths.find((candidate) => fs.existsSync(candidate)) ?? null;
|
|
}
|
|
|
|
const revision = command("git", ["rev-parse", "HEAD"], sourceRoot);
|
|
const status = command("git", ["status", "--porcelain=v1"], sourceRoot);
|
|
const diffStat = command("git", ["diff", "--stat"], sourceRoot);
|
|
const node = command(process.execPath, ["--version"]);
|
|
const npm = command("npm", ["--version"]);
|
|
const cmake = command("cmake", ["--version"]);
|
|
const chromeCandidates = [
|
|
"/usr/bin/google-chrome",
|
|
"/usr/bin/google-chrome-stable",
|
|
"/usr/bin/chromium",
|
|
"/usr/bin/chromium-browser",
|
|
"/opt/google/chrome/google-chrome",
|
|
];
|
|
const chromePath = existing(chromeCandidates);
|
|
const chromeVersion = chromePath ? command(chromePath, ["--version"]) : { command: "chrome --version", stdout: "", exitCode: 127 };
|
|
if (!chromePath) log.push("chrome executable not found; no alternate browser was attempted");
|
|
|
|
const cacheCandidates = [
|
|
path.join(sourceRoot, "build-web", "configure", "CMakeCache.txt"),
|
|
path.join(sourceRoot, "build-web", "CMakeCache.txt"),
|
|
];
|
|
const cachePath = existing(cacheCandidates);
|
|
const cache = cachePath ? fs.readFileSync(cachePath, "utf8") : "";
|
|
const cacheKeys = ["FREECAD_WEB:BOOL", "CMAKE_BUILD_TYPE:STRING", "CMAKE_TOOLCHAIN_FILE:FILEPATH", "CMAKE_GENERATOR:INTERNAL"];
|
|
const cacheValues = Object.fromEntries(cacheKeys.map((key) => [key, cache.match(new RegExp(`^${key.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")}=([^\\r\\n]*)`, "m"))?.[1] ?? null]));
|
|
|
|
const inputRelPaths = [
|
|
"docs/web-parity-matrix.json",
|
|
"docs/workbench-inventory.json",
|
|
"docs/object-inventory.json",
|
|
"docs/format-api-inventory.json",
|
|
"docs/freecad-web-release-gate.json",
|
|
"docs/current/FREECAD_WEB_MIGRATION_ANALYSIS.md",
|
|
"docs/current/FREECAD_WEB_EXECUTION_PLAN.md",
|
|
"docs/current/FREECAD_WEB_PROGRESS.md",
|
|
];
|
|
const inputs = inputRelPaths.map((relativePath) => {
|
|
const absolutePath = path.join(root, relativePath);
|
|
return {
|
|
path: relativePath,
|
|
exists: fs.existsSync(absolutePath),
|
|
size: fs.existsSync(absolutePath) ? fs.statSync(absolutePath).size : null,
|
|
sha256: fs.existsSync(absolutePath) ? sha256(absolutePath) : null,
|
|
};
|
|
});
|
|
|
|
const result = chromePath && revision.exitCode === 0 && inputs.every((item) => item.exists) ? "pass" : "blocked";
|
|
const report = {
|
|
schema: "freecad-web-p6-baseline/v1",
|
|
result,
|
|
generatedAt: new Date().toISOString(),
|
|
host: {
|
|
platform: process.platform,
|
|
arch: process.arch,
|
|
osRelease: os.release(),
|
|
cwd: root,
|
|
},
|
|
source: {
|
|
root: "FreeCAD/freecad-source",
|
|
revision: revision.stdout || null,
|
|
dirty: status.stdout.length > 0,
|
|
statusLines: status.stdout ? status.stdout.split("\n") : [],
|
|
diffStat: diffStat.stdout,
|
|
},
|
|
inputs,
|
|
build: {
|
|
configureDirectory: cachePath ? path.relative(root, path.dirname(cachePath)) : null,
|
|
cache: cacheValues,
|
|
},
|
|
tools: {
|
|
node: node.stdout || null,
|
|
npm: npm.stdout || null,
|
|
cmake: cmake.stdout.split("\n")[0] || null,
|
|
},
|
|
chrome: {
|
|
path: chromePath,
|
|
version: chromeVersion.stdout || null,
|
|
executableExitCode: chromeVersion.exitCode,
|
|
mode: "headless",
|
|
viewport: { width: 1440, height: 900, deviceScaleFactor: 1 },
|
|
network: "intercept-and-require-zero-external-requests",
|
|
},
|
|
policy: {
|
|
browser: "Google Chrome only",
|
|
nonChromeBrowsersAttempted: false,
|
|
dirtyWorktreePreserved: true,
|
|
},
|
|
};
|
|
|
|
fs.writeFileSync(manifestPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
fs.writeFileSync(logPath, `${log.join("\n\n")}\n\nresult=${result}\nmanifest=${path.relative(root, manifestPath)}\n`);
|
|
console.log(JSON.stringify({ schema: report.schema, result, revision: report.source.revision, chrome: report.chrome.version, inputCount: inputs.length }));
|
|
process.exitCode = result === "pass" ? 0 : 1;
|