Skip to content

ci: Node 24 action upgrades, SHA-pinning, security hardening & workflow consolidation - #1218

Merged
chhoumann merged 1 commit into
masterfrom
ci/node24-actions-upgrade-hardening
May 29, 2026
Merged

ci: Node 24 action upgrades, SHA-pinning, security hardening & workflow consolidation#1218
chhoumann merged 1 commit into
masterfrom
ci/node24-actions-upgrade-hardening

Conversation

@chhoumann

@chhoumann chhoumann commented May 29, 2026

Copy link
Copy Markdown
Owner

Why

The last Release run warned that GitHub forces Node 20 actions onto the Node 24 runtime on 2026-06-02 and removes Node 20 from runners in fall 2026 (any action still declaring runs.using: node20 then fails to start, taking down Release/CI). Separately, Node 20 itself reached EOL on 2026-04-30, so the build toolchain was on an unsupported runtime.

This PR upgrades every action to a Node 24 release pinned to a full commit SHA, hardens permissions and the supply chain, and consolidates the CI workflows. Every version/SHA below was pulled from the GitHub API and independently re-verified.

Action upgrades (all SHA-pinned, Node 24 confirmed)

Action Before After Runtime
actions/checkout @v4 @de0fac2… # v6.0.2 node24
actions/setup-node @v4 @48b55a0… # v6.4.0 node24
oven-sh/setup-bun @v1 (latest) @0c5077e… # v2.2.0 (1.3.14) node24
actions/attest-build-provenance @v2 @a2bbfa2… # v4.1.0 composite → node24 attest
actions/github-script @v6 @3a2844b… # v9.0.0 node24
jwalton/gh-find-current-pr @v1 @f3d61b4… # v1.3.5 node24
rtCamp/action-slack-notify @v2 @e31e87e… # v2.3.3 composite
github/codeql-action — (new) @7211b7c… # v4 node24
actions/dependency-review-action — (new) @a1d282b… # v5.0.0 node24
step-security/harden-runner — (new) @ab7a940… # v2.19.4 node24

github-script had to go to v8+ (v8/v9 are node24; v7 is still node20). v9's ESM-only change doesn't affect the inline github.rest… usage.

Security hardening

  • Removed Ilshidur/action-discord@master — a moving branch is a supply-chain RCE vector (tj-actions/changed-files class), and DISCORD_WEBHOOK is set, so it ran live on every release. Replaced with a plain curl webhook call (message JSON-encoded with jq).
  • Least-privilege GITHUB_TOKEN: top-level permissions: contents: read on every workflow; the release job keeps its (now annotated) write scopes; the notify job is pinned to contents: read.
  • Removed the dead NPM_TOKEN from the Release step (@semantic-release/npm has npmPublish:false). The plugin itself is kept — it drives the version lifecycle hook that runs version-bump.mjs to update manifest.json/versions.json.
  • Script-injection hardening: github-script inputs are now passed via env: and read with process.env; version extraction uses node -p instead of node -e … && source.
  • harden-runner (egress audit) on the release + CI build/test jobs.
  • persist-credentials: false on CI checkouts; release checkout keeps fetch-depth: 0 + token (semantic-release needs both).

Modernization

  • Consolidated test.yml + build-with-lint.ymlci.yml (push/PR to master, paths-ignore docs/markdown, cancel-in-progress concurrency, --frozen-lockfile) + a dependency-review job on PRs.
  • CodeQL code scanning (codeql.yml, security-extended, weekly cron).
  • Dependabot for github-actions + bun (root) + bun (docs) — the root-cause fix so actions never silently drift again.
  • Build toolchain Node 20 → 24 (dropped the pointless single-value matrix); serialized concurrency on release.yml; dropped the no-op bun run build --if-present.

Repo settings applied out-of-band (via API)

  • ✅ Secret scanning + push protection — enabled
  • ✅ Dependabot security updates — enabled (immediately flagged 7 moderate advisories on master)
  • ✅ Default workflow token → read
  • Require SHA pinningdeferred until this PR merges (enabling it now would block the still-tag-based workflows on master). After merge: Settings → Actions → General → "Require actions to be pinned to a full-length commit SHA".

Rollout / test plan

  1. CI on this PR validates Test + Build-Lint on Node 24 with the pinned actions.
  2. Smoke-test the new release.yml on Node 24 without publishing (it's dispatchable on a branch because the workflow already exists on the default branch):
    gh workflow run release.yml --ref ci/node24-actions-upgrade-hardening -f dryRun=true
    
    Then confirm the run log no longer shows the Node 20 deprecation annotation.
  3. Merge, then enable Require SHA pinning.

attest-build-provenance only runs on a real release (skipped in dry-run), so it's validated on the first publish after merge.

Behavior changes to be aware of

  • CI push now triggers only on master (feature branches rely on the PR trigger) — removes duplicate runs; opening a PR still runs full CI.
  • CI now uses --frozen-lockfile (matches release.yml; fails loudly on lockfile drift). Verified the current bun.lock is in sync.
  • Dependabot's bun updater has a known quirk of stripping the lockfile configVersion line on some Bun versions; if that creates --frozen-lockfile friction, the bun ecosystem entries can be dropped without affecting the critical github-actions updater.

Out of scope (flagged for a follow-up)

release.yml passes --release-as and --prerelease to semantic-release, neither of which is a valid flag in v24 (it derives the version from commits) — so the releaseType/preRelease dispatch inputs are inert and the "pre-release" path would cut a normal release. Left unchanged here to keep this PR scoped to the Node 24 / hardening work; worth fixing or removing the inputs separately.

Validation

bun install --frozen-lockfile clean · bun run build OK · 1083 tests pass.

Summary by CodeRabbit

  • Chores
    • Enabled automated dependency management with scheduled weekly updates and version grouping.
    • Consolidated and improved continuous integration workflows with enhanced testing and build quality checks.
    • Integrated automated security code scanning into the build pipeline.
    • Enhanced release workflow with security hardening and improved release notifications.

Review Change Stack


Open in Devin Review

Ahead of GitHub's 2026-06-02 forced Node20->Node24 action migration (and
Node20 removal from runners in fall 2026), upgrade every action to a Node24
release and pin to full commit SHAs.

Actions (all SHA-pinned, Node24-verified):
- actions/checkout v4 -> v6.0.2
- actions/setup-node v4 -> v6.4.0
- oven-sh/setup-bun v1 -> v2.2.0 (bun-version pinned 1.3.14, was 'latest')
- actions/attest-build-provenance v2 -> v4.1.0
- actions/github-script v6 -> v9.0.0
- jwalton/gh-find-current-pr v1 -> v1.3.5
- rtCamp/action-slack-notify v2 -> v2.3.3

Build toolchain Node 20 -> 24 (Node 20 reached EOL 2026-04-30; @types/node
is already 24). Dropped the single-value node matrix.

Security hardening:
- Replace Ilshidur/action-discord@master (moving-branch = supply-chain RCE,
  DISCORD_WEBHOOK is live) with a plain curl webhook call (jq-encoded).
- Least-privilege permissions: top-level contents:read everywhere; release
  job keeps its annotated write scopes; notify job pinned to contents:read.
- Remove dead NPM_TOKEN from the Release step (@semantic-release/npm has
  npmPublish:false; the plugin is kept because it drives the version bump).
- Env-bind github-script inputs and use `node -p` for version extraction to
  remove script-injection surface.
- harden-runner (egress audit) on release + CI build/test jobs.

Modernization:
- Consolidate test.yml + build-with-lint.yml into ci.yml (push/PR to master,
  paths-ignore docs/md, cancel-in-progress concurrency, persist-credentials
  false, --frozen-lockfile) + dependency-review on PRs.
- Add CodeQL code scanning (codeql.yml).
- Add Dependabot for github-actions + bun (root + docs) so actions never
  silently drift again.
- Serialized concurrency on release.yml; drop the no-op `build --if-present`.

Local validation: `bun install --frozen-lockfile` clean, `bun run build` OK,
1083 tests pass.
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR refactors GitHub Actions infrastructure: adding Dependabot configuration for automated dependency updates, consolidating test and build-lint workflows into a unified CI pipeline, introducing CodeQL security scanning, and modernizing the release workflow with pinned actions, improved concurrency control, and updated notification logic for Slack and Discord.

Changes

CI & Release Infrastructure

Layer / File(s) Summary
Dependabot configuration for automated updates
.github/dependabot.yml
Configures Dependabot to check GitHub Actions, root-level Bun dependencies, and /docs Bun dependencies weekly, grouping updates per scope with PR limits, commit prefixes, and category labels.
Unified CI workflow with test, build-lint, and dependency-review jobs
.github/workflows/ci.yml
Consolidates test and build-lint into a single CI workflow triggering on push/PR to master (ignoring docs and markdown files), with hardened runners, Bun v1.3.14 and Node.js 24 setup, frozen-lockfile installs, test/build execution, and dependency-review job for PRs with high-severity failure detection.
CodeQL security analysis workflow
.github/workflows/codeql.yml
Adds CodeQL security scanning for JavaScript/TypeScript on push/PR to master and weekly schedule, with concurrency control, hardened runner, CodeQL initialization using security-extended query suite, and language-scoped analysis.
Release workflow updates: concurrency, pinned actions, and notifications
.github/workflows/release.yml
Modernizes release workflow by adding concurrency serialization, pinning GitHub Actions to commit SHAs, upgrading to Node.js 24, refactoring post-release control flow to set released output from exit code, updating attestation and version-extraction steps, and switching Discord notifications from third-party action to direct webhook curl with jq-constructed JSON payload.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through workflows new,
Dependabot keeps deps in view,
CI tests and CodeQL scans align,
With pinned actions—all is fine!
Security and release so bright,
The pipelines run throughout the night!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: Node 24 upgrades, SHA-pinning, security hardening, and workflow consolidation, all of which are primary focal points in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/node24-actions-upgrade-hardening

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0278df4
Status: ✅  Deploy successful!
Preview URL: https://cb3c0147.quickadd.pages.dev
Branch Preview URL: https://ci-node24-actions-upgrade-ha.quickadd.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)

108-137: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix RELEASE_EXIT_CODE capture under default errexit in the release step.

GitHub Actions’ default Linux/macOS bash for run: is bash -e (errexit). With bunx semantic-release inside the if/elif/else bodies, a non-zero exit aborts the step before RELEASE_EXIT_CODE=$? and the released=false write run. Temporarily disable errexit around the release call so the exit code and output logic execute.

♻️ Capture exit code with errexit disabled (also quotes the user-supplied inputs)
                   EXTRA_ARGS=""

                   if [ "$DRY_RUN" == "true" ]; then
                     echo "Running in dry-run mode"
                     EXTRA_ARGS="--dry-run"
                   fi

+                  # Disable errexit so a non-zero semantic-release exit is captured
+                  # instead of aborting the step before the status logic runs.
+                  set +e
                   if [ -n "$PRE_RELEASE" ]; then
                     echo "Running pre-release: $PRE_RELEASE"
-                    bunx semantic-release --prerelease $PRE_RELEASE $EXTRA_ARGS
-                    RELEASE_EXIT_CODE=$?
+                    bunx semantic-release --prerelease "$PRE_RELEASE" $EXTRA_ARGS
                   elif [ "$RELEASE_TYPE" == "auto" ]; then
                     echo "Running auto release"
                     bunx semantic-release $EXTRA_ARGS
-                    RELEASE_EXIT_CODE=$?
                   else
                     echo "Running release with type: $RELEASE_TYPE"
-                    bunx semantic-release --release-as $RELEASE_TYPE $EXTRA_ARGS
-                    RELEASE_EXIT_CODE=$?
+                    bunx semantic-release --release-as "$RELEASE_TYPE" $EXTRA_ARGS
                   fi
+                  RELEASE_EXIT_CODE=$?
+                  set -e

                   # Set release status based on exit code
                   if [ "$DRY_RUN" != "true" ] && [ $RELEASE_EXIT_CODE -eq 0 ]; then
                     echo "released=true" >> $GITHUB_OUTPUT
                   else
                     echo "released=false" >> $GITHUB_OUTPUT
                   fi

                   exit $RELEASE_EXIT_CODE
🤖 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.yml around lines 108 - 137, The release step
currently suffers from bash "errexit" aborting before RELEASE_EXIT_CODE is
captured when running bunx semantic-release; wrap each bunx invocation so
errexit is temporarily disabled (e.g., using set +e before the bunx
semantic-release calls and set -e after) and then immediately capture the exit
code into RELEASE_EXIT_CODE; also ensure user-provided variables PRE_RELEASE,
RELEASE_TYPE and EXTRA_ARGS are safely quoted when passed to bunx so they are
treated as single arguments.
🤖 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.

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 108-137: The release step currently suffers from bash "errexit"
aborting before RELEASE_EXIT_CODE is captured when running bunx
semantic-release; wrap each bunx invocation so errexit is temporarily disabled
(e.g., using set +e before the bunx semantic-release calls and set -e after) and
then immediately capture the exit code into RELEASE_EXIT_CODE; also ensure
user-provided variables PRE_RELEASE, RELEASE_TYPE and EXTRA_ARGS are safely
quoted when passed to bunx so they are treated as single arguments.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7e1ab51f-091a-4ef7-839e-5ba5e33ee15d

📥 Commits

Reviewing files that changed from the base of the PR and between 1e5130a and 0278df4.

📒 Files selected for processing (6)
  • .github/dependabot.yml
  • .github/workflows/build-with-lint.yml
  • .github/workflows/ci.yml
  • .github/workflows/codeql.yml
  • .github/workflows/release.yml
  • .github/workflows/test.yml
💤 Files with no reviewable changes (2)
  • .github/workflows/build-with-lint.yml
  • .github/workflows/test.yml

@chhoumann
chhoumann merged commit 58ba3ec into master May 29, 2026
9 checks passed
@chhoumann
chhoumann deleted the ci/node24-actions-upgrade-hardening branch May 29, 2026 07:29

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.13.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants