Skip to content

ci: give action versions a watcher, and settle the Node pins - #332

Open
FrameAutomata wants to merge 4 commits into
mainfrom
ci/330-dependabot-actions
Open

ci: give action versions a watcher, and settle the Node pins#332
FrameAutomata wants to merge 4 commits into
mainfrom
ci/330-dependabot-actions

Conversation

@FrameAutomata

@FrameAutomata FrameAutomata commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #330. Follow-up to #326 (#328 / #329), which hand-bumped 68 action pins across 16 workflows after a human read a run annotation — twice in one day.

Independent of the #326 stack: I test-merged all three file edits against ci/326-node24-third-party and they auto-merge cleanly, so this targets main and can land in either order.

Dependabot, github-actions only

npm and Go modules are deliberately left unwatched — backend/ and cli/ already run govulncheck daily, and those ecosystems would bury the signal this file exists for.

Two groups, split the way #328/#329 split by hand — but on what a major means, not on who ships it:

group why it's its own PR
actions/* a major here is normally a runtime bump (#326 was exactly that): wide, mechanical, reviewable as one sweep
everything else a major here can change behaviour — wrangler-action v4 silently moved its default Wrangler CLI from 3 to 4, which is the whole of #331

A major/minor split would be dead config: all 79 updatable pins are bare major tags, so Dependabot can only ever propose majors here. Realistic volume is well under one PR/month.

ci is deliberately not auto-applied. The issue asks for a call on labels:, and this is it. That label is the collaborator trust gate; an unattended bot PR proposing an action version nobody has read should not trip it. open-pull-requests-limit is deliberately not set — with every action grouped it could never bind, and a key that does nothing is worse than the default.

Coverage is honest about its gaps. Three dtolnay/rust-toolchain@stable refs are branch pins against a repo with no version tags, so they never surface. Three more action pins ship inside documentation (sourcemap-upload.mdx, auto-fix-loop.mdx) and live outside .github/workflows, so Dependabot never sees them. Both are named in the config rather than left to be assumed covered.

Validation is not what I first wrote

The first draft told reviewers to validate a release-path bump with a workflow_dispatch dry run. There is no dry run on that path — dispatching release-docs.yml or release-website.yml runs wrangler-action with command: deploy and the production Cloudflare token, and the other release-*.yml workflows publish real assets. Following that advice to check a wrangler-action major would have deployed tracewayapp.com from an unreviewed bot branch. Corrected to point at release notes instead.

Two landing sites were also wrong: golangci-lint-action is label-reachable via cli-lint.yml, and traceway-autofix.yml has no dispatch at all (only issues: labeled), so claude-code-action — running with contents/pull-requests/issues: write and id-token — is the one pin with no pre-merge validation path whatsoever.

The two Node questions, settled in the same pass

node-version: "latest"22 in release-docs.yml and release-website.yml. latest resolves to Node Current, which the runner tool cache never ships, so every deploy re-downloaded a Node tarball — and neither docs/package.json nor website/package.json declares an engines floor to catch it. 22 matches release-traceway.yml, benchmark-processor.yml and the node:22-alpine images.

This follows f95424c rather than reversing it: that commit loosened engines.node to >=22 and deliberately replaced node-version-file: with a literal 22, because node-version-file resolves a floor to the newest release. A single-source-of-truth (.nvmrc + node-version-file:) is a real option, but it reverses a documented maintainer decision and belongs in its own issue, not folded in here.

sourcemap-upload.mdx:93 node-version: 2022 — the repo's last node-version: 20, recommending readers a major used nowhere here.

Verification

  • .github/dependabot.yml and all 16 workflows parse; group keys resolve as intended.
  • Group patterns checked against all 82 real uses: refs — the partition is exact (63 first-party, 19 third-party, 3 unbumpable).
  • Test-merged against ci/326-node24-third-party: clean, correctly combining ci: move every first-party action off the Node 20 runtime #328's @v7 bumps with this branch's node-version: 22.
  • Corrected three factual errors in my own prose: not every Node image is alpine (Dockerfile.browser is bookworm-slim), nothing asserts the images match engines.node, and testing/devtesting-nestjs is still on Node 20 — so "pinned to 22 at every site" was false.
  • No CI runs on this PR until a maintainer applies ci, and none of the touched workflows is label-reachable. The config itself is only exercised once merged to the default branch.

🤖 Generated with Claude Code

FrameAutomata and others added 4 commits August 28, 2026 18:02
Nothing in this repo watched action versions: no dependabot.yml, no
renovate.json, and the strings appear nowhere. That is how the Node 20
runtime migration (#326) was found twice in one day, both times because a
human read a run annotation. #328/#329 bought distance from that deadline
but no detection, so the next major lands exactly as silently.

Adopt Dependabot for the github-actions ecosystem only. npm and Go modules
are left unwatched on purpose -- backend/ and cli/ already run govulncheck
daily, and those ecosystems would bury the signal this file exists for.

Two groups, split the same way #328/#329 split by hand, on what a major
means rather than on who ships it: actions/* majors are normally runtime
bumps, wide and mechanical and reviewable as one sweep, while a third-party
major can change behaviour -- wrangler-action v4 silently moved its default
Wrangler CLI from 3 to 4, which is the whole of #331.

The PRs are deliberately not auto-labelled `ci`. That label is the
collaborator trust gate that makes PR CI opt-in, and an unattended bot PR
proposing an action version nobody has read should not trip it; a reviewer
applies it after looking, as for any human PR. Worth being clear-eyed that
this is mostly detection and not validation: `ci` reaches only backend*.yml
and cli*.yml, while most pins sit on release-*.yml, benchmark-*.yml and
traceway-autofix.yml, where validating a bump still means a
workflow_dispatch dry run. Detection is the half that was missing.

Settle the two Node questions in the same pass, since they are the same
discipline. release-docs.yml and release-website.yml pinned
`node-version: "latest"`, which floats two production deploys onto whatever
Node shipped that morning, up to and including a non-LTS Current release --
and neither docs/package.json nor website/package.json declares an engines
floor to catch it. Both now pin 22, the major already used by
release-traceway.yml, benchmark-processor.yml and the node:22-alpine
Dockerfiles, matching frontend/package.json's >=22. Whether Traceway moves
to Node 24 is now one decision made once rather than one made silently on
two deploy paths.

The source-map upload recipe was the last surviving `node-version: 20` in
the repo, recommending to readers a major this repo uses nowhere while
#328 had just bumped the action pins around it to the Node 24 majors. It
recommends 22 now, the same version the shipped Dockerfiles run.

Closes #330

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cleanup pass over the previous commit; no behaviour change to the config.

Drop `open-pull-requests-limit: 4`. With every action grouped it can never
bind (two groups, default ceiling five), and its comment conceded as much
while also claiming headroom for security updates -- which the key does not
govern, those run against a separate limit. Dead config plus a false claim.

Move the "detection, not validation" paragraph out of the YAML and into
CLAUDE.md, which already owns the `ci`-label policy one paragraph above. It
changed no key in the config, so an editor of that file could not act on it.
Cut the `labels:` comment down for the same reason: the absence of the key
needs a marker, not an essay that re-states what it points at.

Note the three dtolnay/rust-toolchain@stable refs in the group comment.
`stable` is a branch and that repo publishes no version tags, so those three
never surface -- worth saying, or the third-party group looks like total
coverage of the non-actions pins when it is 16 of 19.

Reword the two node-version comments to the real cost and match the two-line
form release-traceway.yml:63 already uses for the same decision. The win is
the runner tool cache, which ships LTS lines and never Current, so "latest"
re-downloaded Node on every deploy; npm caching was never affected, since
setup-node keys that on platform, arch and the lockfile hash only.

Split the CLAUDE.md paragraph in two -- Dependabot policy and Node pinning
are separate topics -- and fix what it got wrong: not every Node image is
alpine (Dockerfile.browser runs bookworm-slim), nothing asserts the images
agree with engines.node, and testing/devtesting-nestjs is still on Node 20,
so "pinned to 22 at every site" was false. Say plainly that CI pins per site
*because* engines.node is a floor and node-version-file would resolve it to
the newest release, which is the reasoning f95424c already applied to
release-traceway.yml -- otherwise this contradicts the dev-shell paragraph
above, which does read that file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review pass over the two prose changes. The config is unchanged; every fix
here is a claim that did not match how the repo behaves.

CLAUDE.md told a reviewer to validate a release-path bump with a
workflow_dispatch dry run. There is no dry run on that path. Dispatching
release-docs.yml or release-website.yml runs cloudflare/wrangler-action with
`command: deploy` and the production Cloudflare token, and the other
release-* workflows publish real assets with contents: write -- so following
that instruction to check a wrangler-action major would have deployed
tracewayapp.com from an unreviewed bot branch. Say what the path actually
costs, and point validation at the action's release notes instead.

Two landing sites were wrong in the same sentence. golangci-lint-action is
label-reachable through cli-lint.yml, so it does not need any of this.
traceway-autofix.yml has no workflow_dispatch at all -- only `issues:
labeled` -- so claude-code-action, which runs with contents/pull-requests/
issues write and id-token, is the one pin with no pre-merge validation path
whatever. That is worth knowing before its next major, not after.

The third-party enumeration in the config under-listed the group it
describes: `*` minus actions/* also captures Swatinem/rust-cache and
anthropics/claude-code-action.

Extend the not-covered note. Alongside the three @stable branch refs, three
action pins ship inside documentation -- two in the source-map recipe this
PR already edits, one in a website blog post -- and live outside
.github/workflows, so Dependabot never sees them. Naming them keeps the file
from reading as total coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This PR changed release-docs.yml and release-website.yml from
node-version: "latest" to a literal 22, and documented "Node is pinned
per site rather than read from one file" as the intended shape. #346
(closing #333) makes .nvmrc the single source of truth instead, so both
are superseded: the two workflows now take node-version-file: .nvmrc,
which fixes "latest" for the same reason and without adding a third and
fourth hand-maintained literal.

Dropping them here rather than reconciling them, since they touched the
exact lines #346 does -- the two PRs would otherwise conflict, and this
one is about Dependabot.

What remains is the watcher and the docs recipe's node20 actions, which
is what the PR was for.

Refs #330, #333.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@FrameAutomata
FrameAutomata force-pushed the ci/330-dependabot-actions branch from abe1b1f to d0b718b Compare August 28, 2026 23:02
@FrameAutomata

Copy link
Copy Markdown
Collaborator Author

Dropped the two node-version changes and the CLAUDE.md paragraph that went with them.

They touched the exact lines #346 changes (node-version: "latest"node-version-file: .nvmrc), so leaving them in would have made two of my own PRs conflict — the thing you flagged on #314/#315/#338, in a smaller form.

#346 supersedes them anyway: it fixes "latest" for the same reason this did, without adding a third and fourth hand-maintained literal. The "Node is pinned per site rather than read from one file" paragraph I wrote here is now the opposite of what the repo does, so it's gone rather than reworded.

What's left is what the PR was actually for: the Dependabot watcher (#330) and the docs recipe's node20 action pins. Also rebased onto current main.


Generated by Claude Code

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nothing detects GitHub Actions pin drift; the node20 sweep was found by accident twice in one day

1 participant