chore(ui): migrate vite 6 → 8, plugin-react 4 → 6; cache UI build in CI#2501
Merged
Conversation
Contributor
There was a problem hiding this comment.
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.13and@vitejs/plugin-react@^6.0.2, plus a lockfile refresh (includingvite-plugin-singlefile@^2.3.3). - Tighten the supported Node.js engine range to
^20.19.0 || >=22.12.0to match newer Vite requirements. - Refactor
ui/vite.config.tsoutput handling from in-memory bundle mutation to a post-write “flatten output” step incloseBundle().
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
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>
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>
a8132e7 to
80c8ba1
Compare
JoannaaKL
approved these changes
May 19, 2026
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.
Supersedes #2496, which only bumped
viteand left@vitejs/plugin-react@4.7.0on a peer range (vite ^4 || ^5 || ^6 || ^7) that excludes vite 8 — breakingscript/build-ui(and every Go job that embeds the UI assets) withERESOLVE.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(peersvite ^8.0.0only)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 compatibilityVite 8 ships Rolldown, which rejects mutation of
bundleingenerateBundle(bundle[k] = chunk; delete bundle[k]is silently ignored with a warning). The oldrename-outputplugin relied on that pattern and was dropped, producing zero HTML output on the first v8 build.Refactored to a
flatten-outputplugin that hoists the singlefile-inlined HTML fromsrc/apps/<app>/index.htmlto<app>.htmlincloseBundle(post-write, on-disk).Output is byte-for-byte the same shape
get-me.htmlissue-write.htmlpr-write.htmlThe 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 processBuild-script refactor
ui/scripts/build.mjsrunsvite.build()in a loop inside one Node process instead of three serialcross-env APP=… vite buildinvocations.cross-envdevDependency.npm run build: 2.4 s → 1.5 s (single Vite/plugin warm-up instead of three).Composite action with content-addressable cache
.github/actions/build-uicaches the three HTML artifacts keyed on a hash ofui/sources (package-lock.json,package.json,index.html,tsconfig*.json,vite.config.ts,src/**,scripts/**).~/.npmcache) and runsscript/build-ui.go,lint,docs-check,license-check,goreleaser,mcp-diff,code-scanning) now use the composite, replacing duplicatedsetup-node+Build UIpairs.Commit 3:
perf(ci): share UI artifact cache across runner OSesSet
enableCrossOsArchive: trueso 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 UIstep durations, baseline (bce4a16) vs cache-hit run (a8132e7):build (ubuntu-latest)build (macos-latest)build (windows-latest)CodeQL / Analyze (go)docs-checklintmcp-diffThat'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 inpkg/github/ui_dist/, byte-identical to v8 baselinecd ui && npx tsc --noEmit✅script/lint✅script/test✅a8132e7Closes #2496.