Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 276b048fa9 | |||
| faa3265e49 |
@@ -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
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"devUrl": "http://localhost:5173",
|
"devUrl": "http://localhost:5173",
|
||||||
"frontendDist": "../../web/dist",
|
"frontendDist": "../../web/dist",
|
||||||
"beforeDevCommand": "pnpm --filter @parking/web dev",
|
"beforeDevCommand": "pnpm --filter @parking/web dev",
|
||||||
"beforeBuildCommand": "pnpm --filter @parking/web build"
|
"beforeBuildCommand": "VITE_API_BASE=http://127.0.0.1:3000 pnpm --filter @parking/web build"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"windows": [
|
"windows": [
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,10 +140,21 @@ Per the user's choices — the operator **keeps OS access** (no fullscreen lockd
|
|||||||
- **Right-click:** the context menu is blocked in **prod only** (`apps/web/src/lib/kiosk.ts`,
|
- **Right-click:** the context menu is blocked in **prod only** (`apps/web/src/lib/kiosk.ts`,
|
||||||
guarded on `import.meta.env.PROD`); dev keeps right-click + devtools. Applies to both the browser
|
guarded on `import.meta.env.PROD`); dev keeps right-click + devtools. Applies to both the browser
|
||||||
prod build and the desktop build (same SPA).
|
prod build and the desktop build (same SPA).
|
||||||
- **`VITE_API_BASE` wired to the environment:** `apps/web/.env.production` (committed, non-secret,
|
- **`VITE_API_BASE` — desktop vs. browser (regression found + fixed 2026-09-03):**
|
||||||
allow-listed in `.gitignore`) sets `VITE_API_BASE=http://127.0.0.1:3000`, auto-loaded by
|
`apps/web/.env.production` (committed, shared by both builds) sets `VITE_API_BASE=` (empty) — this
|
||||||
`vite build` (which the desktop bundle runs). So the desktop build targets Fastify with no manual
|
is correct for the **browser/booth** build (Fastify same-origin, stays relative) since commit
|
||||||
export; the browser-served-by-Fastify build should override to `""`.
|
`96fd97e` (2026-06-27), but that same change silently broke the **desktop** build, which was never
|
||||||
|
given its own override. Result: the desktop shell's `apiUrl()` returned a bare relative path
|
||||||
|
(`/api/auth/login`) to `fetch()` from a page loaded at `tauri://localhost` — WebKitGTK has no base
|
||||||
|
to resolve a relative URL against from a non-`http(s)` origin, and threw `DOMException: "The
|
||||||
|
string did not match the expected pattern."` on the first authenticated request (login). Login
|
||||||
|
worked fine in the browser (same-origin, no absolute URL needed) the whole time, which is what
|
||||||
|
made this easy to miss. **Fix:** `tauri.conf.json`'s `build.beforeBuildCommand` now sets
|
||||||
|
`VITE_API_BASE=http://127.0.0.1:3000` inline (`VITE_API_BASE=http://127.0.0.1:3000 pnpm --filter
|
||||||
|
@parking/web build`) — process env vars override `.env.production` in Vite's load order, so this
|
||||||
|
overrides the shared file for the desktop build only, without touching it (the browser/booth build
|
||||||
|
still gets the empty value, unaffected). Verified: rebuilding with the override bakes
|
||||||
|
`127.0.0.1:3000` into the bundle; rebuilding without it stays clean/relative.
|
||||||
- **Auto-update (prompt-on-update, self-hosted):** `tauri-plugin-updater` + `tauri-plugin-process`.
|
- **Auto-update (prompt-on-update, self-hosted):** `tauri-plugin-updater` + `tauri-plugin-process`.
|
||||||
On launch the SPA checks the endpoint (`apps/web/src/lib/desktop-updater.ts`, no-op in browser /
|
On launch the SPA checks the endpoint (`apps/web/src/lib/desktop-updater.ts`, no-op in browser /
|
||||||
offline), prompts the operator (i18n `update.prompt`), then `downloadAndInstall()` + `relaunch()`.
|
offline), prompts the operator (i18n `update.prompt`), then `downloadAndInstall()` + `relaunch()`.
|
||||||
@@ -221,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.
|
||||||
|
|||||||
+27
@@ -2740,3 +2740,30 @@ model (booth operator as primary adversary) makes an extractable, hard-to-rotate
|
|||||||
deployed binary worse than just publishing installers publicly. `release.yml`,
|
deployed binary worse than just publishing installers publicly. `release.yml`,
|
||||||
`apps/desktop/src-tauri/tauri.conf.json`, `apps/desktop/README.md` updated; full detail on
|
`apps/desktop/src-tauri/tauri.conf.json`, `apps/desktop/README.md` updated; full detail on
|
||||||
[[desktop-shell-tauri]].
|
[[desktop-shell-tauri]].
|
||||||
|
|
||||||
|
## [2026-09-03] fix | Desktop login broken by a VITE_API_BASE regression from the booth same-origin fix
|
||||||
|
|
||||||
|
The 2026-06-27 booth fix (commit 96fd97e) correctly blanked `apps/web/.env.production`'s
|
||||||
|
`VITE_API_BASE` for the browser/booth same-origin case, but the desktop build shares that same
|
||||||
|
file and was never given its own override — the desktop shell has been building with an empty
|
||||||
|
API base since that commit, unnoticed until now. Symptom: login threw `DOMException: "The string
|
||||||
|
did not match the expected pattern."` — WebKitGTK rejecting a relative `fetch()` URL with no base
|
||||||
|
to resolve against, since the desktop window's origin is `tauri://localhost`. Browser login was
|
||||||
|
unaffected (same-origin, no absolute URL needed), which is why this went unnoticed through the CI
|
||||||
|
mirror-repo debugging session. Fixed by setting `VITE_API_BASE=http://127.0.0.1:3000` inline in
|
||||||
|
`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
|
||||||
|
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]].
|
||||||
|
|||||||
Reference in New Issue
Block a user