Skip to content

perf(ci): install the remaining exact Node pins without GitHub API calls - #11073

Merged
kyle-sexton merged 1 commit into
mainfrom
perf/setup-pinned-node-followup
Oct 29, 2026
Merged

kyle-sexton merged 1 commit into
mainfrom
perf/setup-pinned-node-followup

Conversation

@kyle-sexton

Copy link
Copy Markdown
Member

No related issue: follow-up to #10840

Summary

#10840 added the local setup-pinned-node action and switched the steps that run from main. This PR switches the 16 remaining actions/setup-node steps that read the exact .node-version pin (24.21.0). Each of those steps missed the runner's tool cache and spent two job-token API calls reading the actions/node-versions manifest.

Fix

Workflow Steps Form
pr-require-checks.yml: lint-repo, check-plugins, check-catalog, test-bash, test-python, test-node 6 ./.github/actions/setup-pinned-node: the root checkout is the code under test and these jobs already run it with a read-only token, so a PR that changes the action is tested by its own CI
pr-require-checks.yml: ci-status 1 ./.base/...: main's copy; /.github/actions/setup-pinned-node/ added to its sparse .base checkout
pr-review-hosted.yml, pr-review-security-hosted.yml: resolve-model, kill-switch 2 + 2 ./.base/..., path added to each sparse .base checkout
pr-review-main.yml: review, review-subscription, review-fallback 3 ./.base/...: workflow_run, .base is main's tip recorded in the same run, and no PR code is checked out
eval-browser-dispatch.yml 1 ./.base/... (main), never .head
maintenance-check-upstream-drift.yml 1 ./.github/... (schedule runs main; dispatch needs write access)
  • package-manager-cache: false is dropped from each converted step: the action declares only node-version-file and restores no cache.
  • maintenance-audit-test-selection.yml stays on actions/setup-node. It runs 3 times a day and uses cache: npm, so converting it saves about 0 calls/h and drops its cache.
  • scripts/ci-lanes-pr-hold.test.sh: the ci-status no-cache check now passes when ci-status has no actions/setup-node step. It still fails on any setup-node step without package-manager-cache: false. Before this change, the check failed whenever ci-status had no setup-node step.
  • setup-pinned-node/README.md documents the root ./.github/... form and when it must not be used.
  • No hold-floor, hold-path or sync-hold-floor.py change, per the owner's decision for this phase.

Verification

Run locally on the head commit:

  • actionlint on the 6 edited workflows: exit 0.
  • zizmor --offline on the 6 edited workflows: no findings.
  • scripts/ci-lanes-pr-hold.test.sh, ci-hold-cost-decision, ci-rerun-ci-status, setup-pinned-node, and every scripts/*.test.sh whose test-scope covers these workflows (21 more, among them ci-review-lanes, run-zizmor, workflow-self-paths, ci-hold-merge-group): all pass.
  • node --test .github/actions/resolve-config/callers.test.mjs .github/scripts/pr-merge/workflow.test.mjs: pass (200 tests in callers).
  • Static proof: git grep -n -A3 actions/setup-node -- .github/workflows | grep node-version-file returns only maintenance-audit-test-selection.yml.
  • Pre-push check-affected: pass.

Metrics (§9)

Expected drop in pool-B GITHUB_TOKEN spend: about 880 calls/h, mostly from pr-require-checks (about 647/h) and the two hosted review workflows (about 210/h). The parent measures it before and after merge with briefs/poolB-baseline.md: the hourly job-token spend over matched 24 h windows, plus one step log per converted workflow showing Found in cache @ or a nodejs.org download, and no Acquiring 24.21.0 ... from https://github.com/actions/node-versions/.

Related

  • #10840: the mechanism and the first batch.
  • #10966: edits other hunks of pr-require-checks.yml and the hosted review files. There should be no textual conflict, since this PR adds no hold-list change.

🤖 Generated with Claude Code

Switch the 16 remaining actions/setup-node steps that read .node-version to
the local setup-pinned-node action, so they stop spending two job-token
manifest reads per step on a tool-cache miss.

- pr-require-checks: the six lanes whose root checkout is the code under
  test use ./.github/actions/setup-pinned-node; ci-status uses main's copy
  from its sparse .base checkout.
- pr-review-hosted, pr-review-security-hosted, pr-review-main and
  eval-browser-dispatch use ./.base/... (main's copy).
- maintenance-check-upstream-drift uses the root copy (schedule or dispatch).
- maintenance-audit-test-selection stays on setup-node (cache: npm, about
  0 calls/h).
- ci-lanes-pr-hold.test.sh accepts a ci-status with no setup-node step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 29, 2026 22:25
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T22:27:00.566422Z 4023ec2 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

claude-review reviewed 4023ec2fd27c16cb564cf5ccdd16452edcbae76a: 0 finding(s), 0 as inline comments.

No findings.

I read the whole diff (8 files) and the PR context. I checked the changes against the files they touch. I did not run anything, so the PR's lint and test results are author-claimed and unverified by me.

  • Root form (./.github/actions/setup-pinned-node) in pr-require-checks.yml: lint-repo, check-plugins, check-catalog, test-bash, test-python and test-node each check out the PR at the root with contents: read and persist-credentials: false. They already run PR code and already use root local actions such as ./.github/actions/download-full-history. The root form adds no new trust exposure. These jobs do a full checkout, so the action directory is present.
  • .base form: each .base checkout I read is main's code or a sparse checkout of it.
    • ci-status and the hosted review workflows add /.github/actions/setup-pinned-node/ to their sparse checkouts. That covers action.yml and install-node.sh.
    • pr-review-main.yml checks out refs/heads/main or the matrix base at .base, with no sparse list, so the action is present there.
    • eval-browser-dispatch.yml uses the default branch on pull_request and github.sha otherwise. It never uses the .head checkout, which is a separate path.
  • maintenance-check-upstream-drift.yml: it runs only on schedule or workflow_dispatch, so the root checkout is main or a ref chosen by someone with write access.
  • Dropping package-manager-cache: false: the action declares only the node-version-file input and restores no cache, so dropping it is safe.
  • scripts/ci-lanes-pr-hold.test.sh: with no actions/setup-node step in ci-status, both counts are 0 and the check passes. A setup-node step without package-manager-cache: false still fails it. The test passing when no setup-node step exists is the intended change.
  • README: it documents the root form and the case where it must not be used.

Cost: this change only reduces GitHub API calls and tool-cache misses. It starts no billed resource, so there is no hold.

@github-actions

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 4023ec2fd27c16cb564cf5ccdd16452edcbae76a: 0 finding(s), 0 as inline comments.

I found no security issues in this pull request, so critical-findings: 0. I read the diff and the files it gates, and did not run any workflows.

What the change does. It replaces 16 actions/setup-node steps with the local setup-pinned-node action. It adds that action's path to the sparse .base checkouts that now use it.

Where the new action runs from

  • Privileged or main-trust jobs: the ci-status job in pr-require-checks.yml, the hosted review jobs (resolve-model and kill-switch), pr-review-main.yml and eval-browser-dispatch.yml all use ./.base/.github/actions/setup-pinned-node. .base is main's checkout (ref: refs/heads/main, or the default branch for ci-status) with persist-credentials: false, so a pull request cannot change the code these jobs run. ci-status is the job with write scopes (actions, pull-requests, statuses).
  • Jobs that already run pull request code: six pr-require-checks.yml jobs and maintenance-check-upstream-drift.yml use ./.github/actions/setup-pinned-node.
    • The six pr-require-checks.yml jobs run on pull_request or merge_group with only contents: read (workflow-level permissions: at line 70). A pull request that edits the action runs its own code there, but those jobs already run the pull request's scripts and tests. This adds no new reach.
    • maintenance-check-upstream-drift.yml runs on main (schedule, or dispatch by someone with write access).
  • The README warns against the root form in a job that runs with main's trust and has a pull request checked out at the root.

Why a shared tool cache doesn't add a path. The runners are GitHub-hosted ubuntu-24.04 and ephemeral. Code from a pull request job cannot plant a Node build that a privileged job later accepts as "Found in cache".

Supply chain. install-node.sh requires an exact X.Y.Z pin and fetches SHASUMS256.txt and the tarball from nodejs.org over HTTPS. It checks the SHA-256 and the reported version before putting Node on PATH. actions/setup-node was pinned to a SHA; the local action replaces that with the repository's own script and a checksum fetched from the same origin as the tarball.

Test change. The ci-lanes-pr-hold.test.sh check for ci-status now passes when there is no setup-node step. It still fails on any setup-node step that lacks package-manager-cache: false. This does not weaken a merge hold.

Trust controls. No hold script, trusted-actor list, kill-switch action, token request, or pr-pipeline.yaml setting is edited. The check-kill-switch and resolve-config code that runs is unchanged and still comes from main. No manifest files changed, and no instruction-surface text was removed.

Spend. The change only lowers GitHub token API calls. It adds no billed resource, so I did not set hold.

@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 670907d Oct 29, 2026
89 checks passed
@kyle-sexton
kyle-sexton deleted the perf/setup-pinned-node-followup branch October 29, 2026 23:10
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.

1 participant