Skip to content

webui/native/dist/index.html is committed but not reproducible, so any two web UI PRs conflict #545

Description

@christopherthompson81

Summary

webui/native/dist/index.html is committed to the repository and is not reproducible: building the same source twice produces two different files. Any two pull requests that rebuild the web UI therefore conflict in that file, whether or not their source changes overlap.

Reproducing

Two consecutive builds of an unmodified tree:

$ cd webui/native && npm run build
  build 1: id=__sveltekit_1ia7gsf  sha256=bb3f9c3c3be60ded…
  build 2: id=__sveltekit_1xqgjp8  sha256=6cd91a941e0e9d91…

The difference is a per-build identifier that SvelteKit embeds in three places:

-  __sveltekit_191iq4z = { … }
+  __sveltekit_1w7a9yp = { … }
-  this.__sveltekit_191iq4z = this.__sveltekit_191iq4z || {}; …
+  this.__sveltekit_1w7a9yp = this.__sveltekit_1w7a9yp || {}; …
-  __sveltekit_191iq4z.app.start(element);
+  __sveltekit_1w7a9yp.app.start(element);

It is derived from kit.version.name, which SvelteKit defaults to Date.now().toString(). webui/native/svelte.config.js does not set it, so every build gets a new one.

This is what made #539 conflict: the only conflicting file was dist/index.html, while src/lib/text.ts merged cleanly and no upstream commit had touched either file.

Suggested fix

Set kit.version.name to something stable for a given source state. Verified — with

kit: {
  ,
  version: { name: 'audiocpp' }
}

two consecutive builds produce an identical identifier and an identical SHA256.

One trade-off worth deciding rather than inheriting: SvelteKit uses version.name for its client-side "app has been updated" detection. A hard-coded constant disables that. A release version or a git describe output would keep the bundle reproducible for any given source tree and keep update detection meaningful across releases — probably the better value, though for a UI embedded in the server binary the whole binary is replaced anyway, so the detection may not be load-bearing here.

Why not simply stop committing dist/

Worth stating explicitly, since it is the obvious first suggestion: CMakeLists.txt:2226 embeds this file into the server binary at configure time, with a fallback page when it is absent:

set(AUDIOCPP_UI_DIST ".../webui/native/dist/index.html")
if (EXISTS "${AUDIOCPP_UI_DIST}")
    file(READ "${AUDIOCPP_UI_DIST}" AUDIOCPP_UI_HEX HEX)
else()
    string(HEX "<!doctype html>…<p>Embedded WebUI assets are unavailable.</p>" AUDIOCPP_UI_HEX)
endif()

Committing the bundle is what lets the project build a working server without a JavaScript toolchain installed. Removing it would mean either requiring node for every build, or shipping a server whose UI is a placeholder for anyone who builds without running npm run build first — and the failure would be quiet, because the fallback succeeds.

Making the artifact deterministic fixes the conflicts without touching that property, which is why it seems the better lever.

Lesser mitigations, if the above is unwanted

  • .gitattributes with webui/native/dist/index.html -merge (or merge=binary) stops git attempting a line-merge of minified output. Conflicts still occur but resolve as "take either, then rebuild" instead of producing a corrupted hybrid.
  • Regenerating dist/ only on release rather than in every PR, with contributors changing src/ alone.

Neither addresses the cause; both reduce the damage.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions