Skip to content

chore(ui): migrate vite 6 → 8, plugin-react 4 → 6; cache UI build in CI#2501

Merged
SamMorrowDrums merged 4 commits into
mainfrom
sammorrowdrums/bump-vite-to-8
May 19, 2026
Merged

chore(ui): migrate vite 6 → 8, plugin-react 4 → 6; cache UI build in CI#2501
SamMorrowDrums merged 4 commits into
mainfrom
sammorrowdrums/bump-vite-to-8

Conversation

@SamMorrowDrums
Copy link
Copy Markdown
Collaborator

@SamMorrowDrums SamMorrowDrums commented May 19, 2026

Supersedes #2496, which only bumped vite and left @vitejs/plugin-react@4.7.0 on a peer range (vite ^4 || ^5 || ^6 || ^7) that excludes vite 8 — breaking script/build-ui (and every Go job that embeds the UI assets) with ERESOLVE.

While I was in here I also took the opportunity to make the UI build itself materially cheaper, both locally and in CI. The two changes are split into focused commits so each is easy to review on its own.


Commit 1: migrate to vite 8

Dependency bumps

  • vite ^6.0.0^8.0.13
  • @vitejs/plugin-react ^4.3.0^6.0.2 (peers vite ^8.0.0 only)
  • vite-plugin-singlefile ^2.0.0^2.3.3 (already allowed v8; lockfile-only lift)
  • engines.node >=20^20.19.0 || >=22.12.0 — required by Vite 7+

ui/vite.config.ts — Rolldown compatibility

Vite 8 ships Rolldown, which rejects mutation of bundle in generateBundle (bundle[k] = chunk; delete bundle[k] is silently ignored with a warning). The old rename-output plugin relied on that pattern and was dropped, producing zero HTML output on the first v8 build.

Refactored to a flatten-output plugin that hoists the singlefile-inlined HTML from src/apps/<app>/index.html to <app>.html in closeBundle (post-write, on-disk).

Output is byte-for-byte the same shape

File vite 6 vite 8 Δ
get-me.html 845,327 840,070 −0.62 %
issue-write.html 1,154,612 1,141,190 −1.16 %
pr-write.html 1,168,904 1,154,865 −1.20 %

The small shrink is from Oxc replacing esbuild as the minifier. Skeleton HTML is identical; inline JS identifier sets overlap ~99.5 % (the differences are string literals in error messages and Zod i18n, not reachable code).


Commit 2: perf(ui+ci): cache build artifacts and run vite in single process

Build-script refactor

  • New ui/scripts/build.mjs runs vite.build() in a loop inside one Node process instead of three serial cross-env APP=… vite build invocations.
  • Removes the cross-env devDependency.
  • Local npm run build: 2.4 s → 1.5 s (single Vite/plugin warm-up instead of three).

Composite action with content-addressable cache

  • New .github/actions/build-ui caches the three HTML artifacts keyed on a hash of ui/ sources (package-lock.json, package.json, index.html, tsconfig*.json, vite.config.ts, src/**, scripts/**).
  • On hit: just restores files, skips Node setup and the build entirely.
  • On miss: sets up Node (with the existing ~/.npm cache) and runs script/build-ui.
  • Seven workflows (go, lint, docs-check, license-check, goreleaser, mcp-diff, code-scanning) now use the composite, replacing duplicated setup-node + Build UI pairs.

Commit 3: perf(ci): share UI artifact cache across runner OSes

Set enableCrossOsArchive: true so ubuntu / macOS / windows runners share one cache key for the (platform-independent) HTML output, instead of populating three siblings.

Measured CI impact (this PR)

Set up Node.js + Build UI step durations, baseline (bce4a16) vs cache-hit run (a8132e7):

Job Baseline Cache hit Δ
build (ubuntu-latest) 13 s 1 s −12 s
build (macos-latest) 8 s 2 s −6 s
build (windows-latest) 21 s 2 s −19 s
CodeQL / Analyze (go) 26 s 1 s −25 s
docs-check 12 s 1 s −11 s
lint 6 s 2 s −4 s
mcp-diff 8 s 1 s −7 s

That's roughly 80 CI-seconds saved per Go-only PR — and Go-only PRs are the common case. PRs that do touch ui/** invalidate the cache and pay the build cost once.


Verification

  • cd ui && npm ci
  • script/build-ui ✅ — 3 HTMLs in pkg/github/ui_dist/, byte-identical to v8 baseline
  • cd ui && npx tsc --noEmit
  • script/lint
  • script/test
  • All 11 CI checks ✅ on a8132e7

Closes #2496.

@SamMorrowDrums SamMorrowDrums requested a review from a team as a code owner May 19, 2026 07:27
Copilot AI review requested due to automatic review settings May 19, 2026 07:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the ui/ build toolchain to be compatible with Vite 8 (and its Rolldown-based build pipeline) so script/build-ui reliably produces the embedded pkg/github/ui_dist/*.html assets used by the Go server.

Changes:

  • Bump UI build dependencies to vite@^8.0.13 and @vitejs/plugin-react@^6.0.2, plus a lockfile refresh (including vite-plugin-singlefile@^2.3.3).
  • Tighten the supported Node.js engine range to ^20.19.0 || >=22.12.0 to match newer Vite requirements.
  • Refactor ui/vite.config.ts output handling from in-memory bundle mutation to a post-write “flatten output” step in closeBundle().
Show a summary per file
File Description
ui/vite.config.ts Replace the old generateBundle-based renamer with a closeBundle-based output flattener compatible with Rolldown/Vite 8.
ui/package.json Update Node engine requirements and bump Vite / React plugin / singlefile plugin versions.
ui/package-lock.json Update lockfile to reflect the dependency upgrades and new transitive graph under Vite 8.

Copilot's findings

Files not reviewed (1)
  • ui/package-lock.json: Language not supported
  • Files reviewed: 2/3 changed files
  • Comments generated: 1

Comment thread ui/vite.config.ts
SamMorrowDrums added a commit that referenced this pull request May 19, 2026
Addresses Copilot review feedback on #2501: if the singlefile-inlined HTML
isn't where we expect it (e.g. because a future Vite/Rolldown change alters
the output path), throw with the app name and expected path instead of
letting renameSync surface a bare ENOENT.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SamMorrowDrums SamMorrowDrums changed the title chore(ui): migrate vite 6 → 8 and plugin-react 4 → 6 chore(ui): migrate vite 6 → 8, plugin-react 4 → 6; cache UI build in CI May 19, 2026
SamMorrowDrums and others added 4 commits May 19, 2026 10:24
Supersedes the auto-generated bump in #2496, which only updated vite and
left @vitejs/plugin-react on a peer range that excludes vite 8, breaking
the UI build (and every Go job that embeds the UI assets) with ERESOLVE.

- vite ^6.0.0 -> ^8.0.13
- @vitejs/plugin-react ^4.3.0 -> ^6.0.2 (peers vite ^8.0.0 only)
- vite-plugin-singlefile ^2.0.0 -> ^2.3.3 (peers already allowed v8)
- engines.node >=20 -> ^20.19.0 || >=22.12.0 (Vite 7+ requirement)

Vite 8 ships Rolldown instead of Rollup, which rejects bundle mutation in
generateBundle. The rename-output plugin was doing exactly that to flatten
the singlefile-inlined HTML from src/apps/<app>/index.html down to
<app>.html. Refactored it to hoist the file in closeBundle (post-write)
and renamed it to flatten-output to reflect what it actually does.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses Copilot review feedback on #2501: if the singlefile-inlined HTML
isn't where we expect it (e.g. because a future Vite/Rolldown change alters
the output path), throw with the app name and expected path instead of
letting renameSync surface a bare ENOENT.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a content-addressable cache for the embedded UI HTML and refactor
the build script to invoke vite once per Node process instead of three
times.

* New ui/scripts/build.mjs runs vite build() in a loop within one process,
  removing the cross-env dev dependency and avoiding redundant plugin/JIT
  warm-up. Local build time drops from ~2.4s to ~1.5s.

* New .github/actions/build-ui composite action restores
  pkg/github/ui_dist/{get-me,issue-write,pr-write}.html from cache keyed on
  hashes of ui/ sources and the lockfile. On cache hit it skips Node setup
  and the build entirely; on miss it sets up Node and runs script/build-ui
  as before. Saves ~6s per workflow on Go-only PRs, which is the common
  case across seven workflows.

* Replace the duplicated setup-node + Build UI pair in seven workflows
  (go, lint, docs-check, license-check, goreleaser, mcp-diff, code-scanning)
  with a single uses: ./.github/actions/build-ui line. code-scanning keeps
  a dedicated setup-node for the JavaScript CodeQL path.

Output files are byte-identical to the pre-refactor build.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The cached HTML output is platform-independent, so set
enableCrossOsArchive on the cache step. With this any OS can restore
the cache populated by any other OS — one shared cache instead of three.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SamMorrowDrums SamMorrowDrums force-pushed the sammorrowdrums/bump-vite-to-8 branch from a8132e7 to 80c8ba1 Compare May 19, 2026 08:24
@SamMorrowDrums SamMorrowDrums merged commit 7e394af into main May 19, 2026
17 checks passed
@SamMorrowDrums SamMorrowDrums deleted the sammorrowdrums/bump-vite-to-8 branch May 19, 2026 09:52
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.

3 participants