Rebuild local Blender WASM environment and recovery evidence
This commit is contained in:
107
tools/web/build-m12-git-snapshot.mjs
Normal file
107
tools/web/build-m12-git-snapshot.mjs
Normal file
@@ -0,0 +1,107 @@
|
||||
import assert from "node:assert/strict";
|
||||
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 root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const driftPath = path.join(root, "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json");
|
||||
const outputArgumentIndex = process.argv.indexOf("--out");
|
||||
const outputRoot = path.resolve(root, outputArgumentIndex >= 0 ? process.argv[outputArgumentIndex + 1] : "tests/golden/corrective/rehydration/M12-git-history-snapshot");
|
||||
const filesRoot = path.join(outputRoot, "files");
|
||||
const drift = JSON.parse(fs.readFileSync(driftPath, "utf8"));
|
||||
const sha256 = (bytes) => crypto.createHash("sha256").update(bytes).digest("hex");
|
||||
const run = (args) => execFileSync("git", args, { cwd: root, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
||||
const uniqueEntries = new Map();
|
||||
for (const entry of drift.entries) {
|
||||
const key = `${entry.path}\0${entry.originalSha256}`;
|
||||
if (!uniqueEntries.has(key)) uniqueEntries.set(key, entry);
|
||||
}
|
||||
|
||||
function historicalBlob(pathname, expectedSha) {
|
||||
let objectListing = "";
|
||||
try {
|
||||
objectListing = run(["rev-list", "--objects", "--all", "--", pathname]);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const candidates = [...new Set(objectListing.split("\n").map((line) => line.split(" ")[0]).filter((id) => /^[0-9a-f]{40}$/u.test(id)))];
|
||||
for (const blob of candidates) {
|
||||
let type;
|
||||
try {
|
||||
type = run(["cat-file", "-t", blob]).trim();
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
if (type !== "blob") continue;
|
||||
const bytes = execFileSync("git", ["cat-file", "blob", blob], { cwd: root, maxBuffer: 64 * 1024 * 1024 });
|
||||
if (sha256(bytes) !== expectedSha) continue;
|
||||
let commits = [];
|
||||
try {
|
||||
commits = run(["log", "--all", "--format=%H", "--", pathname]).trim().split("\n").filter(Boolean);
|
||||
} catch {
|
||||
commits = [];
|
||||
}
|
||||
const commit = commits.find((candidate) => {
|
||||
try {
|
||||
return run(["rev-parse", `${candidate}:${pathname}`]).trim() === blob;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}) ?? null;
|
||||
return { blob, commit, bytes };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const matched = [];
|
||||
const unresolved = [];
|
||||
const pathPairCounts = new Map();
|
||||
for (const entry of uniqueEntries.values()) pathPairCounts.set(entry.path, (pathPairCounts.get(entry.path) ?? 0) + 1);
|
||||
for (const entry of uniqueEntries.values()) {
|
||||
const pathname = entry.path;
|
||||
const result = historicalBlob(pathname, entry.originalSha256);
|
||||
if (!result) {
|
||||
unresolved.push({ path: pathname, originalSha256: entry.originalSha256, reason: "exact SHA-256 content was not found in Git history" });
|
||||
continue;
|
||||
}
|
||||
const suffix = (pathPairCounts.get(pathname) ?? 0) > 1 ? `.${entry.originalSha256.slice(0, 16)}` : "";
|
||||
const snapshotPath = path.join("files", `${pathname}${suffix}`);
|
||||
const destination = path.join(outputRoot, snapshotPath);
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||
fs.writeFileSync(destination, result.bytes);
|
||||
matched.push({
|
||||
path: pathname,
|
||||
snapshotPath,
|
||||
sha256: entry.originalSha256,
|
||||
bytes: result.bytes.length,
|
||||
gitBlob: result.blob,
|
||||
gitCommit: result.commit,
|
||||
});
|
||||
}
|
||||
|
||||
const repository = run(["remote", "get-url", "origin"]).trim();
|
||||
const head = run(["rev-parse", "HEAD"]).trim();
|
||||
const manifest = {
|
||||
schemaVersion: 1,
|
||||
mode: "IMMUTABLE_REPOSITORY",
|
||||
authorized: true,
|
||||
source: {
|
||||
kind: "GIT_HISTORY",
|
||||
repository,
|
||||
head,
|
||||
selection: "exact original SHA-256 content match; current worktree is never read",
|
||||
},
|
||||
coverage: {
|
||||
requestedPaths: new Set([...uniqueEntries.values()].map((entry) => entry.path)).size,
|
||||
requestedPairs: uniqueEntries.size,
|
||||
matchedPaths: matched.length,
|
||||
unmatchedPaths: unresolved.length,
|
||||
unresolved,
|
||||
},
|
||||
files: matched.sort((a, b) => a.path.localeCompare(b.path)),
|
||||
};
|
||||
fs.mkdirSync(outputRoot, { recursive: true });
|
||||
fs.writeFileSync(path.join(outputRoot, "snapshot-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
||||
process.stdout.write(`m12-git-snapshot-ok requested=${uniqueEntries.size} matched=${matched.length} unmatched=${unresolved.length} output=${path.relative(root, outputRoot).replaceAll(path.sep, "/")}\n`);
|
||||
Reference in New Issue
Block a user