Files
Web_FreeCAD_Bitbybit/scripts/run-freecad-gui-command-shards.mjs
wangdequan 5bbd7b9d4f
Some checks failed
real-verification / chrome (push) Has been cancelled
real-verification / freecad-oracle (push) Has been cancelled
real-verification / wasm (push) Has been cancelled
feat: advance FreeCAD exact parity evidence
2026-08-14 22:39:16 -04:00

82 lines
4.9 KiB
JavaScript

import { spawnSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import { readFile, writeFile } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const shardCount = Number(process.env.FREECAD_GUI_COMMAND_SHARD_COUNT || 100)
if (!Number.isInteger(shardCount) || shardCount < 2 || shardCount > 256) throw new Error('FREECAD_GUI_COMMAND_SHARD_COUNT must be an integer from 2 through 256.')
const mergeOnly = process.argv.includes('--merge-only')
const shardArgument = process.argv.find((argument) => argument.startsWith('--shard='))
const requestedShard = shardArgument ? Number(shardArgument.slice('--shard='.length)) : null
if (mergeOnly && shardArgument) throw new Error('--merge-only and --shard cannot be used together.')
if (shardArgument && (!Number.isInteger(requestedShard) || requestedShard < 0 || requestedShard >= shardCount)) throw new Error(`--shard must be an integer from 0 through ${shardCount - 1}.`)
if (!mergeOnly) {
const shardIndexes = requestedShard == null ? Array.from({ length: shardCount }, (_, index) => index) : [requestedShard]
for (const index of shardIndexes) {
console.log(`[freecad-gui-command-shards] execute ${index + 1}/${shardCount}`)
const execution = spawnSync(process.execPath, ['scripts/run-freecad-reference-probe.mjs'], {
cwd: root,
env: {
...process.env,
FREECAD_ORACLE_PROFILE: 'desktop',
FREECAD_REFERENCE_SCOPE: 'gui-commands',
FREECAD_GUI_COMMAND_SHARD: `${index}/${shardCount}`,
},
stdio: 'inherit',
})
if (execution.status !== 0) throw new Error(`FreeCAD GUI command shard ${index}/${shardCount} failed with exit ${execution.status}.`)
}
}
if (requestedShard != null) {
console.log(JSON.stringify({ status: 'freecad-gui-command-shard-pass', shard: requestedShard, shardCount }, null, 2))
process.exit(0)
}
const shardReports = []
for (let index = 0; index < shardCount; index += 1) {
const relativePath = `.cache/freecad/reference-desktop-gui-commands-${index}-of-${shardCount}.json`
const bytes = await readFile(resolve(root, relativePath))
shardReports.push({ relativePath, bytes, report: JSON.parse(bytes) })
}
const first = shardReports[0].report
const universeCount = first.guiCommands?.commandUniverseCount
const universeSha256 = first.guiCommands?.commandUniverseSha256
const commands = []
let expectedStart = 0
for (const [index, { report }] of shardReports.entries()) {
const gui = report.guiCommands
if (report.baselineId !== 'freecad-1.1.1' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.probeScope !== 'gui-commands') throw new Error(`GUI command shard ${index} has the wrong baseline.`)
if (gui?.shard?.index !== index || gui.shard.count !== shardCount || gui.shard.start !== expectedStart || gui.shard.end <= gui.shard.start) throw new Error(`GUI command shard ${index} has a non-contiguous range.`)
if (gui.commandUniverseCount !== universeCount || gui.commandUniverseSha256 !== universeSha256) throw new Error(`GUI command shard ${index} has a different command universe.`)
if (gui.stateCommandCount !== gui.commands?.length || gui.stateCommandCount !== gui.shard.end - gui.shard.start) throw new Error(`GUI command shard ${index} has an incomplete state range.`)
if (JSON.stringify(report.modules) !== JSON.stringify(first.modules) || JSON.stringify(gui.workbenches) !== JSON.stringify(first.guiCommands.workbenches)) throw new Error(`GUI command shard ${index} changed module or workbench registration evidence.`)
commands.push(...gui.commands)
expectedStart = gui.shard.end
}
if (expectedStart !== universeCount || commands.length !== universeCount || new Set(commands.map((command) => command.id)).size !== universeCount) throw new Error('Merged GUI command shards do not cover the command universe exactly once.')
const mergedUniverseSha256 = createHash('sha256').update(commands.map((command) => command.id).join('\n')).digest('hex')
if (mergedUniverseSha256 !== universeSha256) throw new Error('Merged GUI command order does not match the declared command universe hash.')
const merged = {
...first,
guiCommands: {
...first.guiCommands,
stateCommandCount: commands.length,
shard: null,
commands,
mergedShards: shardReports.map(({ relativePath, bytes, report }) => ({
path: relativePath,
sha256: createHash('sha256').update(bytes).digest('hex'),
start: report.guiCommands.shard.start,
end: report.guiCommands.shard.end,
})),
},
}
const output = resolve(root, '.cache/freecad/reference-desktop-gui-commands.json')
await writeFile(output, `${JSON.stringify(merged, null, 2)}\n`)
console.log(JSON.stringify({ status: 'freecad-gui-command-shards-merged', shardCount, commandCount: commands.length, commandUniverseSha256: universeSha256, output }, null, 2))