128 lines
3.3 KiB
JavaScript
128 lines
3.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { createHash } from 'node:crypto';
|
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const baselinePath = path.join(repoRoot, 'scripts', 'server-node-freeze-baseline.json');
|
|
const needle = 'server-node';
|
|
|
|
const ignoredDirectories = new Set([
|
|
'.git',
|
|
'.codex',
|
|
'.codex-temp',
|
|
'.idea',
|
|
'node_modules',
|
|
'dist',
|
|
'build',
|
|
'coverage',
|
|
'target',
|
|
'logs',
|
|
]);
|
|
|
|
const ignoredFiles = new Set([
|
|
'scripts/check-server-node-freeze.mjs',
|
|
'scripts/server-node-freeze-baseline.json',
|
|
'scripts/server-node-frozen.mjs',
|
|
'docs/audits/engineering/SERVER_NODE_FREEZE_AND_DEPRECATION_2026-04-24.md',
|
|
]);
|
|
|
|
const allowedExtensions = new Set([
|
|
'.cjs',
|
|
'.js',
|
|
'.json',
|
|
'.md',
|
|
'.mjs',
|
|
'.ps1',
|
|
'.rs',
|
|
'.toml',
|
|
'.ts',
|
|
'.tsx',
|
|
'.yaml',
|
|
'.yml',
|
|
]);
|
|
|
|
function toRepoPath(absolutePath) {
|
|
return path.relative(repoRoot, absolutePath).replaceAll(path.sep, '/');
|
|
}
|
|
|
|
function hashLine(line) {
|
|
return createHash('sha256').update(line.trim()).digest('hex');
|
|
}
|
|
|
|
function walk(directory, output) {
|
|
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
if (entry.isDirectory()) {
|
|
if (!ignoredDirectories.has(entry.name)) {
|
|
walk(path.join(directory, entry.name), output);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const absolutePath = path.join(directory, entry.name);
|
|
const repoPath = toRepoPath(absolutePath);
|
|
if (ignoredFiles.has(repoPath)) {
|
|
continue;
|
|
}
|
|
if (!allowedExtensions.has(path.extname(entry.name).toLowerCase())) {
|
|
continue;
|
|
}
|
|
output.push(absolutePath);
|
|
}
|
|
}
|
|
|
|
function collectReferences() {
|
|
const files = [];
|
|
walk(repoRoot, files);
|
|
|
|
const references = new Map();
|
|
for (const file of files) {
|
|
const repoPath = toRepoPath(file);
|
|
const content = readFileSync(file, 'utf8');
|
|
const lines = content.split(/\r?\n/u);
|
|
for (const line of lines) {
|
|
if (!line.toLowerCase().includes(needle)) {
|
|
continue;
|
|
}
|
|
const key = `${repoPath}\u0000${hashLine(line)}`;
|
|
references.set(key, (references.get(key) || 0) + 1);
|
|
}
|
|
}
|
|
return references;
|
|
}
|
|
|
|
function parseBaseline() {
|
|
if (!existsSync(baselinePath)) {
|
|
return new Map();
|
|
}
|
|
const baseline = JSON.parse(readFileSync(baselinePath, 'utf8'));
|
|
return new Map(Object.entries(baseline.references || {}));
|
|
}
|
|
|
|
const currentReferences = collectReferences();
|
|
const baselineReferences = parseBaseline();
|
|
const newReferences = [];
|
|
|
|
for (const [key, count] of currentReferences.entries()) {
|
|
const allowedCount = baselineReferences.get(key) || 0;
|
|
if (count > allowedCount) {
|
|
const [repoPath] = key.split('\u0000');
|
|
newReferences.push({ repoPath, count: count - allowedCount });
|
|
}
|
|
}
|
|
|
|
if (newReferences.length > 0) {
|
|
console.error('检测到冻结后新增的 server-node 引用,请迁移到 server-rs 或更新废弃审计后再处理:');
|
|
for (const reference of newReferences.slice(0, 50)) {
|
|
console.error(`- ${reference.repoPath} (+${reference.count})`);
|
|
}
|
|
if (newReferences.length > 50) {
|
|
console.error(`... 另有 ${newReferences.length - 50} 处`);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log('server-node freeze guard passed: 未发现冻结后新增引用。');
|