105 lines
3.4 KiB
JavaScript
105 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const CODE_EXTENSION = String.raw`\.[cm]?[jt]sx?$`;
|
|
const noRuntimeImpact = [
|
|
/^\.codex\//,
|
|
/^\.tmp-tests\//,
|
|
/^docs\//,
|
|
/^test\//,
|
|
/^workpack\//,
|
|
/^(?:AGENTS|PRODUCT|README)\.md$/,
|
|
/^context\.md$/,
|
|
/^\.env\.example$/,
|
|
/^\.gitignore$/,
|
|
/^Dockerfile\.client$/,
|
|
/^docker-compose\.client(?:\.local)?\.yml$/,
|
|
/^entrypoint\.client\.sh$/,
|
|
/^install\.sh$/,
|
|
/^scripts\/(?:check-import-boundaries\.mjs|clean-test-dist\.mjs|harbor-network-monitor\.sh|harbor-version\.mjs|install-macos-client\.sh)$/,
|
|
];
|
|
const foundation = [
|
|
/^\.dockerignore$/,
|
|
/^\.gitea\/workflows\//,
|
|
/^Dockerfile(?:\.runtime-base)?$/,
|
|
/^docker-compose\.gateway\.yml$/,
|
|
/^entrypoint\.sh$/,
|
|
/^package(?:-lock)?\.json$/,
|
|
/^scripts\/(?:build-on-107-deploy-111|build-runtime-base|deploy-gateway)\.sh$/,
|
|
/^scripts\/runtime-impact\.mjs$/,
|
|
/^tsconfig(?:\.[^.]+)?\.json$/,
|
|
];
|
|
const controlAndDataplane = [
|
|
new RegExp(`^src/server/main${CODE_EXTENSION}`),
|
|
new RegExp(`^src/server/(?:config|gatewayRouting|singbox|singboxRuntime|version)${CODE_EXTENSION}`),
|
|
new RegExp(`^src/server/adapters/neighbors${CODE_EXTENSION}`),
|
|
new RegExp(`^src/server/services/(?:connectivityDiagnosticsService|deviceInventoryService|devicePolicyService|singboxSelectorService)${CODE_EXTENSION}`),
|
|
new RegExp(`^src/shared/(?:connectivityDiagnostics|errors)${CODE_EXTENSION}`),
|
|
/^src\/server\/infrastructure\/dataplane\//,
|
|
];
|
|
const dataplane = [
|
|
new RegExp(`^src/server/dataplane${CODE_EXTENSION}`),
|
|
new RegExp(`^src/server/services/(?:deviceTrafficService|domainTrafficService)${CODE_EXTENSION}`),
|
|
];
|
|
const control = [
|
|
/^index\.html$/,
|
|
/^monitoring\//,
|
|
/^public\//,
|
|
/^src\/server\//,
|
|
/^src\/shared\//,
|
|
/^src\/web\//,
|
|
/^vite\.config\.[cm]?[jt]s$/,
|
|
];
|
|
|
|
function matchesAny(file, patterns) {
|
|
return patterns.some((pattern) => pattern.test(file));
|
|
}
|
|
|
|
function normalizeFile(file) {
|
|
return file.trim().replaceAll('\\', '/').replace(/^\.\//, '');
|
|
}
|
|
|
|
export function classifyRuntimeImpact(files) {
|
|
const affected = new Set();
|
|
for (const value of files) {
|
|
const file = normalizeFile(value);
|
|
if (!file || matchesAny(file, noRuntimeImpact)) continue;
|
|
if (matchesAny(file, foundation) || matchesAny(file, controlAndDataplane)) {
|
|
affected.add('control');
|
|
affected.add('dataplane');
|
|
} else if (matchesAny(file, dataplane)) {
|
|
affected.add('dataplane');
|
|
} else if (matchesAny(file, control)) {
|
|
affected.add('control');
|
|
} else {
|
|
throw new Error(`Unclassified path: ${file}`);
|
|
}
|
|
}
|
|
|
|
const affectedComponents = ['control', 'dataplane'].filter((component) => affected.has(component));
|
|
const restartScope = affected.has('dataplane') ? 'both' : affected.has('control') ? 'control' : 'none';
|
|
return { affectedComponents, restartScope };
|
|
}
|
|
|
|
function formatImpact(impact) {
|
|
return [
|
|
`affected-components=${impact.affectedComponents.join('+') || 'none'}`,
|
|
`restart-scope=${impact.restartScope}`,
|
|
].join('\n');
|
|
}
|
|
|
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
try {
|
|
const args = process.argv.slice(2);
|
|
const files = args.includes('--stdin')
|
|
? fs.readFileSync(0, 'utf8').split(/\r?\n/)
|
|
: args;
|
|
console.log(formatImpact(classifyRuntimeImpact(files)));
|
|
} catch (error) {
|
|
console.error(`[runtime-impact] ${error.message}`);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|