chore(ci): upgrade GitHub Actions to Node 24 runtime majors (#36850) - #37261
chore(ci): upgrade GitHub Actions to Node 24 runtime majors (#36850)#37261nicobytes wants to merge 15 commits into
Conversation
|
Semgrep found 146
GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. If this is a critical or high severity finding, please also link this issue in the #security channel in Slack. |
7a0c4c7 to
1a3e71f
Compare
There was a problem hiding this comment.
Pull request overview
Upgrades GitHub Actions workflow and composite-action uses: pins across .github/** to Node 24–compatible major versions, and adds a dedicated PR lint workflow plus a guard script/test suite to prevent action-version drift from recurring (issue #36850).
Changes:
- Added an action-version drift guard (
.github/scripts/check-action-versions.sh) with a bash fixture-based test suite and a new PR-scoped “Actions Lint” workflow. - Swept
.github/workflows/**and.github/actions/**to newer action majors (notablycheckout@v7,cache@v6,upload-artifact@v7,download-artifact@v8with explicitdigest-mismatch,setup-node@v7, plus PR-path third-party actions). - Updated the issue-linking merge gate workflow logic and tests (non-closing references + paginated Development-section lookup).
Reviewed changes
Copilot reviewed 73 out of 73 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| specs/36850-upgrade-github-actions-node-24/spec.md | Spec artifact (not reviewed; per PR description) |
| specs/36850-upgrade-github-actions-node-24/data-model.md | Spec artifact (not reviewed; per PR description) |
| specs/36850-upgrade-github-actions-node-24/contracts/check-action-versions.md | Spec artifact (not reviewed; per PR description) |
| docs/core/GIT_WORKFLOWS.md | Docs update (not reviewed; per PR description) |
| .github/workflows/utility_discover-docker-tags.yml | Bump actions/checkout to v7 |
| .github/workflows/tests/link-issue-to-pr.test.sh | Tests for updated issue-linking workflow parsing |
| .github/workflows/tests/check-action-versions.test.sh | Tests for the new action-version drift guard |
| .github/workflows/publish_docs.yml | Bump checkout/setup-node pins (disabled workflow) |
| .github/workflows/legacy-release_sbom-generator.yaml | Bump checkout to v7 |
| .github/workflows/legacy-release_maven-release-process.yml | Bump core action pins + add digest-mismatch |
| .github/workflows/legacy-release_comp_maven-build-docker-image.yml | Bump cache/checkout/docker-login action pins |
| .github/workflows/issue_on-open_add-to-triage-project..yml | Bump actions/github-script to v8 |
| .github/workflows/issue_manual_label-issues.yml | Bump checkout to v7 |
| .github/workflows/issue_manual_label-customer_deployed-issues.yml | Bump checkout to v7 |
| .github/workflows/issue_comp_release-labeling.yml | Bump actions/github-script to v8 |
| .github/workflows/issue_comp_link-issue-to-pr.yml | Improve linking logic (refs support + pagination fix) |
| .github/workflows/issue_comp_label-conditional-labeling.yml | Bump actions/github-script to v8 |
| .github/workflows/issue_comp_frontend-notify.yml | Bump actions/github-script to v8 |
| .github/workflows/issue_autodoc.yml | Update checkout SHA pin + download-artifact v8 + digest-mismatch |
| .github/workflows/dotbot-review.yml | Update checkout SHA pin to v7.0.1 |
| .github/workflows/dotbot-act.yml | Update checkout SHA pin to v7.0.1 |
| .github/workflows/cicd_weekly-rolling-tags.yml | Bump docker/login-action to v4.6.0 |
| .github/workflows/cicd_scheduled_opensearch-phase-sweep.yml | Bump checkout to v7 |
| .github/workflows/cicd_scheduled_notify-seated-prs.yml | Bump actions/github-script to v8 |
| .github/workflows/cicd_scheduled_image-cve-scan.yml | Bump upload-artifact to v7 |
| .github/workflows/cicd_release-sdk.yml | Bump checkout to v7 |
| .github/workflows/cicd_release-cli.yml | Bump checkout/setup-node/download-artifact + digest-mismatch |
| .github/workflows/cicd_pr_skill-lint.yml | Bump checkout/setup-node SHA pins to Node24 majors |
| .github/workflows/cicd_pr_actions-lint.yml | New PR lint workflow: guard + tests + actionlint |
| .github/workflows/cicd_post-workflow-reporting.yml | Bump checkout + dawidd6/action-download-artifact to v24 |
| .github/workflows/cicd_manual_publish-starter.yml | Bump artifact actions + add digest-mismatch |
| .github/workflows/cicd_manual_build-java-base.yml | Bump checkout + docker/login-action |
| .github/workflows/cicd_manual_build-docker-context.yml | Bump checkout + docker/login-action |
| .github/workflows/cicd_evergreen-tracks-promote.yml | Bump checkout + docker/login-action |
| .github/workflows/cicd_evergreen-tracks-admin.yml | Bump checkout + docker/login-action |
| .github/workflows/cicd_comp_test-phase.yml | Bump checkout + github-script |
| .github/workflows/cicd_comp_release-prepare-phase.yml | Bump checkout |
| .github/workflows/cicd_comp_release-phase.yml | Bump checkout + download-artifact v8 + digest-mismatch |
| .github/workflows/cicd_comp_publish-pr-test-image.yml | Bump download-artifact v8 + digest-mismatch |
| .github/workflows/cicd_comp_pr-area-labeler.yml | Bump checkout + github-script |
| .github/workflows/cicd_comp_initialize-phase.yml | Bump checkout + paths-filter + dawidd6 download action |
| .github/workflows/cicd_comp_finalize-phase.yml | Bump download/upload-artifact + digest-mismatch |
| .github/workflows/cicd_comp_deployment-phase.yml | Bump checkout |
| .github/workflows/cicd_comp_cli-native-build-phase.yml | Bump checkout + upload-artifact |
| .github/workflows/cicd_comp_changelog-site-publish-phase.yml | Bump checkout |
| .github/workflows/cicd_comp_build-phase.yml | Bump checkout |
| .github/workflows/cicd_comp_ai-release-notes-phase.yml | Bump checkout + setup-node |
| .github/workflows/cicd_6-release.yml | Bump checkout + docker/login-action + setup-node |
| .github/workflows/cicd_5-lts.yml | Bump checkout |
| .github/workflows/cicd_4-nightly.yml | Bump checkout |
| .github/workflows/cicd_3-trunk.yml | Bump checkout |
| .github/workflows/ai_claude-sdk-breaking-change.yml | Bump checkout |
| .github/workflows/ai_claude-rollback-safety.yml | Bump checkout |
| .github/workflows/ai_claude-post-merge-test-plan.yml | Bump checkout + github-script SHA pin |
| .github/workflows/ai_claude-orchestrator.yml | Bump checkout |
| .github/workflows/ai_claude-backend-reviewer.yml | Bump checkout |
| .github/scripts/check-action-versions.sh | New guard: enforces action version floors + SHA/comment + required inputs |
| .github/actions/legacy-release/sbom-generator/action.yml | Bump checkout + upload-artifact |
| .github/actions/core-cicd/setup-java/README.md | Update README example pins |
| .github/actions/core-cicd/setup-java/action.yml | Bump cache restore/save to v6 |
| .github/actions/core-cicd/prepare-runner/README.md | Update README example pins |
| .github/actions/core-cicd/maven-job/README.md | Update README example pins |
| .github/actions/core-cicd/maven-job/action.yml | Bump cache/artifact/docker-login/pnpm pins + add digest-mismatch |
| .github/actions/core-cicd/deployment/deploy-jfrog/README.md | Update README example pins |
| .github/actions/core-cicd/deployment/deploy-jfrog/action.yml | Bump checkout |
| .github/actions/core-cicd/deployment/deploy-javascript-sdk/README.md | Update prose pin references |
| .github/actions/core-cicd/deployment/deploy-javascript-sdk/action.yml | Bump checkout/setup-node/pnpm/cache pins |
| .github/actions/core-cicd/deployment/deploy-javadoc/README.md | Update prose pin references |
| .github/actions/core-cicd/deployment/deploy-javadoc/action.yml | Bump checkout |
| .github/actions/core-cicd/deployment/deploy-docker/action.yml | Bump download-artifact + add digest-mismatch; bump docker/login-action |
| .github/actions/core-cicd/deployment/deploy-cli-npm/action.yml | Bump checkout/setup-node/download-artifact + digest-mismatch |
| .github/actions/core-cicd/cleanup-runner/Readme.md | Update README example pins |
| .github/actionlint.yaml | Add actionlint config (self-hosted label + shellcheck suppression) |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
1a3e71f to
bacd7ab
Compare
…6850) Batch 1 of the Node 24 runtime sweep. Scoped to the two densest, most- exercised files so one PR build gives full backend build + test coverage: maven-job (19 external refs) and setup-java (6). actions/cache @0057852b # v4.3.0 -> @55cc8345 # v6.1.0 actions/cache/restore @v4 -> @v6.1.0 (7 sites) actions/cache/save @v4 -> @v6.1.0 (7 sites) actions/upload-artifact @v4 -> @v7.0.1 (6 sites) pnpm/action-setup @v4 -> @v6.0.10 (1 site) Behaviour-neutral, and verified rather than assumed: - cache v6 is an ESM internals change. Read v6's own action.yml: restore keeps inputs path/key/restore-keys/enableCrossOsArchive/fail-on-cache-miss/ lookup-only and outputs cache-hit/cache-primary-key/cache-matched-key; save keeps path/key/upload-chunk-size/enableCrossOsArchive. So the restore->save handoff through steps.restore-*.outputs.cache-primary-key is untouched. - No cache KEY changes anywhere, so existing caches stay warm and a revert re-reads the same keys. Nothing to re-prime. - upload-artifact v7's new `archive` input defaults to 'true', i.e. exactly v4 behaviour. `archive: false` is deliberately NOT set: maven-job's image.tar is consumed by `docker load`, and finalize-phase's workflow-data by dawidd6/action-download-artifact -- both need the zipped shape. - pnpm resolves from core-web/package.json's packageManager (pnpm@10.17.1), so v6 (which adds pnpm 11 support) resolves the same version. Watch the pnpm store cache-hit on the SECOND consecutive build, not the first: that key embeds steps.pnpm-info.outputs.version, so a drift there would surface as a slow build rather than a failure. The maven-wrapper pin stays SHA-pinned and keeps its rationale comment (a tag can be repointed by its owner, which is how the trivy-action and kics-github-action compromises worked). Its "not the mutable v4 tag" wording is updated to v6 so the comment still describes reality. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ch (#36850) Batch 2 of the Node 24 runtime sweep. 14 sites, v4 -> v8.0.1. v8 changed `digest-mismatch` from warn to **error**: a hash mismatch now fails the run. That is the only change in this whole sweep that can turn a green build red, so it is landed in two steps. This commit writes `digest-mismatch: warn` explicitly at every site, making the runtime bump provably behaviour-neutral -- exactly the pure runtime change #36850 asks for. Flipping to `error` is a separate, revertable commit later in the sweep. Exposure, in order: maven-job:266 maven-repo -> ~/.m2/repository, multi-GB, cross-run maven-job:291 build-classes -> large glob, cross-run maven-job:277 docker-image -> single large file finalize-phase:39 build-reports-* pattern: N artifacts, N chances to fail, on EVERY PR and merge-queue run The value is written out rather than inherited at all 14 sites, so a future major cannot silently move the default again. Verified rather than assumed: - v5's breaking change (single-download-by-ID path flattening) applies only to `artifact-ids:`. Zero uses of `artifact-ids:` in the repo, so it does not apply. - v8 no longer auto-unzips non-zip Content-Type. That only matters alongside `archive: false` uploads, which batch 1 deliberately avoided introducing. - upload-artifact v7 and download-artifact v8 are bumped as a pair, as the issue requires; batch 1 covered the upload side. issue_autodoc.yml:354 keeps its SHA pin, rotated to v8.0.1's SHA with a corrected version comment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 2db94cc)
Batch 4 of the Node 24 runtime sweep. 61 of 62 sites: 56 float tags and 5 SHA pins. cicd_post-workflow-reporting.yml is deliberately held back to its own commit -- it is the repo's only workflow_run + checkout pair and carries ~4,000 runs/90d, so it gets a one-line revert of its own. Legacy pins cleared along the way: @v2 in utility_discover-docker-tags.yml, @V3 in legacy-release_sbom-generator.yaml and issue_manual_label-customer_deployed-issues.yml. v7 refuses to check out fork PR code from pull_request_target / workflow_run. Read src/input-helper.ts and src/unsafe-pr-checkout-helper.ts at tag v7.0.1 -- two independent reasons it cannot trigger here: 1. assertSafePrCheckout is only REACHED when a custom `repository:` or an explicit `ref:` is supplied. A default self-checkout is skipped outright. 2. The throw needs all three: the event is pull_request_target, or workflow_run whose workflow_run.event starts with pull_request; the head repo id differs from the base repo id (a genuine fork); AND the resolved input points at the fork's code (`repository` == fork full_name, or `ref` matching ^refs/pull/[0-9]+/(head|merge)$, or the commit == a payload PR head SHA). Site by site: cicd_publish-pr-test-image.yml has no checkout step at all and is gated head.repo.full_name == github.repository; cicd_comp_publish-pr-test-image.yml never checks out; dotbot-review.yml and dotbot-act.yml fire on pull_request and issue_comment, outside the guard's event list. So: no v6 pins, and `allow-unsafe-pr-checkout: true` is NOT added anywhere. That input would permanently disarm a real pwn-request protection against a risk this repo does not have. v6 moves credentials out of .git/config into a file under $RUNNER_TEMP. Grepped: nothing reads .git/config or extraheader or extracts the token, and no step runs git under sudo or inside `docker run` against the workspace. All ~10 `git push` sites run in the same job and as the same user as their checkout. v6's only hard runner requirement (>= 2.329.0, for Docker container actions) does not apply: there are no `container:` jobs and no `uses: docker://` in the repo. The five SHA pins keep their style, rotated to v7.0.1's SHA with corrected version comments -- including the two that disagreed on main, where the same checkout SHA was labelled # v4.2.2 in issue_autodoc.yml and # v4.2.0 in the dotbot workflows. Running actionlint locally confirms the three "runner of actions/checkout@v2|v3 is too old" reports present on main are now resolved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit fe57fcd)
Batch 5a, isolated on purpose: this action is the mechanism accepted ADR-0013
depends on, so it gets a commit that reverts alone.
ADR-0013 ("Skip Integration and Postman Tests for Frontend-Only Changes in
Merge Queue") turns on path-based test filtering in the merge queue. Its entire
implementation is this one step at cicd_comp_initialize-phase.yml, whose outputs
the "Rewrite Filter" step consumes.
Safe because v4.0.0's only change is `feat: update action runtime to node24` --
no input, output or filter-semantics change. Verified against the upstream
release rather than inferred.
One line touched. `id: filter` and the step's `if:` guard are untouched, because
the fail-open design hangs off them: "Rewrite Filter" reads
`toJSON(steps.filter.outputs)` and defaults every filter to 'true' when change
detection is disabled, so a filter that fails to resolve runs MORE tests, not
fewer. Preserving that direction matters more than the bump.
Note for anyone re-reading ADR-0013: the code has moved on since it was written.
The ADR describes a `validation-level` input and per-output `|| 'true'` defaults;
the workflow now takes `change-detection` and consumes the outputs as a JSON
object. The dependency on paths-filter's outputs is unchanged, which is what
makes this bump safe.
Still to verify on a real merge-queue run (tracked as a task, not assumed here):
that the filter resolves and a frontend-only change still skips integration and
Postman.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit bd5a9dc)
Batch 5b. These four are not in #36850's list, but the runner emits its deprecation annotation PER JOB, listing that job's stale actions -- so without them AC-001 fails on its own terms. Measured on run 33122809569 before the sweep: Label PR / Apply Area Labels checkout@v4, github-script@v7 PR Test / Setup Test Matrix checkout@v4, github-script@v7 Finalize / Prepare Report Data upload-artifact@v4, download-artifact@v4 github-script@v7 appears in two of the three annotated jobs. Bumping only the six actions the issue names would clear Initial Artifact Build and leave initialize, label-pr and test still annotated. actions/github-script @v7 -> @v8 (14) docker/login-action @v3/@v3.0.0 -> @v4.6.0 (12) dawidd6/action-download-artifact @v6 -> @v24 (3) v8 is a pure node24 bump: no "type": "module", still @actions/github ^6.0.0 and @octokit/core ^5.0.1. That is the whole of what this issue needs. Worth recording because the opposite is widely repeated: **v9 does not break require()**. Checked at tag v9.0.0 -- src/main.ts still passes `require: wrapRequire` and `__original_require__`, src/wrap-require.ts is a Proxy over __non_webpack_require__ handling both bare module IDs and ./-relative paths, and src/async-function.ts still declares `require: NodeRequire`. All three require() sites here (require('fs') x2, plus one local-module require) would keep working. What v9 actually changes: @actions/github becomes ESM-only so require('@actions/github') fails -- 0 occurrences here; and getOctokit becomes an injected parameter, making `const`/`let getOctokit` a SyntaxError -- also 0 occurrences. The real cost is Octokit v5 -> v7 under 14 inline scripts, which is a different review from a runtime bump. Deferred to #37194 with that scope, not with a wrong reason. Verified against v24's own action.yml: every input in use is still present (github_token, workflow_search, commit, workflow_conclusion, search_artifacts, dry_run, name, name_is_regexp, path, run_id, if_no_artifact_found) and so is the found_artifact output. All three call sites already set `if_no_artifact_found: warn`, so they are non-fatal by construction. Worth a follow-up, not this PR: actions/download-artifact@v8 now supports run-id + github-token natively, so this dependency may be removable. It would be a behaviour change (no name_is_regexp, no workflow_search/commit discovery), so it belongs in #37194. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit eb6183d)
Batch 6. One line, its own commit, because this is the repo's only workflow_run + actions/checkout pair and it runs ~4,000 times per 90 days. The v7 fork-PR guard cannot fire here, for two independent reasons (full analysis in the batch-4 commit): 1. This is a bare `- uses: actions/checkout@v7.0.1` with no `with:` block at all, so assertSafePrCheckout is never even reached -- the guard only runs when a custom `repository:` or explicit `ref:` is supplied. 2. On workflow_run, github.ref and github.sha resolve to the default branch, never the fork head, so the condition that requires the resolved input to point at fork code is false. Cannot be verified before merge: it needs a real completed workflow_run for 'PR Check', and its fork path only surfaces on the first external-contributor PR after merge. Watch that, not the green internal ones. Blast radius if wrong is reporting only -- Slack messages and test-report check runs. It gates no merge. And the failure mode is loud and instant: the step fails in seconds with "Refusing to check out fork pull request code". With this commit every actions/checkout reference under .github/ is on v7.0.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 0fb3c5e)
Batch 7. Everything still on a stale pin outside the hot path: the four core-cicd deployment composites, the sbom-generator, and the release, manual, scheduled and PR-lint workflows. actions/setup-node @v4 / @49933ea5 -> @v7.0.0 (6) actions/upload-artifact @v4 -> @v7.0.1 (4) actions/cache/restore @v4 -> @v6.1.0 (1) actions/cache/save @v4 -> @v6.1.0 (1) pnpm/action-setup @v4 -> @v6.0.10 (1) pnpm caching is managed manually. That precondition does not exist in this repo, so adding the input would be verifiably dead config -- and dead config gets cargo-culted into places where it also does nothing. setup-node can cache two ways, and both are closed here: 1. Explicitly, via `cache:` / `cache-dependency-path`. Verified: neither input is set at any of the 8 call sites, nor anywhere else under .github/. 2. Automatically, added in v5, driven by packageManager / devEngines.packageManager in $GITHUB_WORKSPACE/package.json. setup-node reads ONLY that path, and this repo has no root package.json -- just a stub root package-lock.json. So core-web/package.json's `packageManager: pnpm@10.17.1` is invisible to it, and v6 narrowed automatic caching to npm anyway. Both facts are load-bearing: (1) alone leaves v5's automatic path open, (2) alone leaves an explicit `cache:` open. Together they establish that no setup-node cache can collide with the manual pnpm store cache at deploy-javascript-sdk/action.yml:123,143 -- the hazard the AC was written to prevent. The deviation is documented in the spec and was signed off there. deploy-javascript-sdk is the one file where setup-node precedes pnpm/action-setup plus a manual store cache, so it is worth an eyeball on the run log rather than a config change. cicd_pr_skill-lint.yml keeps its SHA pin, rotated to v7.0.0's SHA with a corrected comment. What remains after this commit sits only in legacy or disabled workflows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit ac00712)
…les (#36850) Batch 8+10, the tail. This completes the sweep: every in-scope action reference under .github/, code and documentation alike, is now on a node24 major. legacy-release_maven-release-process.yml cache@v4 x5, cache/restore@v4 x4 legacy-release_comp_maven-build-docker-image.yml cache/restore@v3 publish_docs.yml setup-node@v2-beta -> v7.0.0 7 README.md files 8 stale usage references cache/restore@v3 in legacy-release_comp_maven-build-docker-image.yml targeted the v1 cache service, shut down in February 2025. It was already dead code, so bumping it is free. ## publish_docs.yml: pin bumped, workflow deliberately not repaired It is disabled_manually on GitHub with 0 runs in 90 days. The hardcoded `node-version: "16.13.2"` is replaced with node-version-file: 'core-web/.nvmrc' so the file stops asserting an EOL Node, but its `cd core-web && npm install` still cannot work against a pnpm workspace. Converting it would be a functional change to a dead pipeline hidden inside a runtime bump. The follow-up should lean towards DELETING it: #37150 removed core-web/libs/dotcms and stripped `npm run build:docs:dotcms` from this very workflow, leaving only build:docs:dotcms-models. Half of what it existed to publish no longer exists. legacy-release_sbom-generator.yaml is likewise disabled with 0 runs. ## READMEs 8 references across 7 files, broader than first scoped. Four are `uses: actions/checkout@v2` code examples (maven-job, cleanup-runner, setup-java, prepare-runner), one is a checkout@v4 example (deploy-jfrog), and three are prose references (deploy-javascript-sdk x2, deploy-javadoc). Prose counts: a reader copying from it reintroduces the drift. ## Deliberately left alone, tracked in #37194 - The 4 first-party actions on runs.using: 'node16' -- they need dist/ rebuilds, not pin edits, and a regenerated ncc bundle cannot be diff-reviewed. - The dead `runner.os == 'Windows'` branch in maven-job (no Windows runner exists in any matrix). - .github/main.workflow (Actions-v1 HCL, never executed). - ad-m/github-push-action@master (a moving branch ref; Docker action, so the runtime deprecation does not apply, but the pin violates the repo's own Action-security guidance). Running actionlint locally leaves 9 findings, all pre-existing and unrelated to action versions -- 4 expression, 1 events, plus setup-python@v4, configure-aws-credentials@v1 and the two malformed `options:` keys in the issue-fetcher / issue-labeler action.yml files, which belong to #37194. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit c871f91)
Batch 3, landed last on purpose so it reverts alone. Batch 2 bumped download-artifact v4 -> v8.0.1 with `digest-mismatch: warn`, which made the runtime bump provably behaviour-neutral. This commit adopts v8's own secure default at the same 14 sites. The trade: today a corrupt maven-repo artifact surfaces as a baffling Maven resolution error twenty minutes into the build. With `error` it surfaces immediately, at the download step, naming the artifact. If a mismatch does appear -- most likely on the multi-GB maven-repo, or on finalize-phase's `build-reports-*` pattern where N artifacts mean N chances -- revert THIS commit only. The Node 24 migration stays landed. Verify on two consecutive PR builds plus a merge-queue run before trusting it; one green build is not evidence for a hash check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit a12c41e)
…36850) The merge of main added .github/workflows/ai_claude-post-merge-test-plan.yml, which arrived with two stale SHA pins: actions/checkout @11d5960a # v4.4.0 -> @3d3c42e5 # v7.0.1 actions/github-script @60a0d830 # v7.0.1 -> @ed597411 # v8 Both entered through the merge rather than through the sweep, and neither would have been spotted by eye in a 74-file diff. Note the second one also had a wrong version comment -- the SHA was labelled # v7.0.1, which is checkout's version, not github-script's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 7a0c4c7)
bacd7ab to
f6a9d26
Compare
The merge-base changed after approval.
…6850) peter-evans/create-or-update-comment @v4 (node20) -> @v5.0.0 (node24), at both sites in issue_comp_link-issue-to-pr.yml. This was a scoping mistake, not a deferral. It was filed under "cold-path third-party actions" for #37194, but it runs on **every pull request** via `Add Issue to PR / link-issue`, so it kept AC-001 unsatisfied: the annotation scan on this PR still reported Node.js 20 is deprecated. ... peter-evans/create-or-update-comment@v4 The cause: the PR-pipeline surface was traced through cicd_1-pr.yml's job graph only. Other workflows also fire on `pull_request` -- issue_open-pr.yml (which calls this composite), the five ai_claude-* workflows, dotbot-review.yml and cicd_pr_skill-lint.yml -- and they were not walked. Safe: v5.0.0's release notes are dependency bumps only, and every input in use here (issue-number, comment-id, body, edit-mode) is still present in v5's action.yml. The `edit-mode: replace` guard against comment accumulation (issue #35794) is untouched. Remaining Node 20 annotations on this PR are NOT fixable from this repository: `Claude AI Orchestrator` and `Claude AI Rollback Safety Check` run dotCMS/ai-workflows' reusable workflows, addressed by dotCMS/ai-workflows#67. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Semgrep flagged the v5.0.0 tag as blocking on the line the previous commit introduced, and the finding is correct on its own terms: this action runs on every pull request, receives repository-derived values (issue-number, comment-id, body), and holds a token that can modify issue comments. A repointed tag would execute attacker code in that context with no change to this workflow file. @v5.0.0 -> @e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 (node24) Pinned rather than triaged because it costs two lines here, unlike the repo-wide convention change. This joins the existing deliberate SHA pins (maven-job's cache, issue_autodoc, dotbot-*, cicd_pr_skill-lint) under the same reasoning as the comment at maven-job/action.yml: a tag can be repointed by its owner, which is how the trivy-action and kics-github-action compromises worked. Note this does not resolve Semgrep's other ~146 findings of the same rule. Those are pre-existing: main carries 199 mutable tags and this branch carries 199 too, because @v4 -> @v7.0.1 swaps one mutable tag for another. Semgrep's scan is diff-aware, so a touched line is reported as new. Converting all 199 to SHAs is a convention migration that belongs with #37194's dependabot work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Upgrades every GitHub Action under
.github/to a major whoseruns.usingisnode24, so therunner's Node 20 deprecation annotations disappear and the pipeline survives the removal of the
compatibility shim.
Spec: #37193 (merged). Parent: #36850. Deferred scope: #37194.
Scope is
.github/only — no Java, no Angular, no DB, no ES mapping, no API contract. Nothingfalls under ROLLBACK_UNSAFE_CATEGORIES.md.
AC-001 is satisfied, and here is the evidence
The acceptance criterion is about annotations, not a green check — a run can be green and still
carry them. Scanning both runs with the same
gh apiquery:33122809569)33191856953)Initialize / Check Changed FilesLabel PR / Apply Area LabelsPR Test / Setup Test MatrixFinalize / Prepare Report DataPR Build / Initial Artifact BuildThe scan finds 4 annotated jobs on the baseline and 0 here, so this is not a false green — the query
demonstrably detects annotations when they exist.
Note
Initialize / Check Changed Files. Those annotations came fromdorny/paths-filteranddawidd6/action-download-artifact, neither of which #36850 lists. The runner emits its annotationper job, so had this PR bumped only the six actions the issue names, that job would still be
annotated and AC-001 would fail on its own terms. The four extra third-party bumps were necessary,
not scope creep.
What changed
actions/checkoutactions/download-artifactactions/cache+/restore+/saveactions/upload-artifactactions/setup-nodepnpm/action-setupactions/github-scriptdocker/login-actiondawidd6/action-download-artifactdorny/paths-filterPlus 8 stale references across 7
README.mdfiles. Each site keeps its pinning style — float tagstays a float tag, SHA pin gets the new SHA with a corrected
# vX.Y.Zcomment. Two SHA pinsdisagreed on
main(the samecheckoutSHA labelled# v4.2.2in one file and# v4.2.0inothers); both are now correct.
11 commits, each independently revertable.
mainallows onlymerge, not squash, so every one landsin history on its own — the messages carry the per-batch reasoning.
Three places where a major carried real behaviour
download-artifactv8 changeddigest-mismatchfrom warn toerror— the only change here thatcan turn a green build red. Exposure is the multi-GB
maven-repoartifact andfinalize-phase'sbuild-reports-*pattern (N artifacts = N chances, on every PR and merge-queue run). Landed in twocommits: first v8 with an explicit
digest-mismatch: warn, making the runtime bump provablybehaviour-neutral, then a separate commit flipping to
error. If a mismatch appears, revert thatone commit — the Node 24 migration stays landed.
checkoutv7's fork-PR block cannot fire in this repo. Readsrc/input-helper.tsandsrc/unsafe-pr-checkout-helper.tsat tagv7.0.1: the guard is only reached when a customrepository:or explicitref:is supplied, and the throw additionally needs a genuine fork headand inputs pointing at the fork's code.
cicd_post-workflow-reporting.yml:58is a bareuses:with no
with:block at all;cicd_publish-pr-test-image.ymlhas no checkout step and is gated tonon-forks. So no v6 pins, and
allow-unsafe-pr-checkout: trueis deliberately not used — itwould permanently disarm a real pwn-request protection against a risk this repo does not have.
setup-node:package-manager-cache: falseis deliberately NOT added, deviating from an ACwritten in #36850. Both of setup-node's caching paths are already closed: no call site sets
cache:or
cache-dependency-path(explicit path), and the repo has no rootpackage.json— only a stubroot
package-lock.json— so v5's automatic path cannot fire either, and v6 narrowed it to npmregardless. Adding the input would be verifiably dead config. Documented as AC-005 in the spec.
Two more decisions worth a glance
github-scriptstops at v8, not v9. v8 is a pure node24 bump (no"type": "module", still@actions/github ^6.0.0). Worth recording because the opposite is widely repeated: v9 does notbreak
require()—src/main.tsstill injectswrapRequire, verified at tagv9.0.0. What v9actually changes is Octokit v5 → v7 under 14 inline scripts, which is a different review. Deferred to
#37194 with that scope, not with a wrong reason.
dorny/paths-filteris isolated in its own commit because it is the mechanism acceptedADR-0013
depends on. v4.0.0's only change is the node24 runtime — no input, output or filter-semantics change —
and
id: filterplus the step'sif:guard are untouched, because the fail-open design hangs offthem: a filter that fails to resolve runs more tests, not fewer.
About the red Semgrep check
semgrep-cloud-platform/scanfails ongithub-actions-mutable-action-tag, and it ispre-existing, not introduced here:
uses:refsmainIdentical.
@v4→@v7.0.1swaps one mutable tag for another. Semgrep's scan is diff-aware, so achanged line is reported as a new finding; other PRs pass only because their diffs do not touch these
lines.
main's ruleset has no required status checks, so this does not block.Going SHA-only is a legitimate change — the repo's own docs ask for pinning to specific versions,
which Semgrep reads more strictly — but it is a convention migration across 199 references and
belongs with #37194's dependabot work, not inside a runtime bump.
Cannot be verified before merge — please review with this in mind
cicd_publish-pr-test-image.yml—pull_request_targetreads its workflow definition from thebase branch, so a branch edit is inert until merge. Nothing here can exercise it.
cicd_post-workflow-reporting.yml— needs a real completedworkflow_run; its fork path onlysurfaces on the first external-contributor PR after merge.
cicd_6-release.yml,cicd_5-lts.yml,cicd_4-nightly.yml,cicd_release-cli.yml,cicd_release-sdk.yml,cicd_comp_release-*,legacy-release_*) — tag andschedule gated. These need eyes from someone who knows the release process; no automation covers
them.
publish_docs.yml,legacy-release_sbom-generator.yaml—disabled_manually, 0 runs/90d. Theirpins are bumped for consistency; Upgrade remaining GitHub Actions to Node 24 and add drift controls (follow-up to #36850) #37194 should decide whether to delete them.
digest-mismatch: error— one green build is not evidence for a hash check. Watch twoconsecutive PR builds plus a merge-queue run.
Post-merge watch, 72 hours, no release inside it: first
cicd_2-merge-queue.yml, firstcicd_3-trunk.yml, firstcicd_4-nightly.yml, and the first fork PR.Deferred to #37194
This PR stops the annotations. It does not stop them coming back — that is #37194, and it matters
more than it looks:
runs.using: 'node16'(issue-fetcher,issue-labeler,changelog-report,rc-changelog). They needdist/rebuilds, and a regeneratednccbundlecannot be diff-reviewed the way YAML can.
node16is further along the deprecation path than thenode20that prompted this issue.aws-actions/configure-aws-credentialsv1 → v6 needs OIDC and org-level trust-policy work, and
slackapi/slack-github-actionv1 → v2+changes the payload format.
.github/dependabot.ymlfor thegithub-actionsecosystem, and extending.github/filters.yamlso.github/**gets real validation. Today itsbackendfilter matchesonly
cicd_comp_*.yml,cicd_1-pr.ymlandcore-cicd/**/action.yml, so most of the files this PRtouches get no build, no test and no lint on a PR. That gap is why the deprecation went unnoticed
until the runner started shouting.
job). It was removed to keep this PR a pure runtime bump, so nothing currently prevents recurrence —
see AC-007 in the spec for that trade, recorded rather than glossed over.
🤖 Generated with Claude Code
This PR fixes: #36850