From c9bd4b73952ed9277c0f19df4a9f92f0d3c86dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Mon, 30 Mar 2026 21:54:29 +0200 Subject: [PATCH] fix(tauri): stop stale UI assets from shadowing desktop builds (#258) ## Summary - prefer the bundled desktop UI over the downloaded cache when both report the same version, so rebuilt installers do not keep serving stale frontend assets - rebuild the server workspace during the Tauri prebuild step on every desktop package build, matching Electron's correctness boundary for fresh UI/server assets - add a regression test covering the equal-version bundled-vs-downloaded UI selection path ## Why - local desktop rebuilds should reflect the latest server and UI code without requiring users to manually clear cached assets - packaged updates should keep favoring the freshly bundled frontend when the cached copy is not actually newer ## Testing - node --import tsx --test packages/server/src/ui/__tests__/remote-ui.test.ts - npm run build:tauri --- .../server/src/ui/__tests__/remote-ui.test.ts | 27 +++++++++++++++++++ packages/server/src/ui/remote-ui.ts | 4 +-- packages/tauri-app/scripts/prebuild.js | 6 +---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/server/src/ui/__tests__/remote-ui.test.ts b/packages/server/src/ui/__tests__/remote-ui.test.ts index e858498d..99b34900 100644 --- a/packages/server/src/ui/__tests__/remote-ui.test.ts +++ b/packages/server/src/ui/__tests__/remote-ui.test.ts @@ -55,4 +55,31 @@ describe("resolveUi local version preference", () => { assert.equal(result.uiStaticDir, bundledDir) assert.equal(result.uiVersion, "0.8.1") }) + + it("prefers bundled when bundled and downloaded versions are equal", async () => { + const bundledDir = path.join(tempRoot, "bundled") + const configDir = path.join(tempRoot, "config") + const currentDir = path.join(configDir, "ui", "current") + + await mkdir(bundledDir, { recursive: true }) + await mkdir(currentDir, { recursive: true }) + + writeFileSync(path.join(bundledDir, "index.html"), "bundled") + writeFileSync(path.join(bundledDir, "ui-version.json"), JSON.stringify({ uiVersion: "0.8.1" })) + + writeFileSync(path.join(currentDir, "index.html"), "current") + writeFileSync(path.join(currentDir, "ui-version.json"), JSON.stringify({ uiVersion: "0.8.1" })) + + const result = await resolveUi({ + serverVersion: "0.8.1", + bundledUiDir: bundledDir, + autoUpdate: false, + configDir, + logger: noopLogger, + }) + + assert.equal(result.source, "bundled") + assert.equal(result.uiStaticDir, bundledDir) + assert.equal(result.uiVersion, "0.8.1") + }) }) diff --git a/packages/server/src/ui/remote-ui.ts b/packages/server/src/ui/remote-ui.ts index 1aff87df..879c5970 100644 --- a/packages/server/src/ui/remote-ui.ts +++ b/packages/server/src/ui/remote-ui.ts @@ -250,7 +250,7 @@ async function pickBestLocalUi(args: { uiStaticDir: currentResolved, source: "downloaded", uiVersion: await readUiVersion(currentResolved), - priority: 2, + priority: 1, }) } @@ -260,7 +260,7 @@ async function pickBestLocalUi(args: { uiStaticDir: bundledResolved, source: "bundled", uiVersion: await readUiVersion(bundledResolved), - priority: 1, + priority: 2, }) } diff --git a/packages/tauri-app/scripts/prebuild.js b/packages/tauri-app/scripts/prebuild.js index a382a8d2..f74825f5 100644 --- a/packages/tauri-app/scripts/prebuild.js +++ b/packages/tauri-app/scripts/prebuild.js @@ -56,11 +56,7 @@ async function ensureMonacoAssets() { function ensureServerBuild() { const distPath = path.join(serverRoot, "dist") const publicPath = path.join(serverRoot, "public") - if (fs.existsSync(distPath) && fs.existsSync(publicPath)) { - return - } - - console.log("[prebuild] server build missing; running workspace build...") + console.log("[prebuild] rebuilding server workspace for desktop packaging...") execSync("npm --workspace @neuralnomads/codenomad run build", { cwd: workspaceRoot, stdio: "inherit",