ci: add pull-request checks and pin Node to 24 - #38
Merged
Conversation
UPD-002. The Pages workflow only runs on pushes to main, so a pull request could be merged without ever having been built. Adds .github/workflows/ci.yml, which runs on pull requests targeting main: lint, typecheck, build, and a static-site/ drift check. The drift check regenerates the snapshot and fails if the result differs from what the branch committed. That is the failure this repository has actually hit, and it was only ever caught by hand afterwards. It uses `git status --porcelain` rather than `git diff` so a page added to src/ without a matching snapshot is caught too; a new file under static-site/ would be untracked, and untracked files are invisible to `git diff`. UPD-007. There was no .nvmrc, .node-version, or engines declaration, and the deploy workflow asked for Node 20. Node 20 is past deprecation on the runners, which logged on every run that the actions were "being forced to run on Node.js 24" - so CI already executed on 24 while declaring 20. Pinned to Node 24, the current LTS and the version already used locally: .nvmrc, an engines field, and node-version-file in both workflows so the pin has one source. The actions still targeting the retired Node 20 runtime were upgraded too, since leaving them would have kept the warning on every deploy: checkout v4->v7, setup-node v4->v7, configure-pages v5->v6, upload-pages-artifact v3->v5, deploy-pages v4->v5. upload-pages-artifact needs care: from v4 it excludes dotfiles by default (`--exclude=.[^/]*`), and this workflow creates out/.nojekyll. Bumping it without `include-hidden-files: true` would have dropped that file silently. package-lock.json is deliberately untouched. Bumping @types/node to ^24 to match the runtime typechecks cleanly, but npm 11.9.0 rewrites the lock and strips the `libc` constraint from 43 entries, which is what keeps a glibc native binary off musl. That is not a change to make as a side effect, so it is recorded as UPD-012 instead. Branch protection is still open and cannot be set from a pull request, so the new check is advisory until a ruleset requires it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> The first CI run found a real cross-platform difference the snapshot work so far had missed. Regenerating on Linux against a Windows-generated snapshot gives 101 deletions, 101 additions and 0 modifications: Next.js writes its RSC segment-cache prefetch payloads as a flat about/__next.about.__PAGE__.txt on Linux and a nested about/__next.about/__PAGE__.txt on Windows. It comes from next build itself, not the copy step. Zero files differ by content, so every HTML document, chunk, stylesheet and asset is byte-identical across platforms. The check therefore excludes __next.* and the finding is recorded under UPD-004, which it is direct evidence for.
yorkerhodes3
force-pushed
the
ci/pr-checks-and-node-pin
branch
from
August 16, 2026 02:19
9c0f66b to
97d1a47
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes UPD-002 (partially) and UPD-007.
UPD-002 — pull-request CI
The Pages workflow only runs on pushes to
main, so a pull request could bemerged without ever having been built. Adds
.github/workflows/ci.yml, which runson pull requests targeting
main:npx eslintnpx tsc --noEmitnpm run buildnpm run sync:static+ drift checkstatic-site/no longer describingsrc/The drift check is the interesting one. Snapshot drift is a failure this repository
has actually hit — it was only ever caught by hand, after merge. It uses
git status --porcelainrather thangit diffon purpose: if a contributor adds apage under
src/without regenerating, the new file understatic-site/isuntracked, and untracked files are invisible to
git diff. I tested both branchesof that logic locally — clean tree exits 0; an untracked file under
static-site/isdetected and exits 1.
The earlier blocker is gone. UPD-002 was recorded as blocked because the
ghtoken had no
workflowscope. The active token (yorkerhodes3) now carriesgist, read:org, repo, workflow, and the push in this PR proves it.Still open: branch protection cannot be configured from a pull request, so this
check is advisory until an admin adds a ruleset requiring the
Lint, typecheck, build, snapshotstatus. UPD-002 is marked partially met, not done.UPD-007 — pin Node
UPD-007 described CI as using Node 20. It no longer was. Every deploy logged:
So the declared version and the executing version had silently diverged. Pinned to
Node 24 — current LTS ("Krypton"), and already the local version:
.nvmrc→24package.json→"engines": { "node": ">=24" }node-version-file: .nvmrc, so the pin has one sourceActions on the retired node20 runtime were upgraded in the same pass, since leaving
them keeps the warning on every deploy. Each target major was checked to confirm it
actually runs on
node24(runs.usingin itsaction.yml) rather than assumed:checkout@v4→v7·setup-node@v4→v7·configure-pages@v5→v6·upload-pages-artifact@v3→v5·deploy-pages@v4→v5One trap worth flagging
upload-pages-artifactv4 introduced dotfile exclusion — its archive step runs--exclude=.[^/]*unlessinclude-hidden-filesis set. This workflow doestouch out/.nojekyll. Bumping v3→v5 without noticing would have silently dropped.nojekyllfrom the published artifact. The PR setsinclude-hidden-files: trueso behaviour is preserved exactly, with a comment saying why.
Release notes were also checked for the other majors:
setup-node@v6limitsautomatic caching to npm (we pass
cache: npmexplicitly, so no effect), andcheckout@v7blocks fork checkout forpull_request_target/workflow_run(we useplain
pull_request, so no effect).Deliberately not included
@types/nodestill resolves to 20.19.43 while the runtime is 24. Bumping it to^24typechecks cleanly — I tried it — but
npm installunder npm 11.9.0 rewritespackage-lock.jsonand strips thelibcconstraint from 43 entries (43 removed, 0added). Those constraints are what stop a glibc native binary being installed on
musl. Weakening them as a side effect of a types bump, inside a CI pull request, is a
bad trade. The lockfile here is byte-identical to
main, and the finding isrecorded as UPD-012 instead.
Verification
Run locally on this branch, all green:
npx eslintnpx tsc --noEmitnpm run sync:staticgit status --porcelain -- static-sitenpm ciwithenginespresent, lock unchangedgit diff origin/main -- package-lock.jsonThe 0-file drift result also confirms the check will pass on a correct branch rather
than failing every PR —
main's snapshot is genuinely in sync.This PR is its own test: the CI workflow it adds runs against it.
Docs updated
UPDATES-NEEDED.md— UPD-002 partially met, UPD-007 done, UPD-012 addedBACKLOG.md§8 — theworkflowscope blocker ticked offCONTRIBUTING.md—nvm usein setup; the stale claim that the lint baseline "hasknown legacy failures" corrected (it is clean, and CI now enforces it); §5 explains
what CI runs and what to do when the snapshot check fails
What the first CI run found
The drift check failed on its first run — and it was right to. Regenerating the
snapshot on Linux against the Windows-generated copy on
maingives:101 deleted · 101 added · 0 modified
Every one is a Next.js RSC segment-cache prefetch payload, differing in path shape
only:
static-site/about/__next.about.__PAGE__.txt(flat file)static-site/about/__next.about/__PAGE__.txt(nested directory)I checked whether the Bash
cp -Rwas mangling paths — it is not. The Windowsout/directory already has the nested shape, so this comes fromnext builditself.
The 0-modified figure is the reassuring part: every HTML document, JavaScript
chunk, stylesheet, and asset is byte-identical across Windows and Linux. The build-ID
pin,
.gitattributes, and the icon-ordering fix all hold. The only remainingdivergence is layout of these prefetch files.
So the check now excludes
__next.*, with the reasoning inline, and the finding isrecorded under UPD-004 — which it is direct evidence for, since that item's
acceptance criterion was precisely "the same npm command produces an equivalent
snapshot on Windows and Linux". It also means the tracked snapshot is not quite the
1:1 mirror of the deployed export it claims to be; Pages serves a Linux build.
I verified the narrowed check still catches real drift: an added file under
static-site/is detected and fails the step.CI result
Run 31921639663 — all green.
.nvmrcresolved toNode 20 is being deprecatedwarnings