import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { createHash } from "node:crypto"; import { lstatSync, readFileSync, readdirSync, readlinkSync } from "node:fs"; import { dirname, relative, resolve } from "node:path"; import { fileURLToPath } from "node:url"; const webRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); const appRoot = resolve(webRoot, "app"); const distRoot = resolve(appRoot, "dist"); function digestTree(root) { const entries = []; function walk(path) { for (const name of readdirSync(path).sort()) { const entryPath = resolve(path, name); const stat = lstatSync(entryPath); const relativePath = relative(root, entryPath).split("\\").join("/"); if (stat.isDirectory()) { entries.push({ type: "directory", path: relativePath, mode: stat.mode & 0o777 }); walk(entryPath); } else if (stat.isSymbolicLink()) { entries.push({ type: "symlink", path: relativePath, mode: stat.mode & 0o777, target: readlinkSync(entryPath), }); } else if (stat.isFile()) { entries.push({ type: "file", path: relativePath, mode: stat.mode & 0o777, sha256: createHash("sha256").update(readFileSync(entryPath)).digest("hex"), }); } else { assert.fail(`unsupported static release entry: ${relativePath}`); } } } walk(root); return { entryCount: entries.length, fileCount: entries.filter(({ type }) => type === "file").length, digest: createHash("sha256").update(JSON.stringify(entries)).digest("hex"), }; } const first = digestTree(distRoot); const build = spawnSync("npm", ["run", "build"], { cwd: appRoot, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], }); process.stdout.write(build.stdout || ""); process.stderr.write(build.stderr || ""); assert.equal(build.status, 0, `second static build failed with status ${build.status}`); const second = digestTree(distRoot); assert.deepEqual(second, first); console.log(`static_release_entry_count=${second.entryCount}`); console.log(`static_release_file_count=${second.fileCount}`); console.log(`static_release_tree_sha256=${second.digest}`); console.log("static_release_reproducibility=ok");