* test(#2474): update dispatch gate test for dual-gate behavior The #2772 test asserted the gate reads USE_WORKTREES_FOR_PLAN only. Update to accept the dual-gate (USE_WORKTREES + USE_WORKTREES_FOR_PLAN). * fix(#2474): gate worktree dispatch on project-level USE_WORKTREES too The per-plan dispatch condition checked only USE_WORKTREES_FOR_PLAN (submodule-derived), ignoring the project-level USE_WORKTREES flag. Add USE_WORKTREES to the gate. Net-negative edit: compress two nearby prose lines to offset the added shell condition (93353 bytes, down from 93368). Closes #2474 * docs(#2474): backfill changeset PR number (2561) * fix: merge coverage gate into single-process check (#2474) The test:coverage:unit script chained two c8 invocations with &&: the first ran tests and wrote coverage data to .nyc_output/, the second read that data for per-file branch checks. On fast CI runners (ubuntu/24), the second process started before the filesystem flushed the first process's writes — a classic TOCTOU race that caused intermittent coverage gate failures. Replace the two-process chain with a single c8 invocation that generates both text and json-summary reports, followed by a Node script (scripts/check-coverage-gate.cjs) that reads the JSON summary once and checks both overall and per-file thresholds. No filesystem race is possible because the JSON report is fully written before the check script reads it.
52 lines
1.4 KiB
JavaScript
52 lines
1.4 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const summaryPath = path.join(process.cwd(), 'coverage', 'coverage-summary.json');
|
|
if (!fs.existsSync(summaryPath)) {
|
|
console.error('check-coverage-gate: coverage/coverage-summary.json not found.');
|
|
console.error('Ensure c8 runs with --reporter json-summary before this script.');
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
|
|
const errors = [];
|
|
|
|
const OVERALL_LINES = 70;
|
|
const OVERALL_BRANCHES = 60;
|
|
const PER_FILE_BRANCHES = 70;
|
|
const PER_FILE_FILES = [
|
|
'gsd-core/bin/lib/state.cjs',
|
|
'gsd-core/bin/lib/phase.cjs',
|
|
'gsd-core/bin/lib/verify.cjs',
|
|
'gsd-core/bin/lib/init.cjs',
|
|
];
|
|
|
|
const overall = summary.total;
|
|
if (overall.lines.pct < OVERALL_LINES) {
|
|
errors.push(`Overall lines ${overall.lines.pct}% < ${OVERALL_LINES}%`);
|
|
}
|
|
if (overall.branches.pct < OVERALL_BRANCHES) {
|
|
errors.push(`Overall branches ${overall.branches.pct}% < ${OVERALL_BRANCHES}%`);
|
|
}
|
|
|
|
for (const file of PER_FILE_FILES) {
|
|
const key = Object.keys(summary).find((k) => k.endsWith(file));
|
|
if (!key) {
|
|
errors.push(`${file}: not found in coverage summary`);
|
|
continue;
|
|
}
|
|
const pct = summary[key].branches.pct;
|
|
if (pct < PER_FILE_BRANCHES) {
|
|
errors.push(`${file}: branches ${pct}% < ${PER_FILE_BRANCHES}%`);
|
|
}
|
|
}
|
|
|
|
if (errors.length > 0) {
|
|
for (const e of errors) console.error(`ERROR: ${e}`);
|
|
process.exitCode = 1;
|
|
}
|