Skip to content

chore(ci): fix the daily CI failures — Node 22, vitest 4 config, Dependabot - #54

Merged
soodoku merged 8 commits into
mainfrom
chore/ci-node22
Aug 7, 2026
Merged

chore(ci): fix the daily CI failures — Node 22, vitest 4 config, Dependabot#54
soodoku merged 8 commits into
mainfrom
chore/ci-node22

Conversation

@soodoku

@soodoku soodoku commented Aug 7, 2026

Copy link
Copy Markdown
Member

The nightly Security & Updates run has failed every day since at least 2026-07-25, and CI/CD Pipeline has failed on every push and dependabot PR since 2026-07-20.

Root cause

Both red X's are the same bug. All seven setup-node steps pinned node-version: '18', but the locked toolchain needs Node ≥ 20.19 (vite 8, rolldown 1.1, @vitejs/plugin-react 5, vitest 4, pdfjs-dist 4). npm test died at config load:

SyntaxError: The requested module 'node:util' does not provide an export named 'styleText'
  at node_modules/rolldown/dist/shared/rolldown-build-*.mjs

styleText does not exist in Node 18. That killed the test job in ci.yml and the Run tests after updates step in the nightly dependency-updates job.

Second, vitest.config.ts still used poolOptions, which vitest 4 removed. That was a real tsc error (TS2769) hidden behind continue-on-error: true on the type-check job.

Fixing either alone leaves the other failing, so they land together.

Changes

The fix

  • Node 22 pinned once in .nvmrc (+ engines in package.json), read via node-version-file in every setup-node step.
  • vitest.config.ts migrated to vitest 4poolOptionsmaxWorkers: 1. isolate: true is deliberately kept; see the commit for why the upstream isolate: false mapping is wrong for this repo.
  • type-check is now a real gate (continue-on-error removed). next.config.ts sets typescript.ignoreBuildErrors, so this job is the repo's only type check.

Nightly workflow → Dependabot

  • Deleted the hand-rolled npm-check-updates job and the npm audit job. The updater duplicated Dependabot without its rebase handling, and the audit job ran behind continue-on-error uploading an artifact nobody read — it was the source of the nightly exit code 1 annotation.
  • Added .github/dependabot.yml: npm weekly (grouped, so a week of bumps is one or two PRs) plus github-actions weekly — the latter is what would have flagged the Node 20 deprecation before it became an annotation on every run. Dependabot had no config file here, so it was only doing security updates.
  • What's left is CodeQL, renamed to codeql.yml, running Mondays, with permissions dropped to contents: read.

Cleanups found along the way

  • outdated.json untracked and gitignored. The nightly job wrote it into the working tree, measured changes_made against a git status its own scratch file dirtied, and with no add-paths committed it — it is tracked in git and this workflow pushed it to main five times.
  • Deleted the Vercel deploy + Lighthouse jobs. Never ran (blocked behind build) and would have gone red the moment tests were fixed. Zero secrets/variables/environments, no vercel.json, zero deployments ever recorded.
  • Coverage actually emits lcov. vitest's default reporter set has no lcov, so coverage/lcov.info never existed and the Codecov upload was a silent no-op. Also fixed codecov's filefiles (renamed in v5).
  • Build artifact is no longer empty. .next is a dotfile directory and upload-artifact skips hidden paths by default. Also excludes .next/cache — 2.4 MB uploaded instead of the full webpack cache.
  • All actions bumped to latest, clearing the Node 20 deprecation annotation; trivy-action pinned off @master.
  • CodeQL matrix collapsed to a single javascript-typescript job — GitHub was warning about duplicate alerts every run.

Verification

Locally on Node 22.23.2: npm ci · format:check · lint · typecheck · test (26 passed, 2 skipped) · build — all green.

In CI: all five ci.yml jobs green with zero annotations. The old nightly workflow was also dispatched before being replaced — all jobs passed, and the PR it generated contained exactly package.json + package-lock.json, confirming the add-paths fix.

🤖 Generated with Claude Code

soodoku and others added 7 commits August 7, 2026 00:12
The locked toolchain requires Node >= 20.19 (vite 8, rolldown 1.1,
@vitejs/plugin-react 5, vitest 4, pdfjs-dist 4). On Node 18 `npm test`
died at config load with:

  SyntaxError: The requested module 'node:util' does not provide an
  export named 'styleText'

`styleText` does not exist in Node 18. This is the root cause of both
the failing `test` job in ci.yml and the failing "Run tests after
updates" step in the nightly dependency-updates job.

Pin the version once in .nvmrc and read it via node-version-file in all
seven setup-node steps, so local and CI can't drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vitest 4 removed poolOptions entirely (Tinypool is gone); singleThread
and maxThreads collapse into a top-level maxWorkers, and minThreads has
no replacement. The stale block was a real `tsc --noEmit` error:

  vitest.config.ts(24,5): error TS2769: No overload matches this call.
  'poolOptions' does not exist in type 'InlineConfig'.

Keep isolate: true. The upstream migration note maps singleThread to
"maxWorkers: 1, isolate: false", but that is a performance recipe, not a
semantic equivalence -- the old config set singleThread AND isolate, and
dropping isolation here would share src/test/setup.ts's non-configurable
global `process` replacement across files and leak logger.test.ts's
console spies (it never calls restoreAllMocks).

Also set coverage.reporter to ['text', 'lcov']. vitest's default set is
text/html/clover/json, so coverage/lcov.info never existed and the
Codecov upload in ci.yml has always been a silent no-op.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
deploy-preview, deploy-production, and performance-audit have never
executed -- they sit behind `needs: [build]`, and build has been blocked
by the failing test job. Fixing the tests would have made them run for
the first time and go red.

Nothing in this repo deploys to Vercel: the repo has zero secrets, zero
variables, and zero environments; there is no vercel.json and no
.vercel/; `gh api repos/gojiplus/reader/deployments` returns empty; and
README.md, CLAUDE.md, and firebase.json all describe a Firebase project.
Vercel appeared nowhere outside this file.

performance-audit was broken independently: lighthouserc.json sets
startServerReadyPattern to "ready on", which Next 15 never prints (it
prints "Ready in ..."), so LHCI would block until its start-server
timeout. lighthouserc.json is left in place for manual `npx lhci
autorun`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… gate

Action bumps (every `uses:` in both workflows):

  actions/checkout               v4 -> v7
  actions/setup-node             v4 -> v7
  actions/upload-artifact        v4 -> v7
  github/codeql-action/*         v3 -> v4   (v3 deprecates Dec 2026)
  codecov/codecov-action         v4 -> v7
  peter-evans/create-pull-request v5 -> v8
  aquasecurity/trivy-action  master -> 0.36.0

This clears the "Node.js 20 is deprecated" annotation on every run, and
pinning trivy-action off `master` closes an unpinned-ref supply-chain
exposure.

codecov-action v5 removed the `file` input in favour of `files`. Left
unchanged, v7 would have failed on an unknown input. Verified against
the action.yml at v7: there is no `file`.

Remove continue-on-error from type-check. The only error it was masking
was the vitest.config.ts poolOptions overload, fixed in the previous
commit; `npx tsc --noEmit` is now clean. This matters because
next.config.ts sets typescript.ignoreBuildErrors, so this job is the
repo's only type gate.

Exclude .next/cache from the build artifact. The build job has never run
to completion, so `path: .next/` has never actually uploaded -- on a
Next 15 build that is a few hundred MB of webpack cache for a 1-day
retention artifact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GitHub annotated every run:

  CodeQL language 'javascript' is referenced by more than one entry in
  the 'language' matrix parameter for job 'codeql-analysis'. This may
  result in duplicate alerts.

They are the same language. The two jobs each spent ~2m40s producing
duplicate alerts. javascript-typescript is the canonical single value --
it is what this repo's own code-scanning API reports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three bugs in one job.

1. `npm outdated --json` prints `{}` when nothing is outdated, and `{}`
   is 3 bytes, so `[ -s outdated.json ]` was always true and
   updates_available was effectively hardcoded to true.

2. The job wrote outdated.json into the working tree and then used
   `git status --porcelain` over the whole tree to decide changes_made,
   so its own scratch file influenced the answer.

3. Because create-pull-request had no add-paths, that scratch file got
   swept into the commit. outdated.json is tracked in git today and this
   workflow has committed it to main five times (#28, #29, #47, ...).

Scratch now goes to $RUNNER_TEMP, the {} case is handled, changes_made
is scoped to package.json and package-lock.json, and add-paths pins the
PR contents to those two files.

Also allow workflow_dispatch. Under `if: github.event_name ==
'schedule'` this job could not be tested without waiting for 2 AM UTC.

`npx --yes` because the log shows npm-check-updates being resolved via
an interactive install prompt, and the step is renamed to match what
`--target patch` actually does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
trivy-action tags are v-prefixed from 0.33.0 onward; @0.36.0 does not
resolve and failed the Security Scan job at set-up.

The build artifact uploaded nothing: ".next" is a dotfile directory and
upload-artifact skips hidden paths unless include-hidden-files is set.
This was pre-existing -- the build job had never run to completion, so
it had never been observed.

Verified the path patterns directly against @actions/glob (the library
upload-artifact uses): with hidden files included, ".next/" matches 202
entries and the "!.next/cache/" exclusion brings that to 140.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@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.

The "Security & Updates" workflow had three jobs and only one of them
was pulling its weight.

- audit-dependencies ran `npm audit` behind continue-on-error and
  uploaded a JSON report as a 30-day artifact. It could not fail and
  nothing consumed the artifact -- it just produced the "exit code 1"
  annotation on every nightly run.

- dependency-updates hand-rolled `npm-check-updates` in bash to open a
  patch-bump PR. Dependabot is already active on this repo and does the
  same thing with rebasing and conflict handling. The bash version had
  no rebase story, and its own scratch file kept landing in main.

- codeql-analysis is the one worth keeping, but daily is far more often
  than this repo changes.

So: add .github/dependabot.yml covering npm weekly (grouped, so a week
of bumps is one or two PRs) plus github-actions weekly -- the latter is
what would have flagged the Node 20 deprecation on checkout/setup-node
before it became an annotation on every run. Until now Dependabot was
only doing security updates from repo settings, with no config file,
which is why the bash updater existed at all.

The workflow is now CodeQL-only and renamed to match, running Mondays.
Its permissions drop to contents: read now that nothing pushes branches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@soodoku soodoku changed the title chore(ci): fix the daily CI failures — Node 22, vitest 4 config, action bumps chore(ci): fix the daily CI failures — Node 22, vitest 4 config, Dependabot Aug 7, 2026
@soodoku
soodoku merged commit 78f0236 into main Aug 7, 2026
7 checks passed
@soodoku
soodoku deleted the chore/ci-node22 branch August 7, 2026 07:40
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