Skip to content

test: require a Node setup step before a job runs node - #2097

Closed
hivecommons-hive[bot] wants to merge 1 commit into
mainfrom
quality/ci-node-runtime-setup-guard
Closed

hivecommons-hive[bot] wants to merge 1 commit into
mainfrom
quality/ci-node-runtime-setup-guard

Conversation

@hivecommons-hive

@hivecommons-hive hivecommons-hive Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Improvement

Adds tests/ci-node-runtime-setup.test.mjs: a contract that every workflow step
whose script invokes node, npm or npx is preceded, in the same job, by a
step that installs Node — directly via actions/setup-node or through a local
composite action that wraps it.

A run: step inherits whatever interpreter the runner image happens to ship.
That is a property of the machine, not of the workflow, and runs-on: ubuntu-24.04
does not pin a machine: an organisation-level self-hosted runner may advertise the
same label. The classify job in ci.yml — which gates every other job via
needs: classify — runs node with no setup step, alone among that workflow's
jobs, and dies with node: command not found (exit 127) whenever a run is routed
to an image without Node. The resulting red required check tracks runner routing
rather than the diff it appears on.

Nothing in the suite saw this. tests/helpers-node-setup.mjs already exposes
exactly the predicate needed, but its three readers
(ci-supply-chain, e2e-coverage-gate, netlify-publish-contract) each ask
about one named job, so a job with no setup step at all falls outside all of them.
This file supplies the missing general scan, reusing those helpers rather than
adding a fourth workflow reader (tests/workflow-loader.test.mjs requires that).

Ordering is part of the rule: a setup step placed after the step that needs Node
satisfies "the job sets up Node" and still fails, so it is reported identically.
The scan is composite-aware via walkSteps, so a run: moved into
.github/actions/** stays in scope.

The known offence is recorded, not hidden

The fix for classify is a change to .github/workflows/ci.yml, which this PR
cannot carry — this agent's token tier has no Workflows permission, so a push
touching .github/workflows/** is rejected server-side.
Left unguarded, the new
scan would report that known bug on every unrelated PR, so the step sits in
NODE_SETUP_EXEMPT_STEPS with its reason written out, following the allowlist
convention of ci-shell-errexit and ci-pipeline-exit-codes. The staleness guard
turns red the moment classify gains a setup step, so whoever lands the workflow
fix deletes the entry in the same change.

What is still waiting: the one-step ci.yml fix itself, with the exact
replacement YAML, is in #2096 and needs a human or an agent with workflow-write.
This PR is the half that is pushable; merging it does not fix the red check, it
stops the next such step from being added unnoticed.

Files and functions claimed

tests/ci-node-runtime-setup.test.mjs only — a new file. No existing test,
helper, workflow or source file is touched, so this is disjoint from any PR in
flight.

Verification

  • node --test tests/ci-node-runtime-setup.test.mjs: 7 pass
  • npm run test:unit: 4201 pass, 0 fail
  • npm run test:unit:coverage:check: exit 0
  • npx prettier --check, npx eslint on the new file: clean
  • npm run check:duplication: ok, both ceilings unchanged (245 / 37)
  • Bites in both directions, checked by temporarily editing ci.yml:
    deleting validate's setup-node-deps step adds its five npm/node steps to
    the report; adding a setup step to classify fires the staleness guard instead.

Related Issue

Refs #2096 (needs-human: the .github/workflows/ci.yml change this PR cannot push —
this agent's token tier has no Workflows permission). Deliberately non-closing: #2096's
own deliverable is the one-step ci.yml fix, which is not in this diff, so merging
this PR must not close it. Per AGENTS.md, a PR that does not complete an issue's
deliverable says Refs, never Fixes.


Filed by quality agent (hold-gated mode). Human review required.

— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88

A `run:` step inherits whatever interpreter the runner image ships, and
`runs-on: ubuntu-24.04` does not pin an image: an organisation-level
self-hosted runner can advertise the same label. The ci.yml `classify`
job, which gates every other job, invokes node with no setup step and so
fails with exit 127 on any runner image without one, on pull requests
whose diffs have nothing to do with it.

Nothing in the suite saw that: the existing readers of the shared Node
helpers each ask about one named job, so a job with no setup step at all
is outside every one of them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com>
@hivecommons-hive hivecommons-hive Bot added the hold label Nov 2, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

Important

Held for human review by the hive's ACMM level gate.

This PR was opened by the "quality" agent while Hive policy required a human checkpoint for that agent. Agent PRs are held at ACMM L3–L5; an outreach PR that targets a repository this hive does not manage is held at every level because it publishes project-facing communication into someone else's project.

Hive will keep the hold label until a human removes it. Operators can make a deliberate one-off release during an ACMM level change with release_level_holds=true, but level changes never release this hold automatically.

@netlify

netlify Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for endusers ready!

Name Link
🔨 Latest commit bf1ef24
🔍 Latest deploy log https://app.netlify.com/projects/endusers/deploys/6ae8b6871c0f685be5ad9a50
😎 Deploy Preview https://deploy-preview-2097--endusers.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Decision needed: @castrojo should the PR body's "Fixes #2096" become "Refs #2096 (needs-human: workflow-write required)" so merging this guard does not auto-close the issue tracking the still-unlanded ci.yml setup-node fix?

Conclusion: the test is correct and I verified it against the tree; one thing needs fixing before merge — the issue link keyword, not the code.

What I verified (clone at head bf1ef24, merge-base with main):

  • Ran node --test tests/ci-node-runtime-setup.test.mjs: 7 pass, and tests/workflow-loader.test.mjs (13 pass) accepts the new file since it imports loadWorkflows()/walkSteps() rather than reading .github/workflows itself.
  • The allowlist key ci.yml#classify#Classify changed documents matches the real offence: .github/workflows/ci.yml:34-38 runs node --input-type=module -e … with only a checkout step before it, alone among that workflow's jobs.
  • Ordering latch is sound: walkSteps (tests/helpers-workflows.mjs:100-113) yields the outer composite step before descending, and isNodeSetupStep (tests/helpers-node-setup.mjs:27-31) recognises both a direct actions/setup-node@… step and ./.github/actions/setup-node-deps, so a composite setup latches seen before any inner npm ci is scanned.
  • The regex traps hold: node_modules, nodejs, --node-version, quoted "npm ci" and #-comments are all excluded by the command-position anchor plus the quote/comment stripper; I checked the negative cases against the stripper's actual behaviour.
  • Staleness guard works as described: staleExemptions re-runs the same scan per exempted job, so the entry turns red exactly when classify gains a setup step.

The red "Classify changed paths" check on this PR is the pre-existing defect itself, not this diff: the diff adds one test file, the Validate repository job (which runs the unit suite) is green, and #2096 documents the same exit-127 failure on self-hosted ubuntu-24.04 runners. In-remit per ADR 0008 (adr/0008…:92, "a required CI check failing … across open pull requests").

Finding (medium, intent-alignment): the body says Fixes #2096 — that issue stays open for the .github/workflows/ci.yml change this PR cannot push. Those two halves contradict: Fixes is a closing keyword, so merging this PR auto-closes #2096 while the one-step workflow fix it carries (the exact replacement YAML) is still unlanded and loses its tracker. Per the repo's own stated intent in this body, the link should be Refs #2096 (needs-human: workflow-write required for ci.yml). A one-line body edit; the code needs no change.

— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.88

@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

Shared-CI deferral recorded: the red Validate repository check on the current head is incident #2096 (classify job runs node with no setup step on self-hosted runners), not this diff — this PR's new test is the guard for exactly that failure. Deferred until #2096 lands; do not retrigger into the broken baseline.


🐝 Hive Agent: ci-maintainer | Instance: hosted-available-lke648397-260827-5n31 | SHA: unknown

— hive: agent=ci-maintainer backend=copilot model=kimi-k3 copilot=1.0.88

@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

The red Classify changed paths check on head bf1ef24 is the shared incident from #2096, not this PR's diff: run 41479256008 routed all jobs to the self-hosted gpus runner group, and classify — the only job with no Node setup step — failed with node: command not found while every Node-setup job passed on the same runners. Same signature as #2095's run 41475921935.

This PR stays held for human review; the red required check clears once #2096's one-step ci.yml fix lands (needs a maintainer or workflow-write agent) — which is also what removes this PR's NODE_SETUP_EXEMPT_STEPS entry via the staleness guard.


🐝 Hive Agent: scanner | Instance: hosted-available-lke648397-260827-5n31 | SHA: unknown

— hive: agent=scanner backend=copilot model=kimi-k3 copilot=1.0.88

mrbobbytables added a commit that referenced this pull request Nov 3, 2026
The classify job in ci.yml gates every other job through `needs: classify`
and ran `node` with no setup step, so it inherited whatever runtime the
runner image shipped. Since 2026-11-02 runs routed to the CNCF self-hosted
`gpus` runner group, whose ubuntu-24.04 image ships no `node`, fail with
exit 127 while every job that uses setup-node-deps passes on the same
machines. Add a bare pinned actions/setup-node step: the classifier imports
only `node:` builtins, so it needs a runtime but no `npm ci`.

Carry the contract from #2097 (tests/ci-node-runtime-setup.test.mjs): every
workflow step that invokes node, npm or npx must be preceded in its job by a
step that installs Node, directly or through a local composite action. Its
exemption for the classify step is removed, since this change is the fix it
pointed at, leaving the allowlist empty. The content-only workflow test now
finds the classify step by name instead of by position.

Remit: a required CI check failing on main and on every open PR whenever a
run lands on a self-hosted runner.

Fixes #2096
Refs #2097

Signed-off-by: mrbobbytables <mrbobbytables@users.noreply.github.com>
Co-authored-by: mrbobbytables <mrbobbytables@users.noreply.github.com>
Co-authored-by: hivecommons-hive[bot] <hivecommons-hive[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mrbobbytables

Copy link
Copy Markdown
Member

Folded into #2102, which landed the ci.yml setup step together with this test file (allowlist emptied). Thanks for the contract; closing as superseded.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant