mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-15 20:20:09 +00:00
fix(maintainability): make gate parallel-pr friendly
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { execFileSync, spawnSync } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
||||
const BASELINE_FILE = path.join('docs', 'metrics', 'maintainability-baseline.json');
|
||||
const BASELINE_SCRIPT = path.join(PROJECT_ROOT, 'scripts', 'maintainability-baseline.js');
|
||||
|
||||
const PROTECTED_BRANCHES = new Set(['main', 'dev']);
|
||||
const HOTFIX_PREFIXES = ['hotfix/', 'kai/hotfix-'];
|
||||
|
||||
function hasFlag(name) {
|
||||
return process.argv.slice(2).includes(name);
|
||||
}
|
||||
|
||||
function detectBranchName() {
|
||||
try {
|
||||
return execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
||||
cwd: PROJECT_ROOT,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
}).trim();
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function isProtectedBranch(branchName) {
|
||||
if (!branchName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PROTECTED_BRANCHES.has(branchName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return HOTFIX_PREFIXES.some(prefix => branchName.startsWith(prefix));
|
||||
}
|
||||
|
||||
function detectMode() {
|
||||
if (hasFlag('--strict')) {
|
||||
return 'strict';
|
||||
}
|
||||
|
||||
if (hasFlag('--warn')) {
|
||||
return 'warn';
|
||||
}
|
||||
|
||||
if (hasFlag('--off')) {
|
||||
return 'off';
|
||||
}
|
||||
|
||||
const explicitMode = (process.env.CCS_MAINTAINABILITY_MODE || '').toLowerCase().trim();
|
||||
if (explicitMode === 'strict' || explicitMode === 'warn' || explicitMode === 'off') {
|
||||
return explicitMode;
|
||||
}
|
||||
|
||||
const eventName = process.env.GITHUB_EVENT_NAME || '';
|
||||
if (eventName === 'pull_request' || eventName === 'pull_request_target') {
|
||||
return 'warn';
|
||||
}
|
||||
|
||||
const gitHubRef = process.env.GITHUB_REF || '';
|
||||
if (gitHubRef.startsWith('refs/heads/')) {
|
||||
const branchFromRef = gitHubRef.slice('refs/heads/'.length);
|
||||
if (isProtectedBranch(branchFromRef)) {
|
||||
return 'strict';
|
||||
}
|
||||
}
|
||||
|
||||
return isProtectedBranch(detectBranchName()) ? 'strict' : 'warn';
|
||||
}
|
||||
|
||||
function runBaselineCheck() {
|
||||
return spawnSync('node', [BASELINE_SCRIPT, '--check', BASELINE_FILE], {
|
||||
cwd: PROJECT_ROOT,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
}
|
||||
|
||||
function writeStreams(result) {
|
||||
if (result.stdout) {
|
||||
process.stdout.write(result.stdout);
|
||||
}
|
||||
if (result.stderr) {
|
||||
process.stderr.write(result.stderr);
|
||||
}
|
||||
}
|
||||
|
||||
function tryParseJson(stdout) {
|
||||
if (!stdout) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(stdout);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function formatViolations(violations) {
|
||||
if (!Array.isArray(violations) || violations.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return violations.map(violation => {
|
||||
if (!violation || typeof violation !== 'object') {
|
||||
return '- unknown violation';
|
||||
}
|
||||
|
||||
const metric = violation.metric || 'unknown';
|
||||
const baseline = typeof violation.baseline === 'number' ? violation.baseline : 'n/a';
|
||||
const current = typeof violation.current === 'number' ? violation.current : 'n/a';
|
||||
return `- ${metric}: baseline=${baseline}, current=${current}`;
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
const mode = detectMode();
|
||||
|
||||
if (mode === 'off') {
|
||||
console.log('[i] Maintainability gate disabled (mode=off).');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const result = runBaselineCheck();
|
||||
if (mode === 'strict') {
|
||||
writeStreams(result);
|
||||
process.exit(result.status === null ? 1 : result.status);
|
||||
}
|
||||
|
||||
if (result.status === 0) {
|
||||
writeStreams(result);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const parsed = tryParseJson(result.stdout);
|
||||
const branchName = detectBranchName();
|
||||
|
||||
console.log('[!] Maintainability regression detected (warning-only mode).');
|
||||
if (branchName) {
|
||||
console.log(`[i] Branch: ${branchName}`);
|
||||
}
|
||||
|
||||
if (parsed && Array.isArray(parsed.violations) && parsed.violations.length > 0) {
|
||||
console.log('[i] Violations:');
|
||||
for (const line of formatViolations(parsed.violations)) {
|
||||
console.log(line);
|
||||
}
|
||||
} else {
|
||||
writeStreams(result);
|
||||
}
|
||||
|
||||
console.log('[i] This is non-blocking on PR/feature branches to support parallel workflow.');
|
||||
console.log(
|
||||
'[i] Use strict mode when needed: bun run maintainability:check:strict'
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user