1 Commits

Author SHA1 Message Date
julian 276b048fa9 fix(desktop): sync tauri.conf.json version to the release tag, stop swallowing install failures
Build desktop / desktop (push) Successful in 4m13s
Build & push images / images (push) Successful in 2m48s
CI / check (push) Successful in 42s
Release desktop / bundle (push) Successful in 4m37s
v0.1.1 was tagged but tauri.conf.json's own "version" field (what Tauri
bakes into the bundle filename/internal version) stayed at 0.1.0 — the
signed binary didn't match what latest.json claimed to describe, so every
update download failed signature verification. desktop-updater.ts's single
catch{} swallowed that identically to "offline", so it looked like nothing
happened at all. release.yml now syncs tauri.conf.json's version from the
git tag before building; the updater now logs a real post-accept failure
instead of silently reverting.
2026-09-03 12:24:04 +02:00
4 changed files with 70 additions and 3 deletions
+21
View File
@@ -75,6 +75,27 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Sync tauri.conf.json version to the git tag
# tauri.conf.json's own "version" field is what Tauri bakes into the
# bundle filename, the app's internal version, AND the updater's
# "current vs. new" comparison — it is NOT derived from the git tag
# automatically. Hit in v0.1.1: the tag was bumped but this file
# wasn't, so the signed binary + its .sig were still built (and
# named) as 0.1.0 while latest.json (built from TAG below) claimed
# 0.1.1 — the updater found the "update", downloaded a file whose
# signature didn't match what the manifest claimed to sign, and
# silently failed (a separate bug in desktop-updater.ts's error
# handling made this invisible — also fixed). Patch it here so the
# checked-in value is only ever a placeholder for local dev builds;
# a real release's version is always driven by the tag.
run: |
set -e
VERSION="${TAG#v}"
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" apps/desktop/src-tauri/tauri.conf.json
grep '"version"' apps/desktop/src-tauri/tauri.conf.json
env:
TAG: ${{ github.ref_name }}
- name: Build + sign desktop bundle - name: Build + sign desktop bundle
env: env:
# Updater signing key (Gitea repo/org secrets). Without these the # Updater signing key (Gitea repo/org secrets). Without these the
+13 -2
View File
@@ -41,11 +41,22 @@ export async function checkForDesktopUpdate(
// Download + install the signed update (signature verified against the // Download + install the signed update (signature verified against the
// pubkey in tauri.conf.json), then relaunch into the new version. // pubkey in tauri.conf.json), then relaunch into the new version.
try {
await update.downloadAndInstall(); await update.downloadAndInstall();
} catch (err) {
// A real update WAS found and accepted — this is a genuine install
// failure (bad signature, corrupted download, disk/permission issue),
// not "offline". Surface it instead of silently reverting to the old
// version with no explanation.
console.error("desktop update download/install failed:", err);
throw err;
}
const { relaunch } = await import("@tauri-apps/plugin-process"); const { relaunch } = await import("@tauri-apps/plugin-process");
await relaunch(); await relaunch();
} catch { } catch (err) {
// Offline / endpoint unreachable / no update server yet → ignore. The app // Offline / endpoint unreachable / no update server yet → ignore. The app
// keeps running on the current version; checking again next launch. // keeps running on the current version; checking again next launch. Still
// log it so a real install failure (rethrown above) isn't invisible.
console.warn("desktop update check/apply skipped:", err);
} }
} }
+22
View File
@@ -232,3 +232,25 @@ The desktop bundle now runs in CI under **two distinct workflows** — keep the
The unsigned CI build therefore overrides it off with The unsigned CI build therefore overrides it off with
`--config '{"bundle":{"createUpdaterArtifacts":false}}'` (a JSON patch merged over the config), `--config '{"bundle":{"createUpdaterArtifacts":false}}'` (a JSON patch merged over the config),
so no `.sig` is attempted and no key is required. `release.yml` keeps the config default (signs). so no `.sig` is attempted and no key is required. `release.yml` keeps the config default (signs).
- **Gotcha (tag ≠ tauri.conf.json version — found + fixed 2026-09-03, v0.1.1).** The git tag
(`v0.1.1`) and `tauri.conf.json`'s own `"version"` field are two independent values with nothing
syncing them. Tauri bakes `"version"` into the bundle filename, the app's internal version, AND
what the updater compares against — NOT the git tag. Bumping only the tag (as the release
procedure implied) left the file at the prior `0.1.0`: the signed binary was built and named as
`0.1.0`, `latest.json` (built from `TAG`) correctly claimed `0.1.1`, and the updater found an
"update," downloaded it, then failed signature verification against a manifest that didn't
actually describe the file it pointed at. Compounded by a second bug (below) that made this
failure completely invisible to the operator. **Fix:** `release.yml` now has a "Sync
tauri.conf.json version to the git tag" step (`sed`-patches `"version"` from `TAG` right before
`tauri build`) — the checked-in value is now only a placeholder for local dev builds; every real
release derives its version from the tag automatically.
- **Gotcha (silent updater failure — found + fixed 2026-09-03).** `desktop-updater.ts`'s
`checkForDesktopUpdate` wrapped the ENTIRE check-download-install-relaunch sequence in one
`catch {}` that swallowed everything, by design, for the offline/no-server case. But that meant
a REAL failure after the operator already accepted the prompt (bad signature, corrupted
download, disk/permission error) failed exactly the same way as "endpoint unreachable" — no
error, no log, the app just silently reverted to the old version and re-showed the same "update
available" prompt on next launch, forever. This is what actually surfaced the tag-sync bug
above (download traffic visible, then nothing). Fixed by nesting `downloadAndInstall()` in its
own try/catch that logs and rethrows — offline/no-update still no-ops silently (outer catch),
but a failure *after* the operator accepted now logs to the console instead of vanishing.
+13
View File
@@ -2754,3 +2754,16 @@ mirror-repo debugging session. Fixed by setting `VITE_API_BASE=http://127.0.0.1:
`tauri.conf.json`'s `beforeBuildCommand`, overriding the shared `.env.production` for the desktop `tauri.conf.json`'s `beforeBuildCommand`, overriding the shared `.env.production` for the desktop
build only (process env wins in Vite's load order) — verified both builds independently. Full build only (process env wins in Vite's load order) — verified both builds independently. Full
detail on [[desktop-shell-tauri]]. detail on [[desktop-shell-tauri]].
## [2026-09-03] fix | Desktop updater silently failed: tag/version drift + swallowed install errors
Two compounding bugs, both closed out on [[desktop-shell-tauri]] §"Desktop in CI": (1) the v0.1.1
release bumped only the git tag — tauri.conf.json's own "version" field (what Tauri actually bakes
into the bundle filename and internal version) stayed at 0.1.0, so the signed binary didn't match
what latest.json claimed to describe, and signature verification failed on every download; (2)
desktop-updater.ts's single blanket try/catch swallowed that failure identically to "offline/no
update," so the operator saw the prompt, watched it download, then nothing — repeating forever with
zero diagnostic trail. Fixed release.yml to sed-patch tauri.conf.json's version from the git tag
right before building (checked-in value is now dev-only, never hand-maintained for releases), and
split desktop-updater.ts's catch so a real post-accept failure logs instead of vanishing. Full
detail on [[desktop-shell-tauri]].