10 KiB
created, status
| created | status |
|---|---|
| 2026-08-07 | draft |
Implementation Plan: Upgrade GitHub Actions to the Node 24 runtime
Overview
GitHub deprecated the Node 20 action runtime; every pinned action whose
action.yml declares runs.using: node20 now runs forced on Node 24 and
emits a deprecation warning on every workflow run. This plan upgrades every
Node-20 action pin in .github/workflows/ to a SHA-pinned release that
declares runs.using: node24, verifying each target's runtime and
backwards-compatibility before landing. Order: upgrade the plain-JavaScript
actions (checkout, artifacts, cache, github-script) first, then the docker/*
family, then the remaining third-party actions, and finish with a full audit
that no node20 pin remains.
There is no product spec for this change: it is CI-infrastructure hygiene with no user-visible behavior. Validation is mechanical — see audit.md for the runtime-check procedure used to build the table below.
Target version table
Verified on 2026-08-07 via the pinned SHA's action.yml runs.using field
(audit.md documents the exact method). "Target" is the release whose pinned
commit declares node24.
| Action | Current pin | Uses | Target | Target SHA | Runtime |
|---|---|---|---|---|---|
| actions/checkout | 34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 |
7 | v6 (already used in repo) | df4cb1c069e1874edd31b4311f1884172cec0e10 |
node24 |
| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 |
2 | v7.0.1 | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a |
node24 |
| actions/upload-artifact | 330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 |
13 | v7.0.1 | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a |
node24 |
| actions/download-artifact | d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 |
2 | v8.0.1 | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c |
node24 |
| actions/download-artifact | 634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 |
12 | v8.0.1 | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c |
node24 |
| actions/cache | 0057852bfaa89a56745cba8c7296529d2fc39830 # v4 |
8 | v6.1.0 | 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 |
node24 |
| actions/github-script | f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 |
4 | v9.0.0 | 3a2844b7e9c422d3c10d287c895573f7108da1b3 |
node24 |
| docker/login-action | c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 |
9 | v4.6.0 | dbcb813823bdd20940b903addbd779551569679f |
node24 |
| docker/setup-buildx-action | 8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 |
8 | v4.2.0 | bb05f3f5519dd87d3ba754cc423b652a5edd6d2c |
node24 |
| docker/build-push-action | 10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 |
7 | v7.3.0 | 53b7df96c91f9c12dcc8a07bcb9ccacbed38856a |
node24 |
| docker/metadata-action | c299e40c65443455700f0fdfc63efafe5b349051 # v5 |
5 | v6.2.0 | dc802804100637a589fabce1cb79ff13a1411302 |
node24 |
| softprops/action-gh-release | 3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 |
1 | v3.0.2 | 3d0d9888cb7fd7b750713d6e236d1fcb99157228 |
node24 |
| cloudflare/wrangler-action | 9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3 |
1 | v4.0.0 | ebbaa1584979971c8614a24965b4405ff95890e0 |
node24 |
| amannn/action-semantic-pull-request | e32d7e603df1aa1ba07e981f2a23455dee596825 # v5 |
1 | v6.1.1 | 48f256284bd46cdaab1048c3721360e808335d50 |
node24 |
| actions/deploy-pages | d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 |
1 | v5.0.0 | cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 |
node24 |
| actions/configure-pages | 983d7736d9b0ae728b81ab479565c72886d7745b # v5 |
1 | v6.0.0 | 45bfe0192ca1faeb007ade9deae92b16b8254a0d |
node24 |
| actions/upload-pages-artifact | 56afc609e74202658d3ffba0e8f6dda462b719fa # v3 |
1 | v5.0.0 | fc324d3547104276b827a68afc52ff2a11cc49c9 |
composite (inner upload-artifact v7.0.0 = node24) |
| robherley/go-test-action | 3856e4e57831b3b6d6d8d89f91747e5200c533f7 # v0 |
1 | v1.1.0 | 2f859e0c8769d755d3174eecb9af8f64660827f3 |
node24 |
Already node24 / no change: actions/checkout v6 (df4cb1c…), actions/setup-node v6 (48b55a0…), actions/setup-go v6 (924ae3a…), pnpm/action-setup v5 (a8198c4…), crazy-max/ghaction-import-gpg v7.0.0 (2dc316d…), actions/create-github-app-token v3.2.0 (bcd2ba4…), Swatinem/rust-cache v2 (42dc69e…). Composite actions with no direct runtime (anthropics/claude-code-action, orhun/git-cliff-action, dtolnay/rust-toolchain) are left as-is.
Compatibility notes (per action)
actions/checkoutv4→v6: v6 already used 34× in this repo — proven.actions/upload-artifactv4/v5→v7.0.1 andactions/download-artifactv4/v5→v8.0.1: the artifact backend is shared across the v4+ majors on GitHub-hosted runners; upload/download must stay on compatible majors (v7 upload + v8 download both use the v4 artifact API). All uses move together in the same PR, so cross-job download/upload pairing is preserved.actions/github-scriptv7→v9.0.0: v9 breaking change isrequire('@actions/github')no longer working; all four uses in this repo only use injectedgithub/context— audited, norequire('@actions/github')in any script block.docker/login-actionv3→v4.6.0,docker/setup-buildx-actionv3→v4.2.0: Node 24 runtime + ESM; v4 removed deprecated inputs/outputs — repo uses none (with:blocks checked).docker/build-push-actionv6→v7.3.0: v7 removed deprecatedDOCKER_BUILD_NO_SUMMARY/DOCKER_BUILD_EXPORT_RETENTION_DAYSenvs — not used in repo.docker/metadata-actionv5→v6.2.0: list inputs now preserve#inside values — release.yml uses no#in tags/images lists.softprops/action-gh-releasev2→v3.0.2: Node 24 only;files:/body_path:inputs unchanged in v3.cloudflare/wrangler-actionv3→v4.0.0: default wrangler version changes to v4 (latest), but repo pinswranglerVersion: 3.90.0explicitly — behavior preserved.amannn/action-semantic-pull-requestv5→v6.1.1: Node 24 + ESM; inputs unchanged.actions/deploy-pagesv4→v5.0.0,actions/configure-pagesv5→v6.0.0,actions/upload-pages-artifactv3→v5.0.0: Pages trio moved together; v5 upload-pages-artifact's inneractions/upload-artifactis v7.0.0 (node24) instead of v4 (node20).robherley/go-test-actionv0→v1.1.0: Node 24;fromJSONFileinput retained.
Tasks
Task files are grouped by workflow file so each task touches a disjoint set of files (safe to run in any order; wave labels only express suggested order):
Wave 1 (parallel candidates — user authorization required):
- [x] [task-01-test-workflows](task-01-test-workflows.md) — backend-tests.yml, frontend-tests.yml, cargo-audit.yml, e2e-tests.yml
- [x] [task-02-container-workflows](task-02-container-workflows.md) — universal-rebuild.yml, ci-base-image.yml
- [x] [task-03-release-workflow](task-03-release-workflow.md) — release.yml (all Node-20 pins)
- [x] [task-04-review-misc-workflows](task-04-review-misc-workflows.md) — claude.yml, claude-code-review.yml, opencode-code-review.yml, notify-docs.yml, preview-env.yml, pr-title.yml, plugin-registry-index.yml
Wave 2:
- [x] [task-05-verify](task-05-verify.md) — runtime audit + lint + contract tests + YAML sanity
Tests
No product behavior is testable — the changed artifacts are workflow YAML only. Pre-PR validation:
- Runtime audit: rerun the procedure in audit.md (fetch each pinned
action.yml, assertruns.usingisnode24orcomposite) and confirm zeronode20pins remain. - Pinning lint:
python3 .github/scripts/lint-action-pinning.pyandpython3 .github/scripts/lint-action-pinning_test.pystill pass (every new ref is a 40-char SHA). - Workflow contract tests:
python3 .github/scripts/release-workflow-contract_test.py,claude-code-review-workflow-contract_test.py,preview-env-workflow-contract_test.py— the two SHA-asserting contract tests only pincrazy-max/ghaction-import-gpg@2dc316d…andactions/setup-node@48b55a0…, which are unchanged. - YAML sanity:
python3 -c "import yaml,sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]"over every changed workflow. - Runtime smoke (post-PR): watch the first
backend-tests/e2e-tests/frontend-testsrun for the Node-20 deprecation warning; confirm it is gone.
Verification Results
Complete 2026-08-07. All five tasks done; every task's ## Results records the exact commands and outcomes.
- Applied 84
uses:replacements across 14 workflow files (checkout v4→v6, upload/download-artifact v4/v5→v7.0.1/v8.0.1, cache v4→v6.1.0, github-script v7→v9.0.0, docker login/buildx/build-push/metadata v3/v5/v6→v4.6.0/v4.2.0/v7.3.0/v6.2.0, gh-release v2→v3.0.2, wrangler v3→v4.0.0, semantic-pull-request v5→v6.1.1, Pages trio → v6.0.0/v5.0.0/v5.0.0, go-test-action v0→v1.1.0). - Final runtime audit (
/tmp/opencode/audit_final.py): 25 distinct pinned actions; everyruns.usingisnode24orcomposite— zeronode20pins remain. The three annotated-tag-object pins (rust-cache, claude-code-action, pnpm/action-setup) were dereferenced to their commits and re-verified (node24 / composite / node24). python3 .github/scripts/lint-action-pinning.py→ "✓ All 18 workflow file(s) use SHA-pinned action refs.";lint-action-pinning_test.py→ 9 OK.- Contract tests:
release-workflow-contract_test.py→ 24 OK,claude-code-review-workflow-contract_test.py→ 9 OK,preview-env-workflow-contract_test.py→ 1 OK. - YAML parse: 18/18 workflows OK;
git diff --checkclean. - Post-merge smoke: pending — first
backend-tests/e2e-tests/frontend-testsrun after merge should no longer show the Node-20 deprecation warning.
PR Fixup (2026-08-07)
PR #2401 (ci: upgrade GitHub Actions to Node 24 runtime):
- CodeRabbit/zizmor flagged the two v4→v6 checkout blocks in the arm64 release jobs (
docker-arm64,docker-universal-arm64) for missingpersist-credentials: false(artipacked — the checked-out tree feeds the Docker build context). Fixed inb7624fd(ci: disable checkout credential persistence in arm64 release jobs), scoped to exactly the two changed blocks; the other release.yml checkouts already set the flag. - Final head
b7624fd32e43828c0ff50b90e73013fda3bf774e: 47 checks passed, 0 failed, 0 pending,mergeable_state: clean, unresolved threads 0, hidden threads 0, actionable issue comments 0. - Post-merge smoke still pending as above.
Open Questions
None — target SHAs and compatibility are resolved in the table above.