mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 14:19:56 +00:00
fix(ci): remove maintainability baseline gate blocking releases
The maintainability gate (process.exit count + sync FS API count) was a raw grep counter that provided no actionable signal for a CLI tool. It blocked the v7.53.0 stable release because new features naturally increased the counts beyond the baseline. Removed: - scripts/maintainability-baseline.js - scripts/maintainability-check.js - docs/metrics/maintainability-baseline.json - All maintainability:* npm scripts - Gate references from release, dev-release, and CI workflows Quality gates retained: typecheck, eslint, prettier, full test suite.
This commit is contained in:
@@ -30,5 +30,5 @@ jobs:
|
|||||||
- name: Build package
|
- name: Build package
|
||||||
run: bun run build:all
|
run: bun run build:all
|
||||||
|
|
||||||
- name: Validate (typecheck + lint + format + maintainability [warn on PR] + tests)
|
- name: Validate (typecheck + lint + format + tests)
|
||||||
run: bun run validate
|
run: bun run validate
|
||||||
|
|||||||
@@ -48,10 +48,7 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: bun run build:all
|
run: bun run build:all
|
||||||
|
|
||||||
- name: Validate (typecheck + lint + format + maintainability [warn] + tests)
|
- name: Validate (typecheck + lint + format + tests)
|
||||||
env:
|
|
||||||
# Dev channel should surface maintainability regressions without blocking every merge.
|
|
||||||
CCS_MAINTAINABILITY_MODE: warn
|
|
||||||
run: bun run validate
|
run: bun run validate
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
- name: Build package
|
- name: Build package
|
||||||
run: bun run build:all
|
run: bun run build:all
|
||||||
|
|
||||||
- name: Validate (typecheck + lint + format + maintainability [strict] + tests)
|
- name: Validate (typecheck + lint + format + tests)
|
||||||
run: bun run validate
|
run: bun run validate
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"sourceDirectory": "src",
|
|
||||||
"largeFileThresholdLoc": 350,
|
|
||||||
"typeScriptFileCount": 390,
|
|
||||||
"locInSrc": 83786,
|
|
||||||
"processExitReferenceCount": 185,
|
|
||||||
"synchronousFsApiReferenceCount": 894,
|
|
||||||
"largeFileCountOver350Loc": 61
|
|
||||||
}
|
|
||||||
+1
-5
@@ -65,13 +65,9 @@
|
|||||||
"lint:fix": "eslint src/ --fix",
|
"lint:fix": "eslint src/ --fix",
|
||||||
"format": "prettier --write src/",
|
"format": "prettier --write src/",
|
||||||
"format:check": "prettier --check src/",
|
"format:check": "prettier --check src/",
|
||||||
"validate": "bun run typecheck && bun run lint:fix && bun run format:check && bun run maintainability:check && bun run test:all",
|
"validate": "bun run typecheck && bun run lint:fix && bun run format:check && bun run test:all",
|
||||||
"validate:ci-parity": "bash scripts/ci-parity-gate.sh",
|
"validate:ci-parity": "bash scripts/ci-parity-gate.sh",
|
||||||
"verify:bundle": "node scripts/verify-bundle.js",
|
"verify:bundle": "node scripts/verify-bundle.js",
|
||||||
"maintainability:baseline": "node scripts/maintainability-baseline.js --out docs/metrics/maintainability-baseline.json",
|
|
||||||
"maintainability:check": "node scripts/maintainability-check.js",
|
|
||||||
"maintainability:check:strict": "node scripts/maintainability-check.js --strict",
|
|
||||||
"maintainability:check:warn": "node scripts/maintainability-check.js --warn",
|
|
||||||
"test": "bun run build && bun run test:all",
|
"test": "bun run build && bun run test:all",
|
||||||
"test:ci": "bun run test:all",
|
"test:ci": "bun run test:all",
|
||||||
"test:all": "bun test tests/unit tests/integration tests/npm",
|
"test:all": "bun test tests/unit tests/integration tests/npm",
|
||||||
|
|||||||
@@ -1,308 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const { execFileSync } = require('child_process');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
||||||
const SRC_DIR = path.join(PROJECT_ROOT, 'src');
|
|
||||||
const DEFAULT_BASELINE_PATH = path.join(
|
|
||||||
PROJECT_ROOT,
|
|
||||||
'docs',
|
|
||||||
'metrics',
|
|
||||||
'maintainability-baseline.json'
|
|
||||||
);
|
|
||||||
|
|
||||||
const TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.tsx', '.cts', '.mts']);
|
|
||||||
const LARGE_FILE_THRESHOLD_LOC = 350;
|
|
||||||
|
|
||||||
const FS_SYNC_APIS = [
|
|
||||||
'accessSync',
|
|
||||||
'appendFileSync',
|
|
||||||
'chmodSync',
|
|
||||||
'chownSync',
|
|
||||||
'closeSync',
|
|
||||||
'copyFileSync',
|
|
||||||
'cpSync',
|
|
||||||
'existsSync',
|
|
||||||
'fchmodSync',
|
|
||||||
'fchownSync',
|
|
||||||
'fdatasyncSync',
|
|
||||||
'fstatSync',
|
|
||||||
'fsyncSync',
|
|
||||||
'ftruncateSync',
|
|
||||||
'futimesSync',
|
|
||||||
'lchmodSync',
|
|
||||||
'lchownSync',
|
|
||||||
'linkSync',
|
|
||||||
'lstatSync',
|
|
||||||
'lutimesSync',
|
|
||||||
'mkdirSync',
|
|
||||||
'mkdtempSync',
|
|
||||||
'openSync',
|
|
||||||
'opendirSync',
|
|
||||||
'readFileSync',
|
|
||||||
'readdirSync',
|
|
||||||
'readlinkSync',
|
|
||||||
'readSync',
|
|
||||||
'realpathSync',
|
|
||||||
'renameSync',
|
|
||||||
'rmSync',
|
|
||||||
'rmdirSync',
|
|
||||||
'statSync',
|
|
||||||
'symlinkSync',
|
|
||||||
'truncateSync',
|
|
||||||
'unlinkSync',
|
|
||||||
'utimesSync',
|
|
||||||
'writeFileSync',
|
|
||||||
'writeSync',
|
|
||||||
'writevSync',
|
|
||||||
];
|
|
||||||
|
|
||||||
const PROCESS_EXIT_PATTERN = /\bprocess\s*\.\s*exit\b/g;
|
|
||||||
const FS_SYNC_PATTERN = new RegExp(`\\b(?:${FS_SYNC_APIS.join('|')})\\b`, 'g');
|
|
||||||
|
|
||||||
function printUsage() {
|
|
||||||
console.log(
|
|
||||||
[
|
|
||||||
'Usage:',
|
|
||||||
' node scripts/maintainability-baseline.js',
|
|
||||||
' node scripts/maintainability-baseline.js --out [path]',
|
|
||||||
' node scripts/maintainability-baseline.js --check [path]',
|
|
||||||
'',
|
|
||||||
'Defaults:',
|
|
||||||
` baseline path: ${path.relative(PROJECT_ROOT, DEFAULT_BASELINE_PATH)}`,
|
|
||||||
].join('\n')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseArgs(argv) {
|
|
||||||
const options = {
|
|
||||||
outPath: null,
|
|
||||||
checkPath: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let index = 0; index < argv.length; index += 1) {
|
|
||||||
const arg = argv[index];
|
|
||||||
|
|
||||||
if (arg === '--help' || arg === '-h') {
|
|
||||||
printUsage();
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--out' || arg === '--write') {
|
|
||||||
const nextArg = argv[index + 1];
|
|
||||||
if (nextArg && !nextArg.startsWith('--')) {
|
|
||||||
options.outPath = nextArg;
|
|
||||||
index += 1;
|
|
||||||
} else {
|
|
||||||
options.outPath = path.relative(process.cwd(), DEFAULT_BASELINE_PATH);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg === '--check') {
|
|
||||||
const nextArg = argv[index + 1];
|
|
||||||
if (nextArg && !nextArg.startsWith('--')) {
|
|
||||||
options.checkPath = nextArg;
|
|
||||||
index += 1;
|
|
||||||
} else {
|
|
||||||
options.checkPath = path.relative(process.cwd(), DEFAULT_BASELINE_PATH);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Unknown argument: ${arg}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectTrackedFilesFromGit() {
|
|
||||||
let output;
|
|
||||||
try {
|
|
||||||
output = execFileSync('git', ['ls-files', '-z', '--', 'src'], {
|
|
||||||
cwd: PROJECT_ROOT,
|
|
||||||
encoding: 'utf8',
|
|
||||||
stdio: ['ignore', 'pipe', 'ignore'],
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
throw new Error(
|
|
||||||
'Unable to enumerate tracked files via git. Run this command from a git checkout with git installed.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!output) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return output
|
|
||||||
.split('\0')
|
|
||||||
.filter(Boolean)
|
|
||||||
.sort((left, right) => left.localeCompare(right))
|
|
||||||
.map(relativePath => path.resolve(PROJECT_ROOT, relativePath))
|
|
||||||
.filter(filePath => {
|
|
||||||
const relativeToSrc = path.relative(SRC_DIR, filePath);
|
|
||||||
if (relativeToSrc.startsWith('..') || path.isAbsolute(relativeToSrc)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stats = fs.statSync(filePath);
|
|
||||||
if (!stats.isFile()) {
|
|
||||||
throw new Error(`Tracked path is not a file: ${path.relative(PROJECT_ROOT, filePath)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectFilesInSrc() {
|
|
||||||
return collectTrackedFilesFromGit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function countLines(content) {
|
|
||||||
if (content.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lineBreakMatches = content.match(/\r\n|\n|\r/g);
|
|
||||||
const lineBreakCount = lineBreakMatches ? lineBreakMatches.length : 0;
|
|
||||||
const endsWithLineBreak = content.endsWith('\n') || content.endsWith('\r');
|
|
||||||
|
|
||||||
return endsWithLineBreak ? lineBreakCount : lineBreakCount + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function countMatches(content, pattern) {
|
|
||||||
const matches = content.match(pattern);
|
|
||||||
return matches ? matches.length : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectMetrics() {
|
|
||||||
if (!fs.existsSync(SRC_DIR)) {
|
|
||||||
throw new Error(`Directory not found: ${SRC_DIR}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const files = collectFilesInSrc();
|
|
||||||
|
|
||||||
let typeScriptFileCount = 0;
|
|
||||||
let locInSrc = 0;
|
|
||||||
let processExitReferenceCount = 0;
|
|
||||||
let synchronousFsApiReferenceCount = 0;
|
|
||||||
let largeFileCountOver350Loc = 0;
|
|
||||||
|
|
||||||
for (const filePath of files) {
|
|
||||||
const content = fs.readFileSync(filePath, 'utf8');
|
|
||||||
const loc = countLines(content);
|
|
||||||
const extension = path.extname(filePath).toLowerCase();
|
|
||||||
const isTypeScriptFile = TYPESCRIPT_EXTENSIONS.has(extension);
|
|
||||||
|
|
||||||
locInSrc += loc;
|
|
||||||
processExitReferenceCount += countMatches(content, PROCESS_EXIT_PATTERN);
|
|
||||||
synchronousFsApiReferenceCount += countMatches(content, FS_SYNC_PATTERN);
|
|
||||||
|
|
||||||
if (isTypeScriptFile) {
|
|
||||||
typeScriptFileCount += 1;
|
|
||||||
if (loc > LARGE_FILE_THRESHOLD_LOC) {
|
|
||||||
largeFileCountOver350Loc += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sourceDirectory: 'src',
|
|
||||||
largeFileThresholdLoc: LARGE_FILE_THRESHOLD_LOC,
|
|
||||||
typeScriptFileCount,
|
|
||||||
locInSrc,
|
|
||||||
processExitReferenceCount,
|
|
||||||
synchronousFsApiReferenceCount,
|
|
||||||
largeFileCountOver350Loc,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeMetrics(outPath, metrics) {
|
|
||||||
const resolvedOutPath = path.resolve(process.cwd(), outPath);
|
|
||||||
fs.mkdirSync(path.dirname(resolvedOutPath), { recursive: true });
|
|
||||||
fs.writeFileSync(resolvedOutPath, `${JSON.stringify(metrics, null, 2)}\n`, 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
function runCheck(checkPath, currentMetrics) {
|
|
||||||
const resolvedCheckPath = path.resolve(process.cwd(), checkPath);
|
|
||||||
const baselineContent = fs.readFileSync(resolvedCheckPath, 'utf8');
|
|
||||||
const baselineMetrics = JSON.parse(baselineContent);
|
|
||||||
|
|
||||||
if (baselineMetrics.sourceDirectory !== currentMetrics.sourceDirectory) {
|
|
||||||
throw new Error(
|
|
||||||
`Baseline sourceDirectory mismatch: expected "${currentMetrics.sourceDirectory}", got "${baselineMetrics.sourceDirectory}"`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baselineMetrics.largeFileThresholdLoc !== currentMetrics.largeFileThresholdLoc) {
|
|
||||||
throw new Error(
|
|
||||||
`Baseline largeFileThresholdLoc mismatch: expected ${currentMetrics.largeFileThresholdLoc}, got ${baselineMetrics.largeFileThresholdLoc}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gatedKeys = [
|
|
||||||
'processExitReferenceCount',
|
|
||||||
'synchronousFsApiReferenceCount',
|
|
||||||
];
|
|
||||||
|
|
||||||
const violations = [];
|
|
||||||
for (const key of gatedKeys) {
|
|
||||||
if (typeof baselineMetrics[key] !== 'number') {
|
|
||||||
throw new Error(`Baseline is missing numeric metric: ${key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMetrics[key] > baselineMetrics[key]) {
|
|
||||||
violations.push({
|
|
||||||
metric: key,
|
|
||||||
baseline: baselineMetrics[key],
|
|
||||||
current: currentMetrics[key],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
gate: 'maintainability-baseline',
|
|
||||||
baselinePath: path.relative(PROJECT_ROOT, resolvedCheckPath),
|
|
||||||
passed: violations.length === 0,
|
|
||||||
comparedMetrics: gatedKeys,
|
|
||||||
baseline: {
|
|
||||||
typeScriptFileCount: baselineMetrics.typeScriptFileCount,
|
|
||||||
locInSrc: baselineMetrics.locInSrc,
|
|
||||||
processExitReferenceCount: baselineMetrics.processExitReferenceCount,
|
|
||||||
synchronousFsApiReferenceCount: baselineMetrics.synchronousFsApiReferenceCount,
|
|
||||||
largeFileCountOver350Loc: baselineMetrics.largeFileCountOver350Loc,
|
|
||||||
},
|
|
||||||
current: {
|
|
||||||
typeScriptFileCount: currentMetrics.typeScriptFileCount,
|
|
||||||
locInSrc: currentMetrics.locInSrc,
|
|
||||||
processExitReferenceCount: currentMetrics.processExitReferenceCount,
|
|
||||||
synchronousFsApiReferenceCount: currentMetrics.synchronousFsApiReferenceCount,
|
|
||||||
largeFileCountOver350Loc: currentMetrics.largeFileCountOver350Loc,
|
|
||||||
},
|
|
||||||
violations,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
const options = parseArgs(process.argv.slice(2));
|
|
||||||
const metrics = collectMetrics();
|
|
||||||
|
|
||||||
if (options.outPath) {
|
|
||||||
writeMetrics(options.outPath, metrics);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.checkPath) {
|
|
||||||
const checkResult = runCheck(options.checkPath, metrics);
|
|
||||||
console.log(JSON.stringify(checkResult, null, 2));
|
|
||||||
if (!checkResult.passed) {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(JSON.stringify(metrics, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
#!/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