Real IFC2X3/IFC4 import tested. Full BIM authoring/export remains out of scope; 349-second building import performance remains to optimize.
94 lines
5.5 KiB
JavaScript
94 lines
5.5 KiB
JavaScript
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { execFileSync } from 'node:child_process';
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const native = path.join(root, 'freecad-source');
|
|
const gitDir = fs.realpathSync(path.join(native, '.git'));
|
|
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'freecad-snapshot-index-'));
|
|
const env = { ...process.env, GIT_INDEX_FILE: path.join(temp, 'index') };
|
|
const git = (args, cwd = native, input) => execFileSync('git', args, {
|
|
cwd, env, input, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024,
|
|
}).trim();
|
|
const parent = git(['rev-parse', 'HEAD']);
|
|
const previousSnapshot = process.env.FREECAD_SNAPSHOT_PARENT;
|
|
const commitParent = previousSnapshot ? git(['rev-parse', '--verify', `${previousSnapshot}^{commit}`]) : parent;
|
|
const branch = `workspace-snapshot-${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
const excluded = new Set(['node_modules', '.git', '__pycache__', 'dist', 'offline-package',
|
|
'continuation-runs', 'periodic-optimization', 'scheduler-bin']);
|
|
const entries = [];
|
|
function addFiles(relative, predicate = () => true) {
|
|
const absolute = path.join(root, relative);
|
|
if (!fs.existsSync(absolute)) return;
|
|
const stat = fs.lstatSync(absolute);
|
|
if (stat.isSymbolicLink()) return;
|
|
if (stat.isDirectory()) {
|
|
for (const name of fs.readdirSync(absolute)) {
|
|
if (excluded.has(name) || name.startsWith('.tmp')) continue;
|
|
addFiles(path.join(relative, name), predicate);
|
|
}
|
|
} else if (predicate(relative)) entries.push(relative);
|
|
}
|
|
addFiles('web/src'); addFiles('web/tools'); addFiles('web/public/api');
|
|
addFiles('web/public/samples');
|
|
for (const name of ['ifcopenshell-notice.txt', 'ifcopenshell-COPYING.txt', 'ifcopenshell-COPYING.LESSER.txt']) addFiles(`web/public/${name}`);
|
|
for (const name of fs.readdirSync(path.join(root, 'web'))) {
|
|
if (/\.(html|json|ts)$/.test(name)) addFiles(`web/${name}`);
|
|
}
|
|
addFiles('tools', name => /\.(py|mjs|js|sh|json)$/.test(name));
|
|
for (const folder of ['docs/business-acceptance', 'docs/p6', 'docs/current/user-tests-20260915']) {
|
|
addFiles(folder, name => /\.(md|json|png|html|csv|tsv|FCStd|stp|step|stl|brep|iges)$/i.test(name)
|
|
&& !/(?:events|environment|checkpoint|manual-snapshot|chrome)\.(?:json|log)$/.test(name));
|
|
}
|
|
for (const folder of ['docs', 'docs/current']) {
|
|
for (const name of fs.readdirSync(path.join(root, folder))) {
|
|
if (/\.(md|json)$/.test(name) && !/(?:scheduler|context|packet|continuation|failure-reconciliation|module-implementation|business-placeholder)/i.test(name))
|
|
if (fs.lstatSync(path.join(root, folder, name)).isFile()) entries.push(`${folder}/${name}`);
|
|
}
|
|
}
|
|
for (const name of ['package.json', 'package-lock.json', 'WORKSPACE-SNAPSHOT.md']) addFiles(name);
|
|
|
|
// Block actual credential-shaped assignments; diagnostics never print values.
|
|
const credentialPatterns = [
|
|
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
|
|
/https?:\/\/[^\s/:]+:[^\s/@]+@/,
|
|
/\bsk-[A-Za-z0-9_-]{25,}/,
|
|
/(?:password|api_key|apikey|access_token)\s*["']?\s*[:=]\s*["'][A-Za-z0-9_+\/-]{12,}["']/i,
|
|
];
|
|
const suspicious = [];
|
|
function scan(file, label) {
|
|
if (!fs.lstatSync(file).isFile()) return;
|
|
if (/\.(png|FCStd|stp|step|stl|brep|iges)$/i.test(file)) return;
|
|
const text = fs.readFileSync(file, 'utf8').replaceAll('https://user:secret@example.test', 'https://example.test');
|
|
if (credentialPatterns.some(pattern => pattern.test(text))) suspicious.push(label);
|
|
}
|
|
for (const relative of new Set(entries)) scan(path.join(root, relative), relative);
|
|
const nativeChanged = git(['ls-files', '-m', '-o', '--exclude-standard', 'src', 'cMake', 'tests']).split('\n')
|
|
.filter(name => name && !/\.(qm|pyc|o|a|so|wasm)$/.test(name));
|
|
for (const relative of nativeChanged) scan(path.join(native, relative), `freecad-source/${relative}`);
|
|
if (suspicious.length) {
|
|
console.log(JSON.stringify({ status: 'credential_review_required', paths: suspicious }));
|
|
process.exit(2);
|
|
}
|
|
git(['read-tree', parent]);
|
|
for (let index = 0; index < nativeChanged.length; index += 150)
|
|
git(['add', '--', ...nativeChanged.slice(index, index + 150)]);
|
|
const sourceTree = git(['write-tree']);
|
|
git(['read-tree', '--empty']);
|
|
git(['read-tree', '--prefix=freecad-source/', sourceTree]);
|
|
const unique = [...new Set(entries)].sort();
|
|
for (let index = 0; index < unique.length; index += 150)
|
|
git([`--git-dir=${gitDir}`, `--work-tree=${root}`, 'add', '-f', '--', ...unique.slice(index, index + 150)], root);
|
|
const tree = git(['write-tree']);
|
|
const commit = git(['commit-tree', tree, '-p', commitParent], native,
|
|
'Save verified native IFC import and user validation evidence\n\nReal IFC2X3/IFC4 import tested. Full BIM authoring/export remains out of scope; 349-second building import performance remains to optimize.\n');
|
|
git(['update-ref', `refs/heads/${branch}`, commit, '0000000000000000000000000000000000000000']);
|
|
const receipt = { status: 'committed_locally', branch, commit, parent: commitParent, sourceBase: parent, sourceTree, externalFiles: unique.length,
|
|
nativeChangedFiles: nativeChanged.length, remote: 'https://gitea.meswork.com/wangdequan/FreeCADWasmDev.git',
|
|
omissions: ['WASM/build artifacts', 'dependency sources', 'node_modules', 'agent/session logs', 'credentials', 'RAM-backed IfcOpenShell source'],
|
|
primaryIndexPreserved: true, mainPreserved: true };
|
|
fs.writeFileSync(path.join(temp, 'receipt.json'), JSON.stringify(receipt, null, 2)+'\n');
|
|
console.log(JSON.stringify({ ...receipt, receipt: path.join(temp, 'receipt.json') }));
|