fix(release): latest.json entry per installer type — .deb booths could never self-update
Release desktop / bundle (push) Successful in 5m26s

tauri-plugin-updater resolves the download target as {os}-{arch}-{installer}
first (linux-x86_64-deb — the bundler stamps the installer type into the
binary, verified with `strings` on a local .deb) and only then bare
linux-x86_64. Our manifest carried only the bare key, pointing at the
AppImage. A .deb install therefore downloaded the AppImage, verified its
signature, then failed install_deb()'s is_deb check with
InvalidUpdaterFormat — after the download, before any relaunch. This, not
version drift or swallowed errors, is why v0.1.0→v0.1.6 never self-updated.

latest.json now carries linux-x86_64-deb, linux-x86_64-rpm (when built) and
linux-x86_64 (AppImage), each with its own .sig. A .deb update ends in a
polkit password prompt (pkexec dpkg -i) — the intended admin gate on a
root-installed package. README + wiki updated; wiki also records the v0.1.6
LIVE field verification.

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
2026-09-04 15:28:08 +02:00
parent 52862db8ad
commit 54e691a4c9
4 changed files with 108 additions and 18 deletions
+51 -17
View File
@@ -129,8 +129,23 @@ jobs:
# The Tauri updater fetches a manifest describing the newest version, its
# notes, and per-target {signature, url}. The URL points at the MIRROR
# repo (mca/public_releases) — that's the unauthenticated endpoint field
# appliances actually reach; see the workflow header for why. Adjust the
# platform keys you actually ship.
# appliances actually reach; see the workflow header for why.
#
# ONE ENTRY PER INSTALLER TYPE — this is what made every in-app update
# v0.1.0→v0.1.6 fail. tauri-plugin-updater looks up
# `{os}-{arch}-{installer}` FIRST (linux-x86_64-deb / -rpm / -appimage,
# from the running app's detected bundle type) and only then the bare
# `linux-x86_64`. The booths run the .deb, and the manifest used to
# carry ONLY `linux-x86_64` → the AppImage. So a .deb install found the
# "update", downloaded the AppImage, verified its signature fine, then
# handed the bytes to install_deb(), which checks they're a .deb
# (infer::archive::is_deb) and bails with InvalidUpdaterFormat — after
# the download, before any relaunch, with the error swallowed client-
# side until v0.1.6. Now each installer gets its own signed asset; the
# bare key stays for an AppImage install. .deb/.rpm updates run
# `pkexec dpkg -i` / `rpm -U`, so the operator sees a polkit password
# prompt — intended: updating a root-installed package IS an admin
# action on this box (see wiki/decisions/desktop-shell-tauri.md).
env:
SERVER_URL: ${{ github.server_url }}
MIRROR_REPO: mca/public_releases
@@ -138,22 +153,41 @@ jobs:
run: |
set -e
VERSION="${TAG#v}"
APPIMAGE=$(cd dist && ls *.AppImage | head -1)
SIG=$(cat "dist/${APPIMAGE}.sig")
ASSET_URL="${SERVER_URL}/${MIRROR_REPO}/releases/download/desktop-latest/${APPIMAGE}"
cat > dist/latest.json <<JSON
{
"version": "${VERSION}",
"notes": "Parking System ${TAG}",
"pub_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"platforms": {
"linux-x86_64": {
"signature": "${SIG}",
"url": "${ASSET_URL}"
}
}
ASSET_BASE="${SERVER_URL}/${MIRROR_REPO}/releases/download/desktop-latest"
cat > /tmp/latest.js <<'JS'
const fs = require("fs");
const [version, tag, base] = process.argv.slice(2);
const files = fs.readdirSync("dist");
const pick = (ext) => files.find((f) => f.endsWith(ext));
const entry = (f) => ({
signature: fs.readFileSync(`dist/${f}.sig`, "utf8").trim(),
url: `${base}/${f}`,
});
const deb = pick(".deb"), rpm = pick(".rpm"), appimage = pick(".AppImage");
if (!deb || !appimage) {
console.error(`missing bundle in dist/: deb=${deb} appimage=${appimage}`);
process.exit(1);
}
JSON
const platforms = {
"linux-x86_64-deb": entry(deb),
...(rpm ? { "linux-x86_64-rpm": entry(rpm) } : {}),
"linux-x86_64": entry(appimage),
};
fs.writeFileSync(
"dist/latest.json",
JSON.stringify(
{
version,
notes: `Parking System ${tag}`,
pub_date: new Date().toISOString().replace(/\.\d+Z$/, "Z"),
platforms,
},
null,
2,
) + "\n",
);
JS
node /tmp/latest.js "${VERSION}" "${TAG}" "${ASSET_BASE}"
echo "latest.json:"; cat dist/latest.json
- name: Create release + upload assets (Gitea API)