From 78c5da576e2782892c2162e01dc9f37fadaf4c51 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Thu, 10 Sep 2026 17:36:24 -0700 Subject: [PATCH 1/3] ci: pin every action by SHA to a node24 release GitHub removes the Node 20 action runtime on September 23, 2026. Every `uses:` in .github/workflows now points at the full commit SHA of the latest release of its major, and each pinned action.yml was read to confirm `runs.using: node24` (composite actions have no runtime). Before this change the publish workflows pinned actions/checkout v4.3.1, actions/setup-node v4.4.0, pnpm/action-setup v4.3.0, upload-artifact v4.6.2 and download-artifact v4.3.0, all node20, and the remaining workflows floated on `@v4`, `@v6` and `@v7` tags. Bumps crossing a major were checked against the release notes: - checkout v7 refuses fork PR checkouts under pull_request_target and workflow_run. The only workflow_run checkouts here (release-cli-nightly) are gated on `event == 'push'` first, and the guard returns before it looks at the ref unless the triggering event was a pull_request. - setup-node v5/v6 auto-cache only when package.json names npm; this workspace names pnpm, so nothing changes. v6.1 stops writing `always-auth` to .npmrc and v7 stops exporting a dummy NODE_AUTH_TOKEN. - download-artifact v5 changed the path only for single downloads by ID; release-vale downloads by name. v8 errors on digest mismatch by default. - github-script v9 drops `require('@actions/github')`; the scripts here use `github.rest.*`, `github.paginate` and relative `require` only. - changesets/action stays at v1.9.0, already node24. v2 renames every input and moves to Changesets v3, which is a migration, not a pin. --- .github/workflows/changeset.yml | 2 +- .../workflows/claude-code-review-on-demand.yml | 4 ++-- .github/workflows/demo-rule.yml | 6 +++--- .github/workflows/openspec-label.yml | 2 +- .github/workflows/openspec-sweep.yml | 2 +- .github/workflows/openspec-tracking.yml | 2 +- .github/workflows/release-cli-changeset.yml | 8 ++++---- .github/workflows/release-cli-nightly.yml | 14 +++++++------- .github/workflows/release-cli.yml | 10 +++++----- .github/workflows/release-vale.yml | 18 +++++++++--------- .github/workflows/stack-breadcrumb.yml | 16 ++++++++-------- .github/workflows/update-badges.yml | 4 ++-- .github/workflows/validate.yml | 6 +++--- 13 files changed, 47 insertions(+), 47 deletions(-) diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index d9d97429..113c88a9 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -36,7 +36,7 @@ jobs: name: Changeset runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # need `origin/main` to diff the whole stack against diff --git a/.github/workflows/claude-code-review-on-demand.yml b/.github/workflows/claude-code-review-on-demand.yml index 6cf0cedb..a8705f0c 100644 --- a/.github/workflows/claude-code-review-on-demand.yml +++ b/.github/workflows/claude-code-review-on-demand.yml @@ -147,7 +147,7 @@ jobs: # build/lint/test, and every allowed tool reads the diff through `gh` # (the API), not through local history. - name: Checkout PR head - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: refs/pull/${{ steps.prep.outputs.pr }}/head fetch-depth: 1 @@ -212,7 +212,7 @@ jobs: # comment, so there's no explicit reaction step here. - name: Run Claude Code Review id: review - uses: anthropics/claude-code-action@d40ddef4c030e508327d6e35a9c45f3368482c50 # v1 + uses: anthropics/claude-code-action@0a8d3c9443bbff909ab973b6a17a340b913f229f # v1.0.221 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # Single tracking comment (in-progress → results), updated in place. diff --git a/.github/workflows/demo-rule.yml b/.github/workflows/demo-rule.yml index 85c909e3..04a5d975 100644 --- a/.github/workflows/demo-rule.yml +++ b/.github/workflows/demo-rule.yml @@ -49,13 +49,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - name: Setup Node - uses: actions/setup-node@v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 diff --git a/.github/workflows/openspec-label.yml b/.github/workflows/openspec-label.yml index 774c85e0..740fc446 100644 --- a/.github/workflows/openspec-label.yml +++ b/.github/workflows/openspec-label.yml @@ -45,7 +45,7 @@ jobs: name: OpenSpec Label runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # The head commit, not the merge ref: the question is what this branch # carries. A child branch contains its ancestors' commits, so the head diff --git a/.github/workflows/openspec-sweep.yml b/.github/workflows/openspec-sweep.yml index 24fd05b3..8a6f149f 100644 --- a/.github/workflows/openspec-sweep.yml +++ b/.github/workflows/openspec-sweep.yml @@ -45,7 +45,7 @@ jobs: name: OpenSpec Sweep runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # full history, so `git log` can date each change directory diff --git a/.github/workflows/openspec-tracking.yml b/.github/workflows/openspec-tracking.yml index db5bdadf..9cab94f7 100644 --- a/.github/workflows/openspec-tracking.yml +++ b/.github/workflows/openspec-tracking.yml @@ -46,7 +46,7 @@ jobs: name: OpenSpec Tracking runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Plan tracking issues id: plan diff --git a/.github/workflows/release-cli-changeset.yml b/.github/workflows/release-cli-changeset.yml index 79fa4eff..9cba9f6c 100644 --- a/.github/workflows/release-cli-changeset.yml +++ b/.github/workflows/release-cli-changeset.yml @@ -47,11 +47,11 @@ jobs: contents: write # push the changeset-release/main branch pull-requests: write # open/update the Version Packages PR steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: pnpm @@ -64,7 +64,7 @@ jobs: # is dead until `release-cli.yml` creates the tag after publishing, which # is expected: the notes and the link they describe belong in the same PR. # No `publish:` input — this job can never publish. - - uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1 + - uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0 with: version: pnpm bump commit: "chore: version packages" diff --git a/.github/workflows/release-cli-nightly.yml b/.github/workflows/release-cli-nightly.yml index f1c3a46d..ec05f21b 100644 --- a/.github/workflows/release-cli-nightly.yml +++ b/.github/workflows/release-cli-nightly.yml @@ -266,7 +266,7 @@ jobs: # `ref:` is MANDATORY under workflow_run. Without it checkout takes # `github.sha`, which here is the default branch tip at event time, not # the commit Validate tested. - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.workflow_run.head_sha }} persist-credentials: false # nothing here writes to git @@ -292,7 +292,7 @@ jobs: exit 1 fi echo "Building from ${actual}, which Validate passed on." - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 @@ -424,7 +424,7 @@ jobs: # again here rather than trusted from there. This is the job that produces # the bytes: `gate` proved that some job checked out the validated commit, # not that this one did. - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.workflow_run.head_sha }} persist-credentials: false # publish authenticates via OIDC, not git creds @@ -508,8 +508,8 @@ jobs: - name: Give changesets the `main` ref it diffs against run: git branch -f main HEAD - - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: pnpm @@ -630,7 +630,7 @@ jobs: # SCRIPT that is not the one Validate passed on — while announcing a # nightly built from a different commit. Cosmetic output does not lower # the bar; it is the same fail-open, and it reads as a normal run. - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.workflow_run.head_sha }} persist-credentials: false # gh authenticates with GITHUB_TOKEN below @@ -653,7 +653,7 @@ jobs: exit 1 fi echo "Annotating from ${actual}, which Validate passed on." - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 9caf8c21..50bcfef0 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -73,10 +73,10 @@ jobs: publish: ${{ steps.check.outputs.publish }} version: ${{ steps.check.outputs.version }} steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # no git writes here; don't leave the token in git config - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 - id: check @@ -113,11 +113,11 @@ jobs: contents: read # checkout only id-token: write # OIDC → short-lived npm auth + build provenance steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # publish authenticates via OIDC/npm, not git creds - - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 cache: pnpm diff --git a/.github/workflows/release-vale.yml b/.github/workflows/release-vale.yml index a516adac..f4556c89 100644 --- a/.github/workflows/release-vale.yml +++ b/.github/workflows/release-vale.yml @@ -147,8 +147,8 @@ jobs: steps: # Credentials persist here because this job pushes a branch. It holds no # npm identity and no id-token, and it never runs downloaded code. - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 @@ -233,10 +233,10 @@ jobs: outputs: should_publish: ${{ steps.gate.outputs.should_publish }} steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 # --force on dispatch: an explicit human request publishes even when the @@ -259,10 +259,10 @@ jobs: version: ${{ steps.prepare.outputs.version }} vale_version: ${{ steps.prepare.outputs.vale_version }} steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false # nothing here writes to git - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 @@ -279,7 +279,7 @@ jobs: # had just written six tarballs there. `if-no-files-found: error` is what # turned that into a failed run rather than an empty artifact handed to # the publish job; keep both. - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: vale-tarballs path: .vale-dist/*.tgz @@ -330,11 +330,11 @@ jobs: # Deliberately no checkout. This job publishes tarballs the previous job # already verified and sealed; it has no reason to hold repository source # while an OIDC identity exists. - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 registry-url: https://registry.npmjs.org - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: vale-tarballs path: tarballs diff --git a/.github/workflows/stack-breadcrumb.yml b/.github/workflows/stack-breadcrumb.yml index 0f07ac0b..9df7daf1 100644 --- a/.github/workflows/stack-breadcrumb.yml +++ b/.github/workflows/stack-breadcrumb.yml @@ -80,11 +80,11 @@ jobs: # Check out the DEFAULT branch's copy of the script, not the PR head: this # privileged job (contents: write) must run trusted, reviewed logic, never # PR-supplied code. - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.repository.default_branch }} - - uses: actions/github-script@v7 + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const scriptPath = `${process.env.GITHUB_WORKSPACE}/.github/scripts/stack-breadcrumb.cjs`; @@ -159,11 +159,11 @@ jobs: cancel-in-progress: false steps: # Trusted script from the default branch — never PR-supplied code. - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.repository.default_branch }} - - uses: actions/github-script@v7 + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const scriptPath = `${process.env.GITHUB_WORKSPACE}/.github/scripts/stack-breadcrumb.cjs`; @@ -238,11 +238,11 @@ jobs: cancel-in-progress: false steps: # Trusted script from the default branch — never PR-supplied code. - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.repository.default_branch }} - - uses: actions/github-script@v7 + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const scriptPath = `${process.env.GITHUB_WORKSPACE}/.github/scripts/stack-breadcrumb.cjs`; @@ -323,9 +323,9 @@ jobs: group: stack-reconcile-${{ github.event.client_payload.root || github.event.inputs.root || 'all' }} cancel-in-progress: true steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/github-script@v7 + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const stack = require(`${process.env.GITHUB_WORKSPACE}/.github/scripts/stack-breadcrumb.cjs`); diff --git a/.github/workflows/update-badges.yml b/.github/workflows/update-badges.yml index 05d72146..50ddfc2d 100644 --- a/.github/workflows/update-badges.yml +++ b/.github/workflows/update-badges.yml @@ -86,9 +86,9 @@ jobs: steps: # Credentials persist because this job pushes. It holds no npm identity, # no id-token, and runs nothing it downloaded. - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index bc9d1b03..4cb5679d 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -43,13 +43,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install pnpm - uses: pnpm/action-setup@v6 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - name: Setup Node - uses: actions/setup-node@v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 From 055661300467550ed68ea2ae0dcef05a0a0bc188 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Thu, 10 Sep 2026 17:37:07 -0700 Subject: [PATCH 2/3] ci: record the setup-node floor on each OIDC publish step The `npm warn Unknown user config "always-auth"` line in issue #294 was written by actions/setup-node v4.4.0: its authutil appends `always-auth=` to the .npmrc it creates for `registry-url`, and npm 11 warns that the option is unknown and the next major refuses it. setup-node v6.1.0 removed that line (actions/setup-node#1436) and v7.0.0 stopped exporting the dummy NODE_AUTH_TOKEN (actions/setup-node#1558), so after the pin bump the OIDC exchange is the only credential in each publish job. Nothing in the workflows referenced the option, so the previous commit already fixes it; this one leaves a note beside each publish step so the pin is not lowered again. --- .github/workflows/release-cli-nightly.yml | 3 +++ .github/workflows/release-cli.yml | 7 +++++++ .github/workflows/release-vale.yml | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/.github/workflows/release-cli-nightly.yml b/.github/workflows/release-cli-nightly.yml index ec05f21b..f6ed57e2 100644 --- a/.github/workflows/release-cli-nightly.yml +++ b/.github/workflows/release-cli-nightly.yml @@ -509,6 +509,9 @@ jobs: run: git branch -f main HEAD - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + # setup-node >= v7 is the floor here; release-cli.yml's publish job + # explains why (`always-auth` in .npmrc until v6.1, a dummy + # NODE_AUTH_TOKEN in env until v7). - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 50bcfef0..11bbc81e 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -117,6 +117,13 @@ jobs: with: persist-credentials: false # publish authenticates via OIDC/npm, not git creds - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + # setup-node >= v7 is the floor for this step, not just the current pin. + # `registry-url` writes the .npmrc npm publishes through. Up to v6.0 that + # file also carried `always-auth=false`, which npm 11 warns is an unknown + # option and the next npm major refuses (issue #294); v6.1 stopped + # writing it. Up to v6 the step also exported a dummy NODE_AUTH_TOKEN + # (XXXXX-XXXXX-XXXXX-XXXXX) into every later step; v7 exports nothing, + # so the OIDC exchange below is the only credential this job ever holds. - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 diff --git a/.github/workflows/release-vale.yml b/.github/workflows/release-vale.yml index f4556c89..a0464351 100644 --- a/.github/workflows/release-vale.yml +++ b/.github/workflows/release-vale.yml @@ -330,6 +330,11 @@ jobs: # Deliberately no checkout. This job publishes tarballs the previous job # already verified and sealed; it has no reason to hold repository source # while an OIDC identity exists. + # + # setup-node >= v7 is the floor here; release-cli.yml's publish job + # explains why (`always-auth` in .npmrc until v6.1, a dummy + # NODE_AUTH_TOKEN in env until v7). The warning in issue #294 came from + # this step's .npmrc under v4.4.0. - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 From d3b451e09dbe3cf49a358871a34fab40baf3319f Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Thu, 10 Sep 2026 17:39:43 -0700 Subject: [PATCH 3/3] ci: read the build Node version from .nvmrc `node-version: 24` was repeated in thirteen setup-node steps across seven workflows with no single source. The number now lives in .nvmrc and every step that follows a checkout reads it with `node-version-file`. The one exception is the Vale publish job, which deliberately checks nothing out while it holds an OIDC identity, so there is no file for setup-node to read; it keeps a literal `node-version: 24` with a comment pointing at .nvmrc. 24 is the choice because it is the Active LTS line today (nodejs/Release schedule.json: LTS since 2025-10-28, maintenance from 2026-10-20). The Current line, 26, does not become LTS until 2026-10-28, seven weeks out, and the local toolchain runs 24.14.0. packages/cli's `engines.node` is a consumer-facing floor and is untouched. --- .github/workflows/demo-rule.yml | 2 +- .github/workflows/release-cli-changeset.yml | 2 +- .github/workflows/release-cli-nightly.yml | 6 +++--- .github/workflows/release-cli.yml | 4 ++-- .github/workflows/release-vale.yml | 10 +++++++--- .github/workflows/update-badges.yml | 2 +- .github/workflows/validate.yml | 2 +- .nvmrc | 1 + 8 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 .nvmrc diff --git a/.github/workflows/demo-rule.yml b/.github/workflows/demo-rule.yml index 04a5d975..cb5c6584 100644 --- a/.github/workflows/demo-rule.yml +++ b/.github/workflows/demo-rule.yml @@ -57,7 +57,7 @@ jobs: - name: Setup Node uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/release-cli-changeset.yml b/.github/workflows/release-cli-changeset.yml index 9cba9f6c..98e78c65 100644 --- a/.github/workflows/release-cli-changeset.yml +++ b/.github/workflows/release-cli-changeset.yml @@ -53,7 +53,7 @@ jobs: - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc cache: pnpm - run: pnpm install --frozen-lockfile --ignore-scripts diff --git a/.github/workflows/release-cli-nightly.yml b/.github/workflows/release-cli-nightly.yml index f6ed57e2..02721881 100644 --- a/.github/workflows/release-cli-nightly.yml +++ b/.github/workflows/release-cli-nightly.yml @@ -294,7 +294,7 @@ jobs: echo "Building from ${actual}, which Validate passed on." - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc # No dependency install in this job, by design — gate 1 is the reason the # workflow can decide before installing anything, and nightly-pack.cjs is @@ -514,7 +514,7 @@ jobs: # NODE_AUTH_TOKEN in env until v7). - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc cache: pnpm registry-url: https://registry.npmjs.org # --ignore-scripts: no dependency lifecycle code runs while an OIDC @@ -658,7 +658,7 @@ jobs: echo "Annotating from ${actual}, which Validate passed on." - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc # A LOST UPDATE IS POSSIBLE HERE, AND IS ACCEPTED (Copilot review, #133). # A pull request body is replaced whole: GitHub offers no field-level diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 11bbc81e..57335803 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -78,7 +78,7 @@ jobs: persist-credentials: false # no git writes here; don't leave the token in git config - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc - id: check run: | name=$(node -p "require('./packages/cli/package.json').name") @@ -126,7 +126,7 @@ jobs: # so the OIDC exchange below is the only credential this job ever holds. - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc cache: pnpm registry-url: https://registry.npmjs.org - run: pnpm install --frozen-lockfile --ignore-scripts diff --git a/.github/workflows/release-vale.yml b/.github/workflows/release-vale.yml index a0464351..4b8364f2 100644 --- a/.github/workflows/release-vale.yml +++ b/.github/workflows/release-vale.yml @@ -150,7 +150,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc # No install step: the script is zero-dependency CommonJS. GITHUB_TOKEN is # passed only to raise the GitHub API rate limit; the endpoints are public. @@ -238,7 +238,7 @@ jobs: persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc # --force on dispatch: an explicit human request publishes even when the # pinned version is already out. Only the automatic push path is gated. - id: gate @@ -264,7 +264,7 @@ jobs: persist-credentials: false # nothing here writes to git - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc # No dependency install at all: the script is zero-dependency CommonJS and # unpacks with `tar` and `unzip`, both present on ubuntu-latest. Nothing @@ -335,6 +335,10 @@ jobs: # explains why (`always-auth` in .npmrc until v6.1, a dummy # NODE_AUTH_TOKEN in env until v7). The warning in issue #294 came from # this step's .npmrc under v4.4.0. + # + # A literal major rather than `node-version-file: .nvmrc`, because there + # is no checkout here for the file to be read from. Keep it equal to + # .nvmrc; this is the one place in the workflows the number is repeated. - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 diff --git a/.github/workflows/update-badges.yml b/.github/workflows/update-badges.yml index 50ddfc2d..99f8609a 100644 --- a/.github/workflows/update-badges.yml +++ b/.github/workflows/update-badges.yml @@ -90,7 +90,7 @@ jobs: - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc # No install step: the scripts are zero-dependency CommonJS. GITHUB_TOKEN # is passed only to raise the GitHub API rate limit on the Vale release diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 4cb5679d..d32b3256 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -51,7 +51,7 @@ jobs: - name: Setup Node uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version-file: .nvmrc - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..a45fd52c --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24