import { readFileSync, writeFileSync, mkdirSync } from 'node:fs' import { resolve } from 'node:path' import { spawnSync } from 'node:child_process' const root = resolve(import.meta.dirname, '..') const npmw = resolve(root, 'npmw') const packageJson = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8')) const scripts = packageJson.scripts ?? {} const laneArg = process.argv.find((arg) => arg.startsWith('--lane=')) const requestedLane = laneArg ? laneArg.slice('--lane='.length) : 'all' const validLanes = new Set(['all', 'chrome', 'oracle', 'wasm']) if (!validLanes.has(requestedLane)) { throw new Error(`Unknown lane "${requestedLane}". Expected one of: ${[...validLanes].join(', ')}`) } const phaseArg = process.argv.find((arg) => arg.startsWith('--phase=')) const requestedPhase = phaseArg ? phaseArg.slice('--phase='.length) : 'all' const validPhases = new Set(['all', 'execute', 'check']) if (!validPhases.has(requestedPhase)) { throw new Error(`Unknown phase "${requestedPhase}". Expected one of: ${[...validPhases].join(', ')}`) } if (requestedPhase !== 'all' && requestedLane !== 'chrome') { throw new Error(`Phase "${requestedPhase}" is currently supported only for the chrome lane.`) } const chromeTests = Object.keys(scripts) .filter((name) => name.startsWith('test:chrome-')) .sort() const wasmBuilds = process.env.CI_REAL_REBUILD_WASM === '1' ? ['fetch:freecad-source', 'build:occt-history', 'build:planegcs'] : [] if (wasmBuilds.length > 0) { for (const variable of ['OCCT_SOURCE_DIR', 'EMSDK']) { if (!process.env[variable]) throw new Error(`CI_REAL_REBUILD_WASM=1 requires ${variable} to be configured.`) } } const lanes = { chrome: [ 'build', ...chromeTests, ...chromeTests .map((name) => `check:${name.slice('test:'.length)}`) .filter((name) => scripts[name]), 'check:freecad-tsn-stage-evidence', ], oracle: [ 'fetch:freecad-source', 'configure:freecad-native', 'build:freecad-native', 'probe:freecad-reference', 'check:freecad-desktop-oracle', 'probe:freecad-core-parameter-mutations', 'check:freecad-core-parameter-mutations', 'test:golden:freecad', 'test:golden:freecad:families', 'test:golden:freecad:failures', 'test:golden:freecad:family-failures', 'probe:freecad-sketcher-constraints', 'probe:freecad-partdesign-profiles', 'probe:freecad-partdesign-base', 'probe:freecad-partdesign-loft', 'probe:freecad-partdesign-dressup', 'probe:freecad-partdesign-transform', 'probe:freecad-partdesign-failures', 'probe:freecad-partdesign-revolution-groove', 'probe:freecad-part-builders', 'probe:freecad-composite-history-elementmap', 'test:freecad-fcstd-native', 'generate:freecad-parameter-mutations', 'check:freecad-oracle-coverage', 'check:freecad-golden-coverage', 'check:freecad-parameter-mutations', 'check:freecad-sketcher-constraints', 'check:freecad-fcstd-roundtrip', 'check:freecad-composite-history-elementmap', 'check:freecad-exact-history-elementmap-gate', 'check:freecad-native-naming-evidence', ], wasm: [ ...wasmBuilds, 'check:occt-history-artifact', 'check:freecad-private-naming-boundary', 'test:browser-occt', 'check:browser-occt', 'test:planegcs', 'check:planegcs-artifact', 'test:chrome-planegcs', 'test:chrome-native-history', 'test:chrome-native-pad-history', 'test:chrome-native-pocket-history', 'test:chrome-camotics-wasm', 'test:chrome-cam-native-simulation', 'check:chrome-planegcs', 'check:chrome-native-history', 'check:chrome-native-pad-history', 'check:chrome-native-pocket-history', 'check:chrome-camotics-wasm', 'check:chrome-cam-native-simulation', ], } const selectedLanes = requestedLane === 'all' ? ['oracle', 'wasm', 'chrome'] : [requestedLane] const chromeExecution = ['build', ...chromeTests] const chromeChecks = [ ...chromeTests .map((name) => `check:${name.slice('test:'.length)}`) .filter((name) => scripts[name]), 'check:freecad-tsn-stage-evidence', ] const commandsFor = (lane) => { if (lane !== 'chrome' || requestedPhase === 'all') return lanes[lane] return requestedPhase === 'execute' ? chromeExecution : chromeChecks } const results = [] function runScript(name, laneEnvironment = {}) { if (!scripts[name]) throw new Error(`Required npm script is missing: ${name}`) const startedAt = Date.now() console.log(`\n[real-verification] ${name}`) const result = spawnSync(npmw, ['run', name], { cwd: root, env: { ...process.env, CI_REAL_VERIFICATION: '1', ...(process.env.CI_REAL_OFFLINE === '1' ? { npm_config_offline: 'true', FREECAD_SOURCE_OFFLINE: '1' } : {}), ...laneEnvironment, }, stdio: 'inherit', }) const record = { script: name, status: result.status ?? 1, durationMs: Date.now() - startedAt } results.push(record) if (record.status !== 0) throw new Error(`Real verification failed in ${name} (exit ${record.status})`) } try { for (const lane of selectedLanes) { console.log(`\n[real-verification] lane=${lane}`) const laneEnvironment = lane === 'oracle' ? { FREECAD_ORACLE_PROFILE: 'desktop', FREECAD_CMD: resolve(root, '.cache/freecad/install-desktop/bin/FreeCADCmd'), FREECAD_REFERENCE_CMD: resolve(root, '.cache/freecad/install-desktop/bin/FreeCAD'), } : {} for (const name of commandsFor(lane)) runScript(name, laneEnvironment) } } finally { const outputDir = resolve(root, '.cache', 'ci') mkdirSync(outputDir, { recursive: true }) const summaryName = requestedPhase === 'all' ? 'real-verification.json' : `real-verification-${requestedPhase}.json` writeFileSync(resolve(outputDir, summaryName), `${JSON.stringify({ generatedAt: new Date().toISOString(), lanes: selectedLanes, phase: requestedPhase, results }, null, 2)}\n`) } console.log(`\n[real-verification] completed ${results.length} ${requestedPhase} commands across ${selectedLanes.join(', ')}.`)