chore(ci): make the cycle check runnable on Windows and report drift honestly (#10548)

Two independent papercuts in the circular reference check, both hit while
investigating a failure on another PR.

`npm run check-cycle-references` could not run on Windows at all. It spawned
the extensionless `node_modules/.bin/depcruise` shim through `execFileSync`,
which Windows cannot launch; the `.cmd` sibling does not help either, since
Node >=18 refuses to spawn `.cmd` without a shell (CVE-2024-27980). Resolve
dependency-cruiser's own entry point and run it with `process.execPath`
instead — no shell, works everywhere.

The check also fails on baseline drift (cycles recorded in the baseline that
no longer exist), which is correct: the baseline is a ratchet, and leaving a
fixed cycle in it would silently keep permitting its reintroduction. But CI
labelled every non-success outcome "New circular references detected", so a PR
that *removed* two cycles was reported as having added some. That cost real
debugging time.

Give the script distinct exit codes — 1 for a new cycle, 2 for drift alone —
and have the workflow capture and map them to separate messages. The failure
behaviour is unchanged; only the wording is now accurate. Renamed the final
step to match what it actually gates on.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
yaoweiprcandClaude Opus 5 authored and GitHub committed 2026-09-24 02:58:59 +00:00
1 parent 3de9fe4d26
commit f697cd847c
2 files changed
+46 -8

No files matched your search

+24 -5
View File
@@ -107,7 +107,15 @@ jobs:
id: check-cycles
continue-on-error: true
shell: bash
run: npm run check-cycle-references | tee cycle-report.txt
# Exit code 1 (a new cycle) and 2 (a stale baseline) are reported differently below, so
# capture it rather than letting `-e` collapse both into a generic step failure.
run: |
set +e
npm run check-cycle-references | tee cycle-report.txt
status=${PIPESTATUS[0]}
set -e
echo "status=$status" >> "$GITHUB_OUTPUT"
exit "$status"
- name: Post circular references PR comment
if: github.event_name == 'pull_request' && always() && steps.check-cycles.outcome != 'skipped'
@@ -117,12 +125,21 @@ jobs:
script: |
const fs = require('fs');
const passed = '${{ steps.check-cycles.outcome }}' === 'success';
// 0 = matches baseline, 1 = a new cycle appeared, 2 = baseline is stale. Anything
// else means the check itself blew up, which is not a claim about cycles either way.
const status = '${{ steps.check-cycles.outputs.status }}';
const passed = status === '0';
const summaryByStatus = {
'0': { icon: '✅', text: 'No new circular references' },
'1': { icon: '⚠️', text: 'New circular references detected' },
'2': { icon: '♻️', text: 'No new circular references, but the baseline is stale — regenerate and commit it' },
};
const { icon, text } = summaryByStatus[status] ?? { icon: '💥', text: 'The circular reference check failed to run' };
const report = fs.readFileSync('cycle-report.txt', 'utf8').trim();
const markdownContent = `<details>
<summary>${passed ? '✅' : '⚠️'} Circular References Report</summary>
<summary>${icon} Circular References Report</summary>
**Status:** ${passed ? 'No new circular references' : 'New circular references detected'}
**Status:** ${text}
\`\`\`
${report}
@@ -170,6 +187,8 @@ jobs:
console.log('Created new PR comment');
}
- name: Fail if new circular references found
# Fails on a stale baseline too, not just on new cycles: the baseline is a ratchet, so a
# cycle left in it after being fixed would silently keep permitting its reintroduction.
- name: Fail if the circular reference check did not pass
if: steps.check-cycles.outcome == 'failure'
run: exit 1