ci: clear Node 20 deprecation warnings by bumping action runtimes - #1407
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe development container now uses Node 22, while CI and release workflows upgrade their GitHub Actions, pnpm setup, Node setup, path filtering, artifact upload, and GitHub App token actions. ChangesRuntime and workflow action upgrades
Estimated code review effort: 1 (Trivial) | ~5 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Line 28: Update checkout steps at .github/workflows/ci.yml:28-28,
.github/workflows/ci.yml:71-71, .github/workflows/ci.yml:126-126,
.github/workflows/ci.yml:168-168, and .github/workflows/ci.yml:226-226 to set
persist-credentials to false. In .github/workflows/release-prepare.yml:32-35 and
.github/workflows/release-prepare.yml:73-75, keep App and default GitHub tokens
unavailable during dependency installation, then explicitly re-authenticate
immediately before the changesets/tag-push release step.
In @.github/workflows/release-prepare.yml:
- Around line 27-30: Update the actions/create-github-app-token@v3 step to add
explicit permission-* inputs, granting only the minimum repository permissions
required by the workflow’s checkout, version-update, and release steps. Remove
reliance on the GitHub App installation’s inherited full permission set while
preserving the existing app-id and private-key configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9f44614a-ba40-42f0-a13e-8fa10dfdaf58
📒 Files selected for processing (3)
.devcontainer/devcontainer.json.github/workflows/ci.yml.github/workflows/release-prepare.yml
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| uses: actions/checkout@v5 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== ci.yml ==\n'
nl -ba .github/workflows/ci.yml | sed -n '1,260p'
printf '\n== release-prepare.yml ==\n'
nl -ba .github/workflows/release-prepare.yml | sed -n '1,220p'Repository: Fission-AI/OpenSpec
Length of output: 211
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 'actions/checkout@v5|persist-credentials|token:|git push|changesets|release' .github/workflows/ci.yml .github/workflows/release-prepare.ymlRepository: Fission-AI/OpenSpec
Length of output: 15174
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== ci.yml top ==\n'
sed -n '1,90p' .github/workflows/ci.yml
printf '\n== release-prepare.yml relevant sections ==\n'
sed -n '1,190p' .github/workflows/release-prepare.ymlRepository: Fission-AI/OpenSpec
Length of output: 8951
Avoid persisting checkout credentials during installs. actions/checkout writes the token into git config by default, so pnpm install runs with writable release credentials in both release-prepare jobs. Set persist-credentials: false on the CI checkouts, and in release-prepare.yml authenticate only for the changesets/tag-push steps.
.github/workflows/ci.yml: addpersist-credentials: falseto the checkout steps..github/workflows/release-prepare.yml: don’t keep the App token or defaultGITHUB_TOKENavailable during dependency install; re-authenticate right before the git push / release step.
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 27-28: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
📍 Affects 2 files
.github/workflows/ci.yml#L28-L28(this comment).github/workflows/ci.yml#L71-L71.github/workflows/ci.yml#L126-L126.github/workflows/ci.yml#L168-L168.github/workflows/ci.yml#L226-L226.github/workflows/release-prepare.yml#L32-L35.github/workflows/release-prepare.yml#L73-L75
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml at line 28, Update checkout steps at
.github/workflows/ci.yml:28-28, .github/workflows/ci.yml:71-71,
.github/workflows/ci.yml:126-126, .github/workflows/ci.yml:168-168, and
.github/workflows/ci.yml:226-226 to set persist-credentials to false. In
.github/workflows/release-prepare.yml:32-35 and
.github/workflows/release-prepare.yml:73-75, keep App and default GitHub tokens
unavailable during dependency installation, then explicitly re-authenticate
immediately before the changesets/tag-push release step.
Source: Linters/SAST tools
There was a problem hiding this comment.
Applied the ci.yml half: all five checkouts now set persist-credentials: false — safe because every ci.yml job is read-only after checkout (the one git diff origin/main...HEAD consumer uses fetch-depth: 0, so all refs land during the checkout action itself; dorny/paths-filter authenticates via its API token input, not git config).
Deliberately NOT restructuring release-prepare.yml auth in this PR: the App token in those checkouts is intentional (see #477/#478 — changesets pushes must be authored by the App so they trigger CI), the release path only executes on the next version PR / beta dispatch so a restructure is unverifiable from this PR, and this configuration predates the version bumps here. Happy to tackle it as its own reviewed change if the maintainers want.
There was a problem hiding this comment.
@clay-good, confirmed: all five CI checkout steps now disable credential persistence, and the fetch-depth: 0 checkout preserves the origin/main...HEAD validation path.
Your rationale for keeping the intentionally App-authenticated release-prepare.yml flow out of this action-version PR is reasonable. The remaining release-token hardening is best evaluated and validated as a dedicated change, since preserving App-authored Changesets pushes is a functional requirement.
Would you like me to open a follow-up GitHub issue for that review?
🐇 💭
✏️ Learnings added
Learnt from: clay-good
Repo: Fission-AI/OpenSpec PR: 1407
File: .github/workflows/ci.yml:28-28
Timestamp: 2026-07-21T14:07:08.006Z
Learning: In `.github/workflows/release-prepare.yml`, the checkout’s GitHub App token is intentionally persisted because Changesets tag/release pushes must be authored by the GitHub App to trigger CI (referenced as GitHub issues `#477` and `#478`). Any authentication hardening there must preserve that App-authored push behavior and should be evaluated as a separately validated change.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| uses: actions/create-github-app-token@v3 | ||
| with: | ||
| app-id: ${{ vars.APP_ID }} | ||
| private-key: ${{ secrets.APP_PRIVATE_KEY }} |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🌐 Web query:
actions/create-github-app-token v3 permissions inputs install token inherits all permissions
💡 Result:
In the actions/create-github-app-token action, including v3, the token inherits all permissions from the app installation by default if no specific permission-* inputs are provided [1][2][3]. When you explicitly define permission-* inputs, the action limits the generated token's permissions to the specified subset [3]. If you provide no permission-* inputs, the action defaults to requesting a token with all permissions currently granted to the GitHub App installation [1][3]. Important points regarding this behavior: 1. Inheritance: If no permission-* inputs are set, the action generates a token with the full set of permissions defined in the app installation [1][2][3]. 2. Restriction: If you use the permission-* inputs, the generated token is restricted to the permissions you explicitly defined [3]. These defined permissions must still be a subset of the permissions already granted to the app installation; otherwise, the action will return an error [1][2][4]. 3. Best Practice: The action's documentation recommends explicitly listing only the permissions required for your specific use case to follow the principle of least privilege, rather than relying on the default inheritance of all permissions [1][2].
Citations:
- 1: https://github.com/actions/create-github-app-token
- 2: https://github.com/actions/create-github-app-token/blob/main/README.md
- 3: https://deepwiki.com/actions/create-github-app-token/4.2-permission-system
- 4: https://github.com/marketplace/actions/create-github-app-token
Restrict the GitHub App token permissions.
Without permission-* inputs, create-github-app-token@v3 inherits the app installation’s full permission set. Limit it to the minimum needed for checkout, version updates, and releases.
🧰 Tools
🪛 zizmor (1.26.1)
[error] 27-27: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions
(github-app)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release-prepare.yml around lines 27 - 30, Update the
actions/create-github-app-token@v3 step to add explicit permission-* inputs,
granting only the minimum repository permissions required by the workflow’s
checkout, version-update, and release steps. Remove reliance on the GitHub App
installation’s inherited full permission set while preserving the existing
app-id and private-key configuration.
Source: Linters/SAST tools
There was a problem hiding this comment.
Declining in this PR, with reasoning: the unscoped App token predates this change (v2 had the same default — the bump to v3 doesn't widen anything), and scoping it needs knowledge of the App installation's actual permission set plus a live release run to validate — neither is available from this PR (the release path only executes on the next version PR / beta dispatch). Getting the permission-* scopes wrong here would break version-PR creation or tag pushes silently. Flagging it to the maintainers as a candidate follow-up: likely permission-contents: write + permission-pull-requests: write, validated against a real release cycle.
There was a problem hiding this comment.
@clay-good, agreed—this is pre-existing configuration rather than a permission expansion caused by the v2 → v3 upgrade. Deferring explicit App-token scoping until the installation permissions and a real release-path validation are available is reasonable.
Would you like me to open a follow-up GitHub issue for the least-privilege hardening, including validation of permission-contents: write and permission-pull-requests: write against an actual release cycle?
🐇💭
✏️ Learnings added
Learnt from: clay-good
Repo: Fission-AI/OpenSpec PR: 1407
File: .github/workflows/release-prepare.yml:27-30
Timestamp: 2026-07-21T14:07:13.648Z
Learning: In `.github/workflows/release-prepare.yml`, the `prepare` job’s GitHub App token intentionally remains unscoped during the `actions/create-github-app-tokenv2` to `v3` upgrade because this is pre-existing behavior, not a regression. Explicit `permission-*` scopes should be handled as a separate hardening task after confirming the GitHub App installation permissions and validating version-PR creation and tag pushes through a real release cycle.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alfred-openspec
left a comment
There was a problem hiding this comment.
Reviewed at 37cebb7. Each bumped tag exists and declares node24, the inputs used here remain compatible, CI checkouts no longer persist credentials, the clean build passes, and the full exact-head CI, Nix, CodeQL, and CodeRabbit matrix is green.
Status: Ready for review. Follow-up to the sweep suggested by @TabishB in #1406 review. Version-tag bumps plus one review-driven hardening — no workflow logic, job structure, or filenames change.
What was missing / the motivation: Every JS action we use except
changesets/actionstill declares a Node 20 runtime. GitHub deprecated Node 20 on runners (2025-09-19 changelog): since June 16, 2026 these actions are force-run on Node 24 with the warning quoted in #1406, and on September 16, 2026 Node 20 is removed from runners entirely. The devcontainer also pinned a Node 20 image (Node 20 is EOL since April 2026).What it does: Bumps each action to its current Node 24 major, verified against each action's
action.ymland release notes before choosing the target:actions/checkoutactions/setup-nodecache: 'pnpm'explicitly.node-versionandregistry-urlinputs unchanged.pnpm/action-setuppackageManager: "pnpm@9.15.9"exactly as before; no call site passes aversioninput.actions/upload-artifactdorny/paths-filteractions/create-github-app-tokenchangesets/actionAlso bumps
.devcontainerimagetypescript-node:1-20-bookworm→1-22-bookworm.Per CodeRabbit's review, the five
ci.ymlcheckouts now setpersist-credentials: false— safe because everyci.ymljob is read-only after checkout (the changeset-validation job'sgit diff origin/main...HEADworks offfetch-depth: 0, fetched during the checkout action itself). The equivalent restructure inrelease-prepare.ymlwas deliberately declined there: its persisted App token is intentional (#477/#478 — changesets pushes must trigger CI) and unverifiable from a PR.Deliberately untouched:
node-version: '20.19.0'in CI (floor-testing the declaredengines: >=20.19.0support contract — the runtime warnings are about the actions' own runtime, which is orthogonal),node-version: '24'in release,package.jsonengines, docs, and therelease-prepare.ymlfilename (npm trusted publishing binds to repo + workflow filename, not to action versions inside it).Proof it works: This PR edits
ci.yml, so the full CI matrix — includingnix-flake-validatevia the paths filter — runs on these exact changes. Caveat: the release-prepare path only executes on the next version PR / beta dispatch; the bumps there are tag-only with inputs verbatim.Notes: The repo has no
.github/dependabot.yml, which is why six actions aged into deprecation together — happy to add a monthlygithub-actionsecosystem entry in a follow-up if wanted.🤖 Generated with Claude Code
Summary by CodeRabbit