Skip to content

ci: add pull-request checks and pin Node to 24 - #38

Merged
yorkerhodes3 merged 1 commit into
mainfrom
ci/pr-checks-and-node-pin
Aug 16, 2026
Merged

ci: add pull-request checks and pin Node to 24#38
yorkerhodes3 merged 1 commit into
mainfrom
ci/pr-checks-and-node-pin

Conversation

@yorkerhodes3

@yorkerhodes3 yorkerhodes3 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

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 be
merged without ever having been built
. Adds .github/workflows/ci.yml, which runs
on pull requests targeting main:

Step Guards against
npx eslint regressions against the now-clean baseline (UPD-001)
npx tsc --noEmit type errors the build's own settings might not surface
npm run build a branch that does not produce a static export
npm run sync:static + drift check static-site/ no longer describing src/

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 --porcelain rather than git diff on purpose: if a contributor adds a
page under src/ without regenerating, the new file under static-site/ is
untracked, and untracked files are invisible to git diff. I tested both branches
of that logic locally — clean tree exits 0; an untracked file under static-site/ is
detected and exits 1.

The earlier blocker is gone. UPD-002 was recorded as blocked because the gh
token had no workflow scope. The active token (yorkerhodes3) now carries
gist, 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, snapshot status. 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:

##[warning]Node.js 20 is deprecated. The following actions target Node.js 20 but are
being forced to run on Node.js 24: actions/checkout@v4, actions/configure-pages@v5,
actions/setup-node@v4, actions/upload-artifact@v4

So the declared version and the executing version had silently diverged. Pinned to
Node 24 — current LTS ("Krypton"), and already the local version:

  • .nvmrc24
  • package.json"engines": { "node": ">=24" }
  • both workflows → node-version-file: .nvmrc, so the pin has one source

Actions 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.using in its action.yml) rather than assumed:

checkout@v4→v7 · setup-node@v4→v7 · configure-pages@v5→v6 ·
upload-pages-artifact@v3→v5 · deploy-pages@v4→v5

One trap worth flagging

upload-pages-artifact v4 introduced dotfile exclusion — its archive step runs
--exclude=.[^/]* unless include-hidden-files is set. This workflow does
touch out/.nojekyll. Bumping v3→v5 without noticing would have silently dropped
.nojekyll
from the published artifact. The PR sets include-hidden-files: true
so behaviour is preserved exactly, with a comment saying why.

Release notes were also checked for the other majors: setup-node@v6 limits
automatic caching to npm (we pass cache: npm explicitly, so no effect), and
checkout@v7 blocks fork checkout for pull_request_target/workflow_run (we use
plain pull_request, so no effect).

Deliberately not included

@types/node still resolves to 20.19.43 while the runtime is 24. Bumping it to ^24
typechecks cleanly — I tried it — but npm install under npm 11.9.0 rewrites
package-lock.json and strips the libc constraint from 43 entries (43 removed, 0
added)
. 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 is
recorded as UPD-012 instead.

Verification

Run locally on this branch, all green:

Check Result
npx eslint exit 0
npx tsc --noEmit exit 0
npm run sync:static exit 0
git status --porcelain -- static-site 0 files — snapshot in sync
npm ci with engines present, lock unchanged exit 0
Both workflow files parse as valid YAML
git diff origin/main -- package-lock.json empty

The 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 added
  • BACKLOG.md §8 — the workflow scope blocker ticked off
  • CONTRIBUTING.mdnvm use in setup; the stale claim that the lint baseline "has
    known 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 main gives:

101 deleted · 101 added · 0 modified

Every one is a Next.js RSC segment-cache prefetch payload, differing in path shape
only
:

Platform Path
Linux static-site/about/__next.about.__PAGE__.txt (flat file)
Windows static-site/about/__next.about/__PAGE__.txt (nested directory)

I checked whether the Bash cp -R was mangling paths — it is not. The Windows
out/ directory already has the nested shape, so this comes from next build
itself.

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 remaining
divergence is layout of these prefetch files.

So the check now excludes __next.*, with the reasoning inline, and the finding is
recorded 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 31921639663all green.

Evidence Result
Lint, typecheck, build, snapshot all passed
.nvmrc resolved to Node v24.19.0 (current LTS)
Node 20 is being deprecated warnings 0 (previously on every step)

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
yorkerhodes3 force-pushed the ci/pr-checks-and-node-pin branch from 9c0f66b to 97d1a47 Compare August 16, 2026 02:19
@yorkerhodes3
yorkerhodes3 merged commit 3b27dcb into main Aug 16, 2026
1 check passed
@yorkerhodes3
yorkerhodes3 deleted the ci/pr-checks-and-node-pin branch August 16, 2026 02:24
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.

2 participants