Skip to content

feat(eslint): add no-setfailed-then-exit-zero rule - #47232

Merged
pelikhan merged 4 commits into
mainfrom
copilot/fix-core-setfailed-bug
Jul 22, 2026
Merged

feat(eslint): add no-setfailed-then-exit-zero rule#47232
pelikhan merged 4 commits into
mainfrom
copilot/fix-core-setfailed-bug

Conversation

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

core.setFailed(msg) only schedules a non-zero exit code — it does not terminate the process. Calling process.exit(0) afterward overrides that exit code to success, causing the Actions step to appear green despite the declared failure.

New rule: no-setfailed-then-exit-zero

Detects core.setFailed(...) followed by process.exit(0) or process.exit() (no-arg form also exits 0) in any sequential statement list.

// ❌ flagged — exit code silently reset to success
core.setFailed("validation failed");
process.exit(0);

// ✅ correct
core.setFailed("validation failed");
return;
  • Scans forward past non-control-flow statements (e.g. core.info()) before the process.exit(0) — adjacent pairs get a suggestion fix, non-adjacent pairs are reported without an auto-fix
  • Handles computed (core["setFailed"]) and aliased (const c = core; c.setFailed(...)) forms
  • Stops scanning at any real control transfer (return, throw, break, process.exit(nonzero), etc.)
  • Covers BlockStatement, SwitchCase, and top-level Program nodes
  • Registered in the plugin as no-setfailed-then-exit-zero

Run: https://github.com/github/gh-aw/actions/runs/29906399216

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · gpt54 12.4 AIC · ⌖ 7.31 AIC · ⊞ 7K ·
Comment /souschef to run again

Adds a new ESLint rule that detects `core.setFailed()` followed by
`process.exit(0)` (or `process.exit()` with no args), which silently
resets the exit code to success and hides the failure from GitHub
Actions.

- New rule: `no-setfailed-then-exit-zero`
- Detects adjacent and non-adjacent `core.setFailed()` + `process.exit(0)` in
  BlockStatement, SwitchCase, and Program statement lists
- Also catches `process.exit()` (no args, defaults to 0)
- Provides an autofixable suggestion to replace `process.exit(0)` with `return;`
  for adjacent pairs
- Registered in the plugin index

Closes #47228

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix correctness issue with core.setFailed followed by process.exit feat(eslint): add no-setfailed-then-exit-zero rule Jul 22, 2026
Copilot AI requested a review from pelikhan July 22, 2026 08:14
@pelikhan
pelikhan marked this pull request as ready for review July 22, 2026 08:27
Copilot AI review requested due to automatic review settings July 22, 2026 08:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an ESLint rule to detect failure codes overridden by process.exit(0).

Changes:

  • Implements and registers the rule.
  • Adds detection and suggestion tests.
Show a summary per file
File Description
no-setfailed-then-exit-zero.ts Implements detection and suggestions.
no-setfailed-then-exit-zero.test.ts Tests supported patterns.
index.ts Registers the rule.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (2)

eslint-factory/src/rules/no-setfailed-then-exit-zero.ts:55

  • process.exit() does not reset a previously assigned process.exitCode: when no argument is supplied, Node uses the existing process.exitCode and only defaults to 0 when it is unset. Since core.setFailed() synchronously sets that value to 1, this valid failure path is currently reported and offered an unnecessary rewrite. Restrict this predicate to an explicit zero argument (and move the no-argument test to valid), unless the rule also proves that intervening code reset process.exitCode.
  // process.exit() with no arguments defaults to exit code 0
  if (expr.arguments.length === 0) return true;

eslint-factory/src/rules/no-setfailed-then-exit-zero.ts:157

  • Checking each body/consequent in isolation misses sequential control flow across block boundaries and switch fall-through. For example, if (bad) { core.setFailed("x"); } process.exit(0); is not reported; the existing require-return-after-core-setfailed traversal treats the outer process.exit as the next valid control transfer, so this exact footgun passes both rules. Reuse equivalent ancestor-continuation and switch-fall-through traversal from require-return-after-core-setfailed.ts:81-151.
    return {
      BlockStatement(node: TSESTree.BlockStatement) {
        checkStatements(node.body);
      },
      SwitchCase(node: TSESTree.SwitchCase) {
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Medium

const expr = node.expression;
if (expr.type !== AST_NODE_TYPES.CallExpression) return false;
const callee = expr.callee;
if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;
"no-core-error-then-process-exitcode": noCoreErrorThenProcessExitCodeRule,
"no-exec-interpolated-command": noExecInterpolatedCommandRule,
"require-execsync-try-catch": requireExecSyncTryCatchRule,
"no-setfailed-then-exit-zero": noSetFailedThenExitZeroRule,
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the unresolved review feedback already left on this PR, and rerun checks once the branch is up to date.

This PR has review coverage already; please work through the open feedback, then rerun checks after the branch update.

Run: https://github.com/github/gh-aw/actions/runs/29906399216

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · gpt54 12.4 AIC · ⌖ 7.31 AIC · ⊞ 7K ·
Comment /souschef to run again

….config.cjs

- Add isDestructuredCoreMethodIdentifier support to no-setfailed-then-exit-zero rule
  so patterns like `const { setFailed } = core; setFailed("bad"); process.exit(0);`
  are correctly flagged
- Enable the rule in eslint-factory/eslint.config.cjs so repository lint runs
  execute and verify the rule
- Add test cases for destructured setFailed (direct and renamed binding)

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address the unresolved review feedback already left on this PR, and rerun checks once the branch is up to date.

This PR has review coverage already; pleas...

Both review threads addressed in commit fix(eslint): add destructured alias support and enable rule in eslint.config.cjs:

  1. Destructured alias supportisCoreSetFailedStatement now handles the Identifier callee case by calling isDestructuredCoreMethodIdentifier(callee, "setFailed", sourceCode), matching the companion rule pattern. const { setFailed } = core; setFailed("bad"); process.exit(0) and renamed bindings (const { setFailed: sf } = core) are now flagged correctly.

  2. Rule enabled in eslint.config.cjs — Added "gh-aw-custom/no-setfailed-then-exit-zero": "warn" so repository lint runs exercise the rule against existing scripts.

Test cases for both destructured forms (direct and renamed binding) were also added. All local validation passes (fmt, lint, test-unit).

Copilot AI requested a review from gh-aw-bot July 22, 2026 09:29
@pelikhan
pelikhan merged commit 6c729ac into main Jul 22, 2026
@pelikhan
pelikhan deleted the copilot/fix-core-setfailed-bug branch July 22, 2026 10:25
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Code Quality] Fix correctness footgun: core.setFailed() followed by process.exit(0) silently resets exit code to success

4 participants