统一验收壳生产替身扫描
check:native-shells 增加移动壳和桌面壳生产源码临时替身词扫描。 扫描排除测试、配置检查脚本、node_modules、target 和移动壳 export 烟测产物。 更新宿主壳方案说明根级验收覆盖生产壳替身词门禁。 更新共享决策日志记录壳生产源码禁替身进入统一验收。
This commit is contained in:
@@ -1,9 +1,37 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import {spawnSync} from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
|
||||
const productionShellRoots = ['apps/mobile-shell', 'apps/desktop-shell'];
|
||||
const productionShellExtensions = new Set([
|
||||
'.json',
|
||||
'.mjs',
|
||||
'.rs',
|
||||
'.toml',
|
||||
'.ts',
|
||||
'.tsx',
|
||||
]);
|
||||
const productionShellExcludedSegments = new Set([
|
||||
'.expo-export-smoke',
|
||||
'node_modules',
|
||||
'target',
|
||||
]);
|
||||
const productionShellDevScaffoldTerms = [
|
||||
'mo' + 'ck',
|
||||
'fa' + 'ke',
|
||||
'place' + 'holder',
|
||||
'st' + 'ub',
|
||||
'TO' + 'DO',
|
||||
'FIX' + 'ME',
|
||||
'占' + '位',
|
||||
'模' + '拟',
|
||||
'伪' + '造',
|
||||
];
|
||||
|
||||
const h5HostBridgeTests = [
|
||||
'packages/shared/src/contracts/hostBridge.test.ts',
|
||||
'src/services/host-bridge/hostBridge.test.ts',
|
||||
@@ -58,6 +86,55 @@ const steps = [
|
||||
},
|
||||
];
|
||||
|
||||
function shouldScanProductionShellFile(filePath) {
|
||||
const normalizedPath = filePath.split(path.sep).join('/');
|
||||
if (
|
||||
normalizedPath.includes('.test.') ||
|
||||
normalizedPath.endsWith('/scripts/check-config.mjs')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return productionShellExtensions.has(path.extname(filePath));
|
||||
}
|
||||
|
||||
function collectProductionShellFiles(entryPath) {
|
||||
const stats = fs.statSync(entryPath);
|
||||
if (stats.isDirectory()) {
|
||||
const name = path.basename(entryPath);
|
||||
if (productionShellExcludedSegments.has(name)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fs
|
||||
.readdirSync(entryPath, { withFileTypes: true })
|
||||
.flatMap((entry) => collectProductionShellFiles(path.join(entryPath, entry.name)));
|
||||
}
|
||||
|
||||
return shouldScanProductionShellFile(entryPath) ? [entryPath] : [];
|
||||
}
|
||||
|
||||
function assertNoProductionShellDevScaffoldTerms() {
|
||||
const files = productionShellRoots.flatMap(collectProductionShellFiles);
|
||||
|
||||
for (const file of files) {
|
||||
const source = fs.readFileSync(file, 'utf8');
|
||||
const lowerSource = source.toLowerCase();
|
||||
for (const term of productionShellDevScaffoldTerms) {
|
||||
const matchIndex = lowerSource.indexOf(term.toLowerCase());
|
||||
if (matchIndex === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const line =
|
||||
source.slice(0, matchIndex).split('\n').length;
|
||||
throw new Error(
|
||||
`production native shell source must not include ${term}: ${file}:${line}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const step of steps) {
|
||||
console.log(`[check:native-shells] ${step.label}`);
|
||||
const result = spawnSync(step.command, step.args, {
|
||||
@@ -84,4 +161,7 @@ for (const step of steps) {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[check:native-shells] production-shell-dev-scaffold-scan');
|
||||
assertNoProductionShellDevScaffoldTerms();
|
||||
|
||||
console.log('[check:native-shells] OK');
|
||||
|
||||
Reference in New Issue
Block a user