Skip to content

ci: upgrade to Node 24, add code quality workflows, and update dependencies#33

Merged
jaysin586 merged 5 commits into
mainfrom
feature-udpated-version
Jan 29, 2026
Merged

ci: upgrade to Node 24, add code quality workflows, and update dependencies#33
jaysin586 merged 5 commits into
mainfrom
feature-udpated-version

Conversation

@jaysin586

@jaysin586 jaysin586 commented Jan 29, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR modernizes the CI/CD infrastructure with Node.js 24 support, adds new code quality workflows, and updates all dependencies to their latest versions.

Changes

🔄 CI/CD Changes

  • Add Trunk code quality workflow for automated code checks on PRs
  • Add workflow validation using zizmor for security auditing
  • Upgrade GitHub Actions to v6 (checkout, setup-node)
  • Switch to Blacksmith runners for faster CI execution
  • Add Node.js 24 to test matrix across all workflows
  • Add zizmor security annotations for unpinned action references
  • Enhance npm-publish workflow with debug statement checks and Playwright tests
  • Improve error handling and inline documentation in workflows

📦 Dependency Updates

  • Upgrade Volta Node version from 22.21.1 to 24.13.0
  • Update @eslint/compat, @types/node, @typescript-eslint packages
  • Update vitest, vite, prettier, and other dev dependencies
  • Update docs dependencies (svelte, shiki, wrangler, etc.)

📚 Documentation

  • Update Cloudflare compatibility_date to 2026-01-20
  • Remove redundant rel="noopener noreferrer" attribute (modern browsers handle this automatically for target="_blank")

Commits

  • 1d55797 chore(docs): update compatibility date and remove redundant attribute
  • 1785542 build: update dependencies and upgrade to Node 24
  • eb8ee4e ci: update workflows for Node 24, Blacksmith runners, and security
  • 438bb0e ci: add trunk code quality and workflow validation

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end Playwright tests and expanded unit-test matrix for broader coverage.
  • Chores

    • Upgraded toolchain and dev dependencies (Node/tooling) for improved stability.
    • Expanded CI: workflow validation, code-quality checks, caching, and enhanced publishing pipeline.
    • Improved deployment steps to capture and upload logs, plus sitemap manifest generation for docs.
    • Added project lint/configuration files and adjusted deployment compatibility date.

✏️ Tip: You can customize this high-level summary in your review settings.

jaysin586 and others added 4 commits January 29, 2026 14:08
Add two new CI workflows:
- trunk-check.yml: Runs Trunk code quality checks on PRs
- validate-workflows.yml: Validates workflow files using zizmor

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Upgrade to Node.js 24 and add to test matrix
- Switch to Blacksmith runners for faster CI
- Update GitHub Actions to v6 (checkout, setup-node)
- Add zizmor security annotations for unpinned actions
- Add trunk-ignore comments for custom runner labels
- Enhance npm-publish with debug checks and Playwright tests
- Improve error handling and add inline documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update dev dependencies:
- @eslint/compat: 2.0.0 → 2.0.1
- @types/node: 25.0.3 → 25.1.0
- @typescript-eslint/*: 8.52.0 → 8.54.0
- @vitest/coverage-v8: 4.0.16 → 4.0.18
- globals: 17.0.0 → 17.2.0
- prettier: 3.7.4 → 3.8.1
- prettier-plugin-sort-json: 4.1.1 → 4.2.0
- publint: 0.3.16 → 0.3.17
- vite: 7.3.0 → 7.3.1
- vitest: 4.0.16 → 4.0.18

Update Volta Node version from 22.21.1 to 24.13.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update Cloudflare compatibility_date to 2026-01-20
- Remove redundant rel="noopener noreferrer" from external link

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jan 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Updates CI workflows: swaps GitHub-hosted runners for a specific self-hosted label, upgrades several actions and Node.js to v24, adds workflow validation and Trunk quality checks, extends the publish pipeline with multi-stage checks (including debug scanning, Playwright, and publish gating), updates docs tooling and Wrangler compatibility, and adjusts deployment logging and artifacts.

Changes

Cohort / File(s) Summary
GitHub Actions — runner & action upgrades
.github/workflows/cloudflare-pages.yml, .github/workflows/run-tests.yml, .github/workflows/coveralls.yml
Replaced ubuntu-latest with blacksmith-2vcpu-ubuntu-2404, upgraded actions/checkout and actions/setup-node to v6, bumped Node matrix to include 24, added comments/trunk-ignore for unpinned actions and introduced dependency caching in coveralls.
Publishing & Release Pipeline
.github/workflows/npm-publish.yml
Reworked publish workflow into multi-stage pipeline (merge checks, debug-check, build, playwright, coverage, publish-github-packages), added debug artifact scanning, GPG handling, annotated tagging/releases, OIDC-backed publish, skip/publish gating and PR notifications.
Workflows validation & Trunk integration
.github/workflows/trunk-check.yml, .github/workflows/validate-workflows.yml, .trunk/setup-ci/action.yaml, .trunk/trunk.yaml
Added Trunk code-quality check workflow and a Validate Workflows job using zizmor/uv with caching, per-file validation logs, artifact uploads, and a composite Trunk setup action and trunk config.
Docs & site changes
docs/package.json, docs/wrangler.jsonc, docs/src/lib/components/general/Footer.svelte, docs/scripts/*
Bumped many docs devDependencies and Volta node to 24.x, updated Wrangler compatibility_date to 2026-01-20, removed rel="noopener noreferrer" on a footer link, and added sitemap manifest generation step in pages workflow.
Repo tooling & lint configs
.trunk/configs/..., .trunk/.gitignore, .trunk/.gitignore, .trunk/configs/svgo.config.mjs
Added multiple Trunk config files (markdownlint, shellcheck, yamllint, svgo), adjusted .gitignore to allow .trunk/, and added ignore entries to .trunk/.gitignore.
General package/tooling bumps
package.json
Bumped development dependencies across ESLint, TypeScript, Prettier, Vite/Vitest, globals; upgraded Volta/node to 24.x.
Workflow logging & deploy changes
.github/workflows/cloudflare-pages.yml (deploy step additions)
Added sitemap manifest generation, set SENTRY_AUTH_TOKEN in deploy step, created Wrangler logs dir, captured deploy exit code while still uploading logs and printing latest logs before exiting with original status.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant PR as "PR / Trigger"
  participant GA as "GitHub Actions"
  participant Runner as "Self-hosted Runner\n(blacksmith-2vcpu-ubuntu-2404)"
  participant Artifacts as "Artifact Store"
  participant Registry as "GitHub Packages / npm Registry"
  participant GPG as "GPG / Signing"
  rect rgba(200,200,255,0.5)
    PR->>GA: push/merge/dispatch
    GA->>Runner: run debug-check, build, unit & playwright tests
    Runner->>Artifacts: upload vitest/playwright results & logs
    Runner->>GA: test outcomes
  end
  rect rgba(200,255,200,0.5)
    GA->>GPG: import/use GPG key (if publishing)
    GA->>Registry: publish with pnpm + OIDC provenance
    Registry-->>GA: publish response
    GA->>PR: post success/failure comment & create release/tag
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

skip-publish

Poem

🐰 I hopped through CI with a careful beat,
Runners changed, Node updated my feet,
Debugs hunted, manifests spun,
Tags signed softly—release is done,
Hooray! 🥕🧾

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main changes: Node 24 upgrade, code quality workflows addition, and dependency updates, matching the primary objectives of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

Add Trunk CLI configuration for code quality checks:
- Configure linters: shellcheck, actionlint, checkov, eslint,
  markdownlint, osv-scanner, prettier, trufflehog, yamllint
- Add linter configs for markdownlint, shellcheck, yamllint, svgo
- Include CI setup action for Trunk integration
- Update .gitignore to track .trunk/ directory

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

@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.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @.github/workflows/coveralls.yml:
- Around line 49-56: The "Cache dependencies" step using actions/cache@v5 is
misconfigured: change the cache path from ~/.npm to the pnpm store path
(~/.local/share/pnpm/store) and move this cache step to run before the pnpm
install/pnpm test steps so dependencies are restored, or alternatively replace
the manual cache step by using actions/setup-node with cache: 'pnpm' (or align
with the run-tests.yml approach) to enable built-in pnpm caching; update the
step named "Cache dependencies" and any related keys to keep the existing
key/restore-keys logic but targeting the correct pnpm store location and
placement.

In @.github/workflows/npm-publish.yml:
- Around line 439-443: The validation for GPG_KEY_ID currently only allows 16
uppercase hex chars; update the check to accept either 16- or 40-character hex
strings and both cases. Replace the regex in the if that tests GPG_KEY_ID with
one that matches either 16 or 40 hex digits case-insensitively (for example:
^([A-Fa-f0-9]{16}|[A-Fa-f0-9]{40})$) so the variable GPG_KEY_ID validation
accepts keyid or full fingerprint and lowercase letters.

In @.github/workflows/validate-workflows.yml:
- Around line 139-142: The fallback currently sets changed_files by running find
over the whole repo; change the find invocation that populates changed_files so
it only searches .github/workflows (e.g., replace find . -name "*.yml" -o -name
"*.yaml" with find .github/workflows -name "*.yml" -o -name "*.yaml" and keep
the existing sed 's|^\./||' || true), so the changed_files variable only
contains workflow YAML files.
- Around line 3-6: Change the workflow token permissions to least privilege by
replacing the write scopes in the permissions block: set contents from "write"
to "read" (or remove if not needed) and set pull-requests from "write" to "read"
(or remove if not needed), updating the permissions block that currently lists
"contents: write" and "pull-requests: write" in validate-workflows.yml so the
job only has the minimal required access.

Comment on lines +49 to +56
- name: Cache dependencies
uses: actions/cache@v5 # zizmor: ignore[unpinned-uses]
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ matrix.node-version }}-
${{ runner.os }}-node-

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Cache configuration has issues: wrong path and suboptimal placement.

Two problems with this cache step:

  1. Wrong cache path: The workflow uses pnpm, but the cache path is ~/.npm. The pnpm store is typically at ~/.local/share/pnpm/store, not ~/.npm.

  2. Placement after install/test: The cache step appears after pnpm install and pnpm test. For caching to be effective, it should be placed before the install step so dependencies can be restored from cache.

Note: Since actions/setup-node with cache: pnpm is not used here (unlike in run-tests.yml), manual caching is needed. Consider aligning this workflow with run-tests.yml by using the built-in cache option.

🔧 Option 1: Use setup-node built-in caching (preferred)
             - name: Use Node.js ${{ matrix.node-version }}
               uses: actions/setup-node@v6 # zizmor: ignore[unpinned-uses]
               with:
                   node-version: ${{ matrix.node-version }}
+                  cache: pnpm

             - name: Install
               run: pnpm install --frozen-lockfile
...
-            - name: Cache dependencies
-              uses: actions/cache@v5 # zizmor: ignore[unpinned-uses]
-              with:
-                  path: ~/.npm
-                  key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
-                  restore-keys: |
-                      ${{ runner.os }}-node-${{ matrix.node-version }}-
-                      ${{ runner.os }}-node-
🔧 Option 2: Fix manual cache configuration
+            - name: Get pnpm store directory
+              shell: bash
+              run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
+              id: pnpm-cache
+
+            - name: Cache pnpm dependencies
+              uses: actions/cache@v5 # zizmor: ignore[unpinned-uses]
+              with:
+                  path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
+                  key: ${{ runner.os }}-pnpm-store-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
+                  restore-keys: |
+                      ${{ runner.os }}-pnpm-store-${{ matrix.node-version }}-
+                      ${{ runner.os }}-pnpm-store-
+
             - name: Install
               run: pnpm install --frozen-lockfile
...
-            - name: Cache dependencies
-              uses: actions/cache@v5 # zizmor: ignore[unpinned-uses]
-              with:
-                  path: ~/.npm
-                  key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
-                  restore-keys: |
-                      ${{ runner.os }}-node-${{ matrix.node-version }}-
-                      ${{ runner.os }}-node-
🤖 Prompt for AI Agents
In @.github/workflows/coveralls.yml around lines 49 - 56, The "Cache
dependencies" step using actions/cache@v5 is misconfigured: change the cache
path from ~/.npm to the pnpm store path (~/.local/share/pnpm/store) and move
this cache step to run before the pnpm install/pnpm test steps so dependencies
are restored, or alternatively replace the manual cache step by using
actions/setup-node with cache: 'pnpm' (or align with the run-tests.yml approach)
to enable built-in pnpm caching; update the step named "Cache dependencies" and
any related keys to keep the existing key/restore-keys logic but targeting the
correct pnpm store location and placement.

Comment on lines +439 to 443
# Validate GPG key ID format first
if [[ ! "$GPG_KEY_ID" =~ ^[A-F0-9]{16}$ ]]; then
echo "Invalid GPG key ID format"
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

ghaction-import-gpg output keyid format fingerprint

💡 Result:

In crazy-max/ghaction-import-gpg, the outputs mean:

  • outputs.fingerprint: the full OpenPGP fingerprint of the selected key/subkey (typically 40 hex characters for v4 keys). The action recommends using this as the “user ID”. [1]
  • outputs.keyid: the key ID, i.e. the low 64 bits of the (SHA‑1–based) fingerprint (so typically 16 hex characters). [1]

How to see the same values locally with gpg:

  • Show long key IDs (16 hex chars) and fingerprints:
    • gpg --list-secret-keys --keyid-format=long --with-subkey-fingerprint (prints subkey fingerprints too). [2][3]

If you pass the action input fingerprint: (to pick a specific signing subkey), use that subkey fingerprint value (the 40-hex string shown under the sub key), and the action’s fingerprint output will correspond to the selected key/subkey. [1]

Sources: [1] [2] [3]


🌐 Web query:

GPG key ID format 16 hex vs 40 hex fingerprint casing

💡 Result:

  • 16 hex “key ID” (a.k.a. long keyid): this is a 64‑bit identifier, typically shown as 16 hexadecimal characters. In GnuPG, --keyid-format long selects this 16‑hex display (and short is 8 hex). [2]
  • 40 hex “fingerprint”: for the common OpenPGP v4 key format, the fingerprint is a 160‑bit SHA‑1 hash, shown as 40 hexadecimal characters. [1]
  • How they relate (v4 keys): the 64‑bit key ID is derived from the fingerprint—for v4 keys it is the low‑order 64 bits (i.e., the last 16 hex characters) of the v4 fingerprint. [1]
  • Casing (upper vs lower): these identifiers are hexadecimal; letter casing does not change the value (most tools accept either; many UIs display uppercase).
  • Note on newer keys: newer OpenPGP key versions can use longer fingerprints (e.g., 256‑bit) while the Key ID remains 64 bits, but the derivation can differ by key version. [1]

Sources:
[1] RFC 9580, OpenPGP — Key IDs & Fingerprints (v4 fingerprint = 160-bit; Key ID = 64-bit; v4 keyid derived from fingerprint)
[2] GnuPG manual (gpg(1) / configuration options) — --keyid-format short vs long (8 vs 16 hex)


Allow 40‑char and lowercase GPG key IDs from the import action.

The ghaction-import-gpg action can output either the keyid (16 hex characters) or the full fingerprint (40 hex characters), and both are valid GPG identifiers. Additionally, hexadecimal values may be lowercase, which is equally valid. The current regex rejects both cases, potentially breaking releases.

🔧 Suggested fix
-                  if [[ ! "$GPG_KEY_ID" =~ ^[A-F0-9]{16}$ ]]; then
+                  if [[ ! "$GPG_KEY_ID" =~ ^([A-Fa-f0-9]{16}|[A-Fa-f0-9]{40})$ ]]; then
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Validate GPG key ID format first
if [[ ! "$GPG_KEY_ID" =~ ^[A-F0-9]{16}$ ]]; then
echo "Invalid GPG key ID format"
exit 1
fi
# Validate GPG key ID format first
if [[ ! "$GPG_KEY_ID" =~ ^([A-Fa-f0-9]{16}|[A-Fa-f0-9]{40})$ ]]; then
echo "Invalid GPG key ID format"
exit 1
fi
🤖 Prompt for AI Agents
In @.github/workflows/npm-publish.yml around lines 439 - 443, The validation for
GPG_KEY_ID currently only allows 16 uppercase hex chars; update the check to
accept either 16- or 40-character hex strings and both cases. Replace the regex
in the if that tests GPG_KEY_ID with one that matches either 16 or 40 hex digits
case-insensitively (for example: ^([A-Fa-f0-9]{16}|[A-Fa-f0-9]{40})$) so the
variable GPG_KEY_ID validation accepts keyid or full fingerprint and lowercase
letters.

Comment on lines +3 to +6
permissions:
contents: write
pull-requests: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reduce workflow token permissions to least privilege.

This job doesn’t write to repo or PRs, so write permissions broaden the blast radius unnecessarily.

🔧 Suggested permissions tightening
permissions:
-    contents: write
-    pull-requests: write
+    contents: read
+    pull-requests: read
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions:
contents: write
pull-requests: write
permissions:
contents: read
pull-requests: read
🤖 Prompt for AI Agents
In @.github/workflows/validate-workflows.yml around lines 3 - 6, Change the
workflow token permissions to least privilege by replacing the write scopes in
the permissions block: set contents from "write" to "read" (or remove if not
needed) and set pull-requests from "write" to "read" (or remove if not needed),
updating the permissions block that currently lists "contents: write" and
"pull-requests: write" in validate-workflows.yml so the job only has the minimal
required access.

Comment on lines +139 to +142
echo "::warning::Cannot find base ref ${BASE_REF} in any form"
echo "Falling back to validating all workflow files"
changed_files=$(find . -name "*.yml" -o -name "*.yaml" | sed 's|^\./||' || true)
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Limit fallback validation to workflows only.

The fallback currently scans all YAML in the repo, so zizmor may fail on non-workflow YAML. Restrict it to .github/workflows.

🔧 Suggested fix
-                          changed_files=$(find . -name "*.yml" -o -name "*.yaml" | sed 's|^\./||' || true)
+                          changed_files=$(find .github/workflows -type f \( -name "*.yml" -o -name "*.yaml" \) | sed 's|^\./||' || true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
echo "::warning::Cannot find base ref ${BASE_REF} in any form"
echo "Falling back to validating all workflow files"
changed_files=$(find . -name "*.yml" -o -name "*.yaml" | sed 's|^\./||' || true)
fi
echo "::warning::Cannot find base ref ${BASE_REF} in any form"
echo "Falling back to validating all workflow files"
changed_files=$(find .github/workflows -type f \( -name "*.yml" -o -name "*.yaml" \) | sed 's|^\./||' || true)
fi
🤖 Prompt for AI Agents
In @.github/workflows/validate-workflows.yml around lines 139 - 142, The
fallback currently sets changed_files by running find over the whole repo;
change the find invocation that populates changed_files so it only searches
.github/workflows (e.g., replace find . -name "*.yml" -o -name "*.yaml" with
find .github/workflows -name "*.yml" -o -name "*.yaml" and keep the existing sed
's|^\./||' || true), so the changed_files variable only contains workflow YAML
files.

@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.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.trunk/setup-ci/action.yaml:
- Around line 7-14: Remove the zizmor ignore comments and replace the tag
references with pinned commit SHAs for both actions: change the uses entries for
pnpm/action-setup and actions/setup-node from tag forms (pnpm/action-setup@v4
and actions/setup-node@v6) to specific commit SHAs
(pnpm/action-setup@<commit-sha> and actions/setup-node@<commit-sha>), keeping
the existing with inputs (version: 10 and node-version: 24) intact; after
pinning, remove the zizmor: ignore[unpinned-uses] lines and add or configure
Dependabot to automatically propose SHA updates for these actions.

In @.trunk/trunk.yaml:
- Around line 13-17: The Trunk runtime list currently pins node@22.16.0 under
runtimes.enabled which conflicts with Volta and CI that use Node 24; update the
runtimes.enabled entry that references node@22.16.0 to the Node 24 equivalent
(e.g., node@24) so linters run under the same Node version as CI/Volta, ensuring
consistent behavior across tooling.

Comment on lines +7 to +14
- uses: pnpm/action-setup@v4 # zizmor: ignore[unpinned-uses]
with:
version: 10

- name: Setup Node.js
uses: actions/setup-node@v6 # zizmor: ignore[unpinned-uses]
with:
node-version: 24

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check if the file exists and examine its content
find . -name "action.yaml" -type f | head -20

Repository: humanspeak/memory-cache

Length of output: 96


🌐 Web query:

GitHub Actions security pinning commit SHA best practices

💡 Result:

Best practices for SHA pinning GitHub Actions (security)

  • Pin every third‑party action and reusable workflow to a full-length commit SHA (40 hex chars) (not a branch or tag). This is GitHub’s recommended way to get an immutable reference and reduce supply‑chain risk from moved tags/branches. [1][2]

    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
  • Verify the SHA comes from the action’s official repository (not a fork) before adopting it. [1][2]

  • Prefer SHAs that correspond to a tagged release, and record the release in a comment (as in the example). This improves auditability and helps automated update tooling understand intent. [3]

  • Keep pins updated with Dependabot version updates for GitHub Actions, so you’re not “secure but stale.” [4][5]

  • Enforce it centrally: GitHub provides admin controls to require SHA pinning (and can also block specific actions/versions) via Actions policy settings. [1][2][5]

Notes/caveats

  • Pinning to SHAs is most secure, but it means you must have a process (e.g., Dependabot PRs + review) to regularly roll pins forward. [4][5]

Sources: GitHub Docs (Secure use reference / security hardening) [1][2], GitHub Well-Architected Actions security guidance [3], GitHub Docs (Dependabot for Actions) [4], GitHub Changelog (policy enforcement for SHA pinning + blocking) [5].


🌐 Web query:

zizmor GitHub Actions linter security tool

💡 Result:

zizmor is a security-focused static analysis (linter) tool for GitHub Actions workflows. It scans your CI/CD configuration and flags common issues such as template-injection risks, credential leakage/persistence, and overly broad permissions, and it can output SARIF for GitHub code-scanning integration. (github.com)

How to use it

  • CLI: Run it against a repo/workflows (supports offline-first operation; online mode can use a GitHub token for API/rate limits and private content). (zizmor.sh)
  • GitHub Actions integration: Use zizmorcore/zizmor-action to run zizmor in CI, optionally uploading results to GitHub Advanced Security (code scanning), or running without it (advanced-security: false). (github.com)
  • Install (Python packaging available): zizmor is published on PyPI. (pypi.org)

Minimal GitHub Actions example (no Advanced Security)

- name: Run zizmor
  uses: zizmorcore/zizmor-action@v0.3.0
  with:
    advanced-security: false

(github.com)


Sources (numbered)

[1] (github.com) GitHub repo: zizmorcore/zizmor
[2] (zizmor.sh) zizmor site: zizmor.sh
[3] (github.com) GitHub Marketplace: zizmor-action
[4] (docs.zizmor.sh) docs.zizmor.sh: Usage / token permissions
[5] (pypi.org) PyPI: zizmor project page

Citations:


🏁 Script executed:

# Check the actual file content to see if it matches the snippet
cat -n ./.trunk/setup-ci/action.yaml | head -30

Repository: humanspeak/memory-cache

Length of output: 928


🏁 Script executed:

# Search for all zizmor ignore comments in the repository to understand the pattern
rg "zizmor.*ignore" --type yaml -B 2 -A 2

Repository: humanspeak/memory-cache

Length of output: 637


🌐 Web query:

zizmor unpinned-uses rule GitHub Actions

💡 Result:

unpinned-uses is a zizmor audit/rule for GitHub Actions that flags uses: references that aren’t pinned safely.

What it flags

  • Actions and (newer versions) reusable workflows referenced via uses: that are pinned to something mutable (or not pinned at all), e.g.:
    • uses: actions/checkout@v3 (tag/major version ref, not immutable)
    • uses: owner/repo@main (branch ref, mutable)
  • In pedantic mode, zizmor will flag symbolic refs like @v3 as “not pinned to a hash ref.”
  • Recent versions also flag unpinned reusable workflows (not just actions).

Why it matters

Mutable refs (branches/tags) can change over time; pinning to a commit SHA makes the dependency immutable and reduces supply-chain risk.

Default policy (important recent change)

As of zizmor v1.20.0, the default unpinned-uses policy became stricter: it changed from allowing “ref pinning” for some first‑party actions to requiring hash pinning by default for those as well (unless you configure an exception).

If you want the older, more permissive behavior for first‑party namespaces, you can configure policies like this in zizmor.yml:

rules:
  unpinned-uses:
    config:
      policies:
        actions/*: ref-pin
        github/*: ref-pin
        dependabot/*: ref-pin

Typical fix

Replace:

- uses: actions/checkout@v3

with a commit-SHA pin (often with an optional comment noting the tag/version you intended).


Remove zizmor ignores and pin GitHub Actions to commit SHAs.

Both actions use version tags instead of commit SHAs, leaving them vulnerable to tag retargeting. The zizmor: ignore[unpinned-uses] comments indicate this is a deliberate choice, but GitHub's security guidance and zizmor's defaults require SHA pinning to reduce supply‑chain risk. Implement SHA pinning on both pnpm/action-setup and actions/setup-node, and use Dependabot to automate updates so you get the security hardening without the maintenance burden.

🤖 Prompt for AI Agents
In @.trunk/setup-ci/action.yaml around lines 7 - 14, Remove the zizmor ignore
comments and replace the tag references with pinned commit SHAs for both
actions: change the uses entries for pnpm/action-setup and actions/setup-node
from tag forms (pnpm/action-setup@v4 and actions/setup-node@v6) to specific
commit SHAs (pnpm/action-setup@<commit-sha> and
actions/setup-node@<commit-sha>), keeping the existing with inputs (version: 10
and node-version: 24) intact; after pinning, remove the zizmor:
ignore[unpinned-uses] lines and add or configure Dependabot to automatically
propose SHA updates for these actions.

Comment thread .trunk/trunk.yaml
Comment on lines +13 to +17
runtimes:
enabled:
- go@1.21.0
- node@22.16.0
- python@3.10.8

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the current .trunk/trunk.yaml content
cat -n .trunk/trunk.yaml

Repository: humanspeak/memory-cache

Length of output: 1506


🏁 Script executed:

# Search for Volta configuration in package.json
find . -name "package.json" -type f | head -5 | xargs grep -l "volta" 2>/dev/null || echo "No Volta config found"

Repository: humanspeak/memory-cache

Length of output: 101


🏁 Script executed:

# Search for CI configuration files
find . -path "./.git" -prune -o -type f \( -name "*.yml" -o -name "*.yaml" \) -print | grep -E "(ci|github|setup)" | head -10

Repository: humanspeak/memory-cache

Length of output: 459


🏁 Script executed:

# Look for setup-ci references mentioned in PR
rg "setup-ci|node.*24|node.*22" --type yaml --type json -i

Repository: humanspeak/memory-cache

Length of output: 1159


🏁 Script executed:

cat -n .trunk/setup-ci/action.yaml

Repository: humanspeak/memory-cache

Length of output: 928


🏁 Script executed:

cat -n package.json

Repository: humanspeak/memory-cache

Length of output: 4484


🏁 Script executed:

# Search for any Trunk documentation or version specifications in comments/docs
rg "node@|trunk.*version|runtimes" --type yaml --type md -A 3 -B 1

Repository: humanspeak/memory-cache

Length of output: 13438


Align Trunk runtime Node version with Volta and CI setup (Node 24).

Line 16 specifies node@22.16.0, but Volta and CI setup both use Node 24. This mismatch will cause linters to run under a different Node version than tests and builds, potentially causing lint behavior inconsistencies.

Suggested update
 runtimes:
     enabled:
         - go@1.21.0
-        - node@22.16.0
+        - node@24.13.0
         - python@3.10.8
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
runtimes:
enabled:
- go@1.21.0
- node@22.16.0
- python@3.10.8
runtimes:
enabled:
- go@1.21.0
- node@24.13.0
- python@3.10.8
🤖 Prompt for AI Agents
In @.trunk/trunk.yaml around lines 13 - 17, The Trunk runtime list currently
pins node@22.16.0 under runtimes.enabled which conflicts with Volta and CI that
use Node 24; update the runtimes.enabled entry that references node@22.16.0 to
the Node 24 equivalent (e.g., node@24) so linters run under the same Node
version as CI/Volta, ensuring consistent behavior across tooling.

@jaysin586 jaysin586 added the skip-publish This is something important, but we dont want to publish it label Jan 29, 2026
@jaysin586 jaysin586 merged commit 6d63950 into main Jan 29, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-publish This is something important, but we dont want to publish it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant