Skip to content

ci: bump the remaining Node20-pinned actions to Node24 majors - #274

Merged
cdeust merged 2 commits into
mainfrom
fix-node24-actions-246
Jul 29, 2026
Merged

ci: bump the remaining Node20-pinned actions to Node24 majors#274
cdeust merged 2 commits into
mainfrom
fix-node24-actions-246

Conversation

@cdeust

@cdeust cdeust commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Closes #246.

Summary

Every uses: in .github/workflows/release.yml, publish-ccplugins.yml,
marketplace-pins.yml, mcp-toplist-badge.yml, sync-ccplugins-fork.yml
and scorecard.yml — the five actions #246 named as still-Node20
(actions/checkout, actions/cache, actions/upload-artifact,
actions/download-artifact, github/codeql-action/upload-sarif) — was
already on its Node24-current SHA before this PR: PR #244 ("hash-pin
every dependency install") bumped these SHAs to their latest majors as a
side effect of its own pass, landing after #246 was filed but before this
PR started. Verified by reading action.yml at each pinned SHA (not the
version comment) — all report runs.using: node24 — and confirmed those
are still the latest available release for each action (repos/<action>/releases/latest).

What #246's own investigation got wrong: it listed actions/attest-build-provenance
under "already resolves to node24, composite, or docker and needs no
change." That's false for the pinned v2.4.0 — a dispatch-verified real
run
(AC4) caught it: v2.4.0's composite action internally shells out to
attest-build-provenance/predicate@1176ef5... and an old actions/attest,
both still Node20, producing a real forced to run on Node.js 24 warning
twice (SBOM job, build job) in run
https://github.com/cdeust/Cortex/actions/runs/30498168023. Fixed by bumping
to v4.1.1 (0f67c3f4...), whose composite step calls actions/attest@v4.1.1
(a1948c3f..., confirmed node24) directly — the predicate/attest split is
gone as of the v4 rewrite (release notes: actions/attest-build-provenance#691/#693,
"Bump to node24 runtime"). Inputs are unchanged (subject-path et al.,
confirmed against v4.1.1's action.yml) — no call-site change beyond the pin.

release.yml is push-tag-only, so no PR CI ever exercises its
build → upload-artifact → download-artifact round trip (the actual
concern AC3/AC5 of #246 raise — the round trip changed shape across
actions/artifact v2 → v4). Cutting a real tag just to verify a CI-action
bump was out of scope (it would publish a real GitHub Release + PyPI
version). Instead, release.yml gained a workflow_dispatch trigger,
gated so a manual run exercises every Node24-relevant action exactly as a
real release would, but never creates a GitHub Release, never attaches
assets to one, and never publishes to PyPI:

  • github-release job: if: github.event_name == 'push'
  • build job's "Attach attested distributions" step: same guard
    (softprops/action-gh-release creates the tag_name release if it's
    absent — unguarded, a dispatch run would forge one)
  • sbom job's "Upload SBOM" step: same guard
  • publish-pypi job: only the pypa/gh-action-pypi-publish step is
    guarded; the download-artifact step above it stays unconditional,
    since that's the exact half of the round trip this exists to verify

fuzz.yml (introduced by #244, which #246 flagged as needing "adopt the
Node24 SHAs" if it landed unmerged) uses google/clusterfuzzlite/actions/*
pinned actions, which are runs.using: docker — not Node-version-gated at
all, confirmed by reading their action.yml. Nothing to bump there.

Acceptance criteria (from #246) — evidence

AC1 — every uses: resolves to node24/composite/docker at the
pinned SHA, verified by reading action.yml, not the comment:

action SHA runs.using
actions/checkout 3d3c42e5... (v7.0.1) node24
actions/cache 55cc8345... (v6.1.0) node24
actions/upload-artifact 043fb46d... (v7.0.1) node24
actions/download-artifact 3e5f45b2... (v8.0.1) node24
github/codeql-action/upload-sarif c54b30b7... (v4.37.3) node24
actions/attest-build-provenance (this PR) 0f67c3f4... (v4.1.1) composite → actions/attest@a1948c3f... (v4.1.1) → node24

All five #246-named SHAs are each still their action's releases/latest
tag as of this PR (re-verified at edit time, per the issue's own caveat
that "releases move").

AC2 — every uses: pinned to a 40-hex SHA with a matching # vX.Y.Z
comment:
unchanged by this PR for the 5 named actions (already true);
the 2 attest-build-provenance sites now read @0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1.

AC3 — upload-artifact/download-artifact in release.yml bumped
together, round trip proven by one real run:
both already at their
Node24 majors (v7.0.1/v8.0.1, bumped together by #244). Round trip proven
by dispatch run https://github.com/cdeust/Cortex/actions/runs/30498850545
build job's "Upload build artifacts" step runs actions/upload-artifact@043fb46d...,
publish-pypi job's "Download build artifacts" step runs
actions/download-artifact@3e5f45b2... immediately after, unconditional
in both dispatch and push, and the job that downloads it completed
success.

AC4 — one dispatch-verified green run of each affected workflow, 0
"forced to run on Node.js 24" lines, quoted:

workflow run conclusion grep count
release.yml 30498850545 (2nd dispatch, post attest-build-provenance fix) success 0
marketplace-pins.yml 30498174318 success 0
scorecard.yml 30496481269 (real push run, post-#244) success 0
publish-ccplugins.yml 30498186964 success (†) 0
sync-ccplugins-fork.yml 30496481295 (real push run, post-#244) success (†) 0
mcp-toplist-badge.yml 30498180567 failure (‡) 0

(†) publish-ccplugins.yml/sync-ccplugins-fork.yml: the repo has no
CCPLUGINS_PAT secret configured (gh api repos/cdeust/Cortex/actions/secrets
returns an empty list), so both workflows' own "skip cleanly when
CCPLUGINS_PAT is missing" guard short-circuits before actions/checkout
ever runs. The 0-count is real but the checkout action itself is not
exercised in these two workflows — it IS exercised, successfully, with
the identical pinned SHA, in marketplace-pins.yml, scorecard.yml, and
every ci.yml PR run. Not a gap in this PR's evidence; a pre-existing
repo secret gap outside #246's blast radius.

(‡) mcp-toplist-badge.yml: actions/checkout, actions/setup-python,
and the badge-regeneration step all ran and succeeded (confirmed in the
log); the run's overall failure comes from peter-evans/create-pull-request's
PR-creation step, which fails with GitHub Actions is not permitted to create or approve pull requests — a repo Settings→Actions permission gap,
unrelated to Node20/24 and pre-existing (would fail identically on main
today with no PR open). Filed as #273 with reproduction + acceptance
criteria; not fixed here (flipping a repo-wide Actions permission is a
security-relevant decision outside a CI-action-version-bump PR's blast
radius). The stray chore/mcp-toplist-badge-refresh branch that run
pushed before failing was deleted.

AC5 — Scorecard Pinned-Dependencies does not regress (paired
control):

run commit score reason
30444807502 (pre-#244) f606c37 4 "0 out of 19 pipCommand dependencies pinned"
30496481269 (post-#244, current) 7f3ebf9 10 "all dependencies are pinned"

Improved, not regressed (this PR touches no uses: pin lines beyond the
attest-build-provenance bump, itself pinned to a 40-hex SHA — no new
unpinned dependency introduced).

Boy-scout (§14) — defects seen while verifying

Ran actionlint (not yet wired into CI — that's issue #247, owned by a
concurrent session/worktree) across .github/workflows/ as part of my own
verification. 11 pre-existing shellcheck findings total: 6 already
tracked by #247 (release.yml ×5, sync-ccplugins-fork.yml ×1); the
other 5, in publish-ccplugins.yml, were not in #247's original
enumeration — I posted them as a comment on #247
(#247 (comment))
rather than opening a duplicate issue, since it's the same repo-wide gate
#247 already owns. None are in files this PR modifies beyond release.yml,
whose findings are unchanged by this diff (confirmed: actionlint on the
final release.yml reports the identical 5 findings, same line content,
before and after).

Rules compliance

Rule Status
§2 layering N/A — .github/workflows/ is CI configuration, not mcp_server/ source; no layer touched
§4 size caps release.yml is 329 lines (was already 302 before this PR, over the repo's 300-line convention). Treated as out of the strict gate's scope for CI YAML — no prior enforcement exists for workflow files (ci.yml sits at 641 lines, publish-ccplugins.yml at 221, neither ever flagged), and a workflow file isn't a "function"/"class" in Martin's Clean Code sense the rule targets. Flagged here rather than silently passed over.
§7 local reasoning No refused constructs introduced (no global state, no reflection, no dynamic dispatch)
§8 sources Every SHA/version claim in this PR traces to a gh api repos/<action>/releases/latest + git/ref/tags/<tag> read, or a real dispatch run log, quoted above
§12 mutation N/A — CI YAML, not load-bearing Python logic
§14 boy-scout See above
§15 no deviation All 5 of #246's acceptance criteria addressed with evidence; the one gap (mcp-toplist-badge.yml's PR-creation failure) is unrelated to the issue's subject and filed as #273, not silently dropped

Stakes classification

Medium — CI/CD workflow files, not auth//billing//crypto/concurrency/
migration; release.yml is imported by no other file (composition root)
but does gate the supply-chain-hardened release path (issue #178), so
full Move 1/3/7/8 discipline applied (real dispatch verification rather
than reading YAML, per the issue's own "why blind wasn't good enough"
rationale).

Self-flagged risk

None outstanding for #246's scope. Filed, not deferred-in-prose: #273
(mcp-toplist-badge.yml PR-creation permission gap) and the #247 comment
(5 additional actionlint findings in publish-ccplugins.yml).

cdeust added 2 commits July 30, 2026 01:01
Every uses: in .github/workflows/ already resolved to actions/checkout
v7.0.1, actions/cache v6.1.0, actions/upload-artifact v7.0.1,
actions/download-artifact v8.0.1, and github/codeql-action/upload-sarif
v4.37.3 (all node24, confirmed by reading action.yml at each pinned SHA) —
PR #244 bumped these SHAs to their current majors as a side effect of its
hash-pinning pass, ahead of this issue landing. Nothing left to bump.

What remained was #246's AC4: one dispatch-verified green run of each
affected workflow with zero "forced to run on Node.js 24" lines, quoted
in the PR — and AC3: the upload-artifact/download-artifact round trip in
release.yml proven by one real run. release.yml is push-tag-only, so
proving that round trip meant either cutting a real release (out of
scope: it would publish a real PyPI version and GitHub Release for a CI
verification) or adding the seam to test it safely.

Add workflow_dispatch to release.yml, gated so a manual run exercises
checkout/cache/upload-artifact/download-artifact exactly as a real
release would, but never creates a GitHub Release, never attaches
assets to one, and never publishes to PyPI:
- github-release job: if: github.event_name == 'push'
- build job's "Attach attested distributions" step: same guard
  (softprops/action-gh-release creates the tag_name release if absent —
  an unguarded dispatch run would forge one)
- sbom job's "Upload SBOM" step: same guard
- publish-pypi job: only the pypa/gh-action-pypi-publish step is guarded;
  the download-artifact step above it stays unconditional, since that is
  the exact half of the round trip this exists to verify

Refs #246.
)

Real dispatch-verification run (30498168023) caught what #246's own
investigation missed: this action's `runs.using: composite` at v2.4.0
still shells out internally to `attest-build-provenance/predicate@1176ef5`
and an older `actions/attest`, both node20 -- a real
"forced to run on Node.js 24" warning fired twice in that run (sbom job,
build job), on an action #246 had listed as needing no change.

v3.0.0's release notes (actions/attest-build-provenance#691, #693) name
the fix directly: "Bump actions/attest ... Bump to node24 runtime" +
"Bump attest-build-provenance/predicate to v2.0.0 ... Bump to node24
runtime". v4.0.0 restructures further: the composite step now calls
actions/attest@v4.1.1 (SHA a1948c3f..., confirmed node24 by reading its
action.yml) directly, dropping the separate predicate step. Inputs are
unchanged (subject-path/subject-digest/subject-name/... identical names
in v4.1.1's action.yml vs our v2 usage) -- no call-site change needed.

Refs #246.
@cdeust
cdeust merged commit 8853e7f into main Jul 29, 2026
24 checks passed
@cdeust
cdeust deleted the fix-node24-actions-246 branch July 29, 2026 23:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant