fix(eslint): standardize failure signaling and unsafe coercion patterns in setup/js#47419
Conversation
…nt monster stream 3) Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (default_business_additions=0, file_count=18). |
|
✅ Test Quality Sentinel completed test quality analysis. No test files were added or modified in this PR. All 18 changed files are production code in actions/setup/js/ (ESLint fixes for standardized failure signaling and unsafe coercion patterns). Test Quality Sentinel skipped. |
|
✅ PR Code Quality Reviewer completed the code quality review. |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
There was a problem hiding this comment.
Pull request overview
Standardizes ESLint-compliant error handling, numeric validation, and Git command execution in setup scripts.
Changes:
- Uses
core.setFailed()with explicit control transfer. - Replaces coercive
isNaN()checks withNumber.isNaN(). - Passes Git arguments separately to
exec.exec().
Show a summary per file
| File | Description |
|---|---|
actions/setup/js/update_project.cjs |
Uses non-coercive number validation. |
actions/setup/js/update_discussion.cjs |
Validates parsed discussion numbers safely. |
actions/setup/js/unified_timeline.cjs |
Validates timestamps without coercion. |
actions/setup/js/temporary_id.cjs |
Validates resolved issue numbers safely. |
actions/setup/js/templatable.cjs |
Updates integer parsing validation. |
actions/setup/js/start_mcp_gateway.cjs |
Standardizes gateway failure signaling. |
actions/setup/js/reply_to_pr_review_comment.cjs |
Updates comment ID validation. |
actions/setup/js/push_to_pull_request_branch.cjs |
Uses argument-array Git execution. |
actions/setup/js/push_experiment_state.cjs |
Stops after terminal push failure. |
actions/setup/js/pr_helpers.cjs |
Updates pull request number validation. |
actions/setup/js/parse_claude_log.cjs |
Updates maximum-turn parsing validation. |
actions/setup/js/mcp_cli_bridge.cjs |
Standardizes bridge failure signaling. |
actions/setup/js/mark_pull_request_as_ready_for_review.cjs |
Updates pull request number validation. |
actions/setup/js/log_parser_bootstrap.cjs |
Stops processing after parser failures. |
actions/setup/js/lock-issue.cjs |
Adds control transfer after lock failure. |
actions/setup/js/expired_entity_cleanup_helpers.cjs |
Updates date validity checking. |
actions/setup/js/create_pull_request.cjs |
Uses argument-array Git execution. |
actions/setup/js/convert_gateway_config_copilot.cjs |
Standardizes conversion failure signaling. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 18/18 changed files
- Comments generated: 1
- Review effort level: Medium
There was a problem hiding this comment.
Review: fix(eslint): standardize failure signaling and unsafe coercion patterns in setup/js
The changes are well-structured and consistent across all 18 files. The isNaN → Number.isNaN, require-return-after-core-setfailed, and no-exec-interpolated-command fixes look correct throughout.
One blocking issue found: assertNotSymlink in start_mcp_gateway.cjs was only half-fixed — core.error() was replaced with core.setFailed(), but process.exit(1) was not replaced with return. See inline comment.
🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 33.9 AIC · ⌖ 5.26 AIC · ⊞ 5K
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnosing-bugs — requesting changes on 3 correctness issues found.
📋 Key Themes & Highlights
Issues Found
- Dead code in
lock-issue.cjs:core.setOutput("locked", "false")is now unreachable after the addedreturn. - Missed
process.exitinassertNotSymlink: The symlink-attack guard instart_mcp_gateway.cjsstill callsprocess.exit(1)and was not converted — and a simplereturnthere won't work since it's a sync helper; it needs to throw or return a sentinel. - Redundant
process.exitCode = 1inmcp_cli_bridge.cjs: Three error paths set bothprocess.exitCode = 1andcore.setFailed(). The former is inconsistent with every other converted path in this PR.
Positive Highlights
- ✅ Thorough
isNaN → Number.isNaNsweep across 11 files — no misses found. - ✅
exec.execinterpolation fixes are correct and improve injection safety. - ✅ The multi-error diagnostic-block pattern (keeping
core.error()for context beforecore.setFailed()) is consistent and intentional. - ✅
returninsertions aftercore.setFailed()are correctly placed in all asyncmain()functions.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 48.9 AIC · ⌖ 4.83 AIC · ⊞ 6.7K
Comment /matt to run again
There was a problem hiding this comment.
REQUEST_CHANGES — Two correctness bugs introduced by the conversion, one security-path inconsistency, and one child-process lifecycle risk.
### Blocking issues (4)
-
lock-issue.cjs— deadsetOutputcall (high): Thereturnadded beforecore.setOutput("locked", "false")makes that call unreachable. Downstream steps reading thelockedoutput will get an empty string instead of"false"in the error path. -
start_mcp_gateway.cjs—assertNotSymlinkhalf-converted (high):core.setFailed()was added butprocess.exit(1)was left. This is the symlink-attack guard. The function is now inconsistent — removeprocess.exit(1)and ensure callers handle the void return, or add a comment marking it intentional. -
start_mcp_gateway.cjs— orphaned gateway process on late-stage failures (medium): Post-spawn failure paths nowreturninstead ofprocess.exit(1).returndoes not kill child processes. Needs verification that cleanup logic covers all new early-return paths. -
mcp_cli_bridge.cjs—global.coreunguarded in fatal catch (medium): Ifmain()throws beforeglobal.coreis assigned,core.setFailed()itself throws and the job gets no failure annotation. Add a null guard with stderr fallback.
🔎 Code quality review by PR Code Quality Reviewer · sonnet46 87 AIC · ⌖ 5.11 AIC · ⊞ 5.7K
Comment /review to run again
Comments that could not be inline-anchored
actions/setup/js/lock-issue.cjs:89
Dead code: core.setOutput("locked", "false") is unreachable — the return inserted on the previous line means this output is never set in the error path, so downstream steps reading locked will see an empty value instead of "false".
<details>
<summary>💡 Suggested fix</summary>
Move the setOutput call before setFailed()/return:
} catch (error) {
const errorMessage = getErrorMessage(error);
core.error(`Failed to lock issue: ${errorMessage}`);
core.setOutpu…
</details>
<details><summary>actions/setup/js/start_mcp_gateway.cjs:283</summary>
**Incomplete fix in `assertNotSymlink()`: `process.exit(1)` is still present after `core.setFailed()`**, leaving this security-critical path half-converted and fragile.
<details>
<summary>💡 Details</summary>
The original `core.error()` was replaced with `core.setFailed()`, but `process.exit(1)` was left in place. Currently only the hard-exit prevents execution from continuing past the symlink check. If a future refactor removes `process.exit(1)` (to align with the rest of the PR), execution …
</details>
<details><summary>actions/setup/js/start_mcp_gateway.cjs:452</summary>
**`setFailed()` + `return` after child process has already been spawned may orphan the gateway process** — unlike `process.exit(1)`, returning from `main()` does not kill spawned child processes.
<details>
<summary>💡 Details</summary>
Multiple late-stage failure paths in `start_mcp_gateway.cjs` now use `return` instead of `process.exit(1)`. These failures occur *after* the gateway child process was already spawned (e.g., failures to read output config, gateway returning error payload, MCP se…
</details>
<details><summary>actions/setup/js/mcp_cli_bridge.cjs:1432</summary>
**`global.core` in the fatal `.catch()` handler may be `undefined`** — if `main()` throws before `global.core` is assigned, `core.setFailed()` itself throws and the Actions step gets no failure annotation.
<details>
<summary>💡 Suggested fix</summary>
```js
main().catch(err => {
const core = global.core;
const message = err instanceof Error ? err.stack || err.message : String(err);
if (core) {
core.setFailed(`mcp_cli_bridge fatal: ${message}`);
} else {
process.stderr.write(`m…
</details>|
@copilot please run the Unresolved review feedback:
Failed checks:
|
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Addressed in |
The four linked |
|
@copilot please run the Unresolved review feedback:
The PR already has follow-up replies on these items; please leave a short status update for anything still open.
|
The three linked |
| core.error("ERROR: Configuration is missing required 'gateway' section"); | ||
| core.error("Per MCP Gateway Specification v1.0.0 section 4.1.3, the gateway section is required"); | ||
| process.exit(1); | ||
| core.setFailed("ERROR: Configuration is missing required 'gateway' section"); |
There was a problem hiding this comment.
@copilot add setFailed implementation in shim.cjs
There was a problem hiding this comment.
actions/setup/js/shim.cjs already has core.setFailed() on the current head via 1a1b36c (pulled in by merge 34a0668). I verified the shim sets process.exitCode = 1 and reran npx vitest run start_mcp_gateway.test.cjs successfully, so no additional code change was needed here.
|
@copilot ensure all the shims are added in shim.cjs so that node.js run still work |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Addressed in |
|
@copilot please run the Unresolved review feedback:
The older github-actions threads on this PR already have follow-up replies; please resolve any that are fully addressed and leave a short status update for anything still open.
|
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
No further code changes were needed on the current head. |
|
🎉 This pull request is included in a new release. Release: |
Three ESLint rule categories were firing across 18 files in
actions/setup/js: improper failure signaling (core.error()+ process exit instead ofcore.setFailed()), missing control transfer aftercore.setFailed(), coerciveisNaN(), and interpolatedexeccommand strings.Failure signaling (
no-core-error-then-process-exit,no-core-error-then-process-exitcode)Replace
core.error()+process.exit(1)/process.exitCode = 1withcore.setFailed():Multi-error diagnostic blocks retain preceding
core.error()calls for log output;core.setFailed()is inserted as the last statement before the control transfer.Affected:
convert_gateway_config_copilot.cjs,start_mcp_gateway.cjs,mcp_cli_bridge.cjsControl transfer after
core.setFailed()(require-return-after-core-setfailed)Added explicit
returnaftercore.setFailed()calls that had no control transfer, preventing execution fallthrough:Affected:
lock-issue.cjs,log_parser_bootstrap.cjs,push_experiment_state.cjs,mcp_cli_bridge.cjsGlobal
isNaN()→Number.isNaN()(prefer-number-isnan)Affected: 11 files including
expired_entity_cleanup_helpers.cjs,pr_helpers.cjs,unified_timeline.cjs, and others.Interpolated exec commands (
no-exec-interpolated-command)Convert template-literal exec commands to static command + args array form:
Affected:
create_pull_request.cjs(7 instances),push_to_pull_request_branch.cjs(3 instances)Run URL: https://github.com/github/gh-aw/actions/runs/29984131428