276b048fa9
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.
63 lines
2.6 KiB
TypeScript
63 lines
2.6 KiB
TypeScript
// Desktop auto-update — prompt-on-update flow.
|
|
//
|
|
// Runs ONLY inside the Tauri desktop shell; a plain browser has no updater, so
|
|
// this is a guarded no-op there. On launch it checks the configured update
|
|
// endpoint (tauri.conf.json → plugins.updater); if a signed newer version is
|
|
// available it asks the operator, then downloads + installs and relaunches.
|
|
//
|
|
// The plugins are imported dynamically so the browser build never bundles them
|
|
// and never tries to resolve the Tauri APIs. Offline-first: a failed check (no
|
|
// network — the appliance is usually offline) is swallowed; updates only happen
|
|
// when someone has brought the box online (e.g. a phone hotspot) on purpose.
|
|
|
|
/** True when running inside the Tauri webview (not a normal browser). */
|
|
function inTauri(): boolean {
|
|
return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
|
|
}
|
|
|
|
export interface UpdatePrompt {
|
|
/** Newer version string offered by the server. */
|
|
version: string;
|
|
/** Release notes, if the server provided them. */
|
|
notes?: string;
|
|
}
|
|
|
|
/**
|
|
* Check for an update. If one is available, calls `confirm` (your UI) with the
|
|
* version/notes; when it resolves true, downloads + installs and relaunches.
|
|
* No-op (resolves silently) in the browser or when no update / offline.
|
|
*/
|
|
export async function checkForDesktopUpdate(
|
|
confirm: (info: UpdatePrompt) => Promise<boolean>,
|
|
): Promise<void> {
|
|
if (!inTauri()) return;
|
|
try {
|
|
const { check } = await import("@tauri-apps/plugin-updater");
|
|
const update = await check();
|
|
if (!update) return; // up to date
|
|
|
|
const accepted = await confirm({ version: update.version, notes: update.body });
|
|
if (!accepted) return;
|
|
|
|
// Download + install the signed update (signature verified against the
|
|
// pubkey in tauri.conf.json), then relaunch into the new version.
|
|
try {
|
|
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");
|
|
await relaunch();
|
|
} catch (err) {
|
|
// Offline / endpoint unreachable / no update server yet → ignore. The app
|
|
// 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);
|
|
}
|
|
}
|