Rule
no-core-error-then-process-exit (eslint-factory/src/rules/no-core-error-then-process-exit.ts)
The rule flags core.error(msg); process.exit(nonzero) and steers toward core.setFailed(msg). It only inspects immediately adjacent statements (current = stmts[i], next = stmts[i+1] in checkStatements, rule lines ~139-143), so any statement between the core.error(...) and the process.exit(nonzero) defeats detection:
core.error("fatal: could not load config");
core.summary.addRaw("fatal"); // any intervening statement
process.exit(1); // NOT flagged today
The anti-pattern is unchanged by an intervening log/summary line — it is still core.error used to fail instead of core.setFailed, followed by a hard process.exit(nonzero).
Contrast with the sibling rule
require-return-after-core-setfailed performs cross-statement / nested continuation analysis. no-core-error-then-process-exit is adjacency-only, which is inconsistent and under-covers.
Existing tests
Only adjacent pairs are covered (core.error("..."); process.exit(1);). No test exercises an intervening statement.
Proposed refinement
Within the same statement list (block / switch-case / program body), after a core.error(...) statement, scan forward for a process.exit(nonzero) before any of:
- a
core.setFailed(...) call (that already fixes the failure signalling — do not flag), or
- a control-transfer statement (
return / throw / break / continue), or
- a nested container that changes flow (be conservative — only scan the flat sibling list to avoid FPs).
Keep the existing autofix/suggestion behavior for the directly-adjacent case; the widened detection can report without a suggestion when the pair is non-adjacent (or extend the fixer to remove the process.exit and rewrite the core.error).
Acceptance criteria
Grounding
No live sites in actions/setup/js (no process.exit / core.error in non-test .cjs). This is regression-guard hardening. Confidence: medium; the scan-forward variant is a small, well-scoped change but must guard against FPs (hence the intervening-setFailed and control-transfer stops above).
Generated by 🤖 ESLint Refiner · 455.7 AIC · ⌖ 10.8 AIC · ⊞ 4.6K · ◷
Rule
no-core-error-then-process-exit(eslint-factory/src/rules/no-core-error-then-process-exit.ts)The rule flags
core.error(msg); process.exit(nonzero)and steers towardcore.setFailed(msg). It only inspects immediately adjacent statements (current = stmts[i],next = stmts[i+1]incheckStatements, rule lines ~139-143), so any statement between thecore.error(...)and theprocess.exit(nonzero)defeats detection:The anti-pattern is unchanged by an intervening log/summary line — it is still
core.errorused to fail instead ofcore.setFailed, followed by a hardprocess.exit(nonzero).Contrast with the sibling rule
require-return-after-core-setfailedperforms cross-statement / nested continuation analysis.no-core-error-then-process-exitis adjacency-only, which is inconsistent and under-covers.Existing tests
Only adjacent pairs are covered (
core.error("..."); process.exit(1);). No test exercises an intervening statement.Proposed refinement
Within the same statement list (block / switch-case / program body), after a
core.error(...)statement, scan forward for aprocess.exit(nonzero)before any of:core.setFailed(...)call (that already fixes the failure signalling — do not flag), orreturn/throw/break/continue), orKeep the existing autofix/suggestion behavior for the directly-adjacent case; the widened detection can report without a suggestion when the pair is non-adjacent (or extend the fixer to remove the
process.exitand rewrite thecore.error).Acceptance criteria
core.error("x"); core.info("y"); process.exit(1);is flagged.core.error("x"); core.setFailed("x"); process.exit(1);is not flagged (setFailed already present).core.error("x"); process.exit(0);remains valid (clean exit).npm testpasses.Grounding
No live sites in
actions/setup/js(noprocess.exit/core.errorin non-test.cjs). This is regression-guard hardening. Confidence: medium; the scan-forward variant is a small, well-scoped change but must guard against FPs (hence the intervening-setFailedand control-transfer stops above).