webui: make the committed bundle reproducible - #546
Draft
christopherthompson81 wants to merge 1 commit into
Draft
christopherthompson81 wants to merge 1 commit into
christopherthompson81 wants to merge 1 commit into
Conversation
dist/index.html is committed and embedded into the server binary at configure
time (CMakeLists.txt:2226), which is what lets the project build a working
server without a JavaScript toolchain. But the bundle is not reproducible:
SvelteKit defaults kit.version.name to Date.now() and derives the
__sveltekit_<id> global it embeds from it, so two builds of identical source
differ.
before: build 1 __sveltekit_1ia7gsf sha256 bb3f9c3c...
build 2 __sveltekit_1xqgjp8 sha256 6cd91a94...
after: build 1 __sveltekit_1vzi1g sha256 65bed68b...
build 2 __sveltekit_1vzi1g sha256 65bed68b...
Any two branches that rebuild the web UI therefore conflict in that file
whether or not their source changes overlap -- which is what happened to 0xShug0#539,
where dist/index.html was the only conflict while src/lib/text.ts merged
cleanly and no upstream commit had touched either file.
That the id is a pure function of this setting was confirmed by building with
the timestamp the committed bundle carries: the derived id came back as
__sveltekit_ega6lw, and the result was byte-identical to the committed file.
The package version is used rather than a constant. It is stable for a given
source tree, so the bundle reproduces, and it changes when the version is
bumped, so SvelteKit's client-side "app has been updated" check keeps working
across releases. A hard-coded string would have disabled that silently.
dist/index.html is rebuilt here so the tree is consistent: after this, running
npm run build leaves git clean instead of producing a diff every time.
Normalising the build id, the version constant and the Vite content hashes --
all three derived from this one setting -- leaves zero differing lines against
the committed bundle, so nothing else in the UI changes.
Reported as 0xShug0#545.
Contributor
Author
|
Moving this to draft so it isn't sitting in your review queue while you think over the trade-offs in #545 — no pressure either way, just ping me and I'll mark it ready again. |
christopherthompson81
marked this pull request as draft
September 15, 2026 00:06
Owner
|
@mirek190 Any thoughts on this PR? |
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.
Fixes #545.
dist/index.htmlis committed and embedded into the server binary at configure time (CMakeLists.txt:2226) — that is what lets the project build a working server without a JavaScript toolchain. But it is not reproducible: SvelteKit defaultskit.version.nametoDate.now()and derives the embedded__sveltekit_<id>from it.__sveltekit_1ia7gsf/bb3f9c3c…__sveltekit_1xqgjp8/6cd91a94…__sveltekit_1vzi1g/65bed68b…__sveltekit_1vzi1g/65bed68b…So any two branches that rebuild the web UI conflict in that file regardless of whether their source overlaps. That is what happened to #539, where
dist/index.htmlwas the only conflict whilesrc/lib/text.tsmerged cleanly and no commit on main had touched either file.Uses the package version rather than a constant: stable for a given source tree, so the bundle reproduces, but it changes when the version is bumped, so SvelteKit's client-side "app has been updated" check keeps working across releases. A hard-coded string would have disabled that silently.
Verification
The embedded id is a pure function of this setting. Building current
mainwith the timestamp its committed bundle carries (1789350153403) produced__sveltekit_ega6lw— matching it — and a file byte-identical to the committed one. So this toolchain reproduces what is in the repo, and the setting is the only thing steering the id.Nothing else in the UI changes.
dist/index.htmlis rebuilt here so the tree is consistent — after this,npm run buildleavesgit statusclean instead of producing a diff on every run. The rebuilt bundle differs from the committed one only in the version constant and the two things derived from it (the SvelteKit id, and the Vite content hashes, since the version string lives inside a chunk). Normalising those three leaves zero differing lines.Alternatives considered
Stop committing
dist/. This was the first idea and it has a real cost: the CMake step above falls back to a placeholder page when the file is absent, and the fallback succeeds. Removing the bundle would mean either requiring node for every build, or quietly shipping a server whose UI is a placeholder for anyone who did not runnpm run buildfirst. Making the artifact deterministic fixes the conflicts without giving that up..gitattributeswith-mergeon the file would stop git attempting a line-merge of minified output. Conflicts would still occur, but resolve as "take either, then rebuild" rather than producing a corrupted hybrid. Worth doing as well, perhaps, but it treats the symptom.