Skip to content

feat(updater): ship in-app updates from GitHub Releases - #8

Merged
kolebayev merged 4 commits into
devfrom
kolebayev/updates-delivery
Aug 21, 2026
Merged

kolebayev merged 4 commits into
devfrom
kolebayev/updates-delivery

Conversation

@kolebayev

Copy link
Copy Markdown
Owner

Summary

Platter is handed out as a DMG link, so an installed copy stayed on whatever build it was downloaded as, forever. This adds a launch-time update check against GitHub Releases, a header badge that offers the new version, and the release plumbing that produces a signed archive and a latest.json feed for it.

Changes

  • tauri-plugin-updater + tauri-plugin-process, wired in lib.rs and the default capability. The HTTP request is made in Rust, not the webview — the CSP allows no origin but 'self', and widening connect-src to reach GitHub would open that door for the whole UI.
  • ui/lib/updates.ts — check, install with download progress, relaunch. checkForUpdate never throws: the feed 404s for every build older than the first release carrying a latest.json, and an offline launch looks the same.
  • ui/components/UpdateBadge.tsx — shows the available version beside the current one, with the notes and Install and restart. A declined version is remembered by version, not as a flag, so declining 0.2.0 still leaves 0.3.0 audible.
  • bundle-dylibs.sh packs and signs Platter.app.tar.gz itself, after re-signing. Tauri's own createUpdaterArtifacts stays off: it runs inside tauri build, before a single dylib has been copied, so its archive would carry an app that still links ~/.local and /opt/homebrew — it installs cleanly and then dies in dyld on the user's machine, with no way back.
  • build-dmg.yml takes the signing key as a secret and uploads the DMG and the archive as one artifact, so a release can never ship an update that isn't the download.
  • release.yml fails outright when the .sig is missing, rather than drafting a release the updater could never use, and plants an <!-- update-notes-end --> marker in the body.
  • updater-feed.yml (new) writes latest.json on release: published — it has to carry the release notes, and those are typed into the draft long after the tag build finished. Publishing the draft is therefore the switch that ships an update.
  • README section on updates, and the Shipping updates notes in .claude/CLAUDE.md.

Test plan

  • npm test — 128 pass, including the new updates.test.ts.
  • npx tsc --noEmit — clean.
  • cargo check — clean.
  • End-to-end update path (bundle → install to /Applications → publish a draft → badge → install → relaunch) not yet exercised; it needs a real tagged release to be cut.

Note

The branch also carries 631c1c0 (README rewrite), which is already in main but not in dev, so it shows in this diff.

The DMG is handed out by link, so without a check on launch a user stays
on whatever build they downloaded once. The HTTP request is made in Rust
rather than the webview: the CSP allows no origin but 'self', and
widening connect-src to reach GitHub would open that door for the whole
UI.

A declined version is remembered by version, not as a flag, so saying no
to 0.2.0 still leaves 0.3.0 audible.

Claude-Session: https://claude.ai/code/session_01NTrpqWnfR4k3BNVhAkeJe1
The archive is packed by bundle-dylibs.sh, not by tauri's own
createUpdaterArtifacts: that one is produced inside `tauri build`,
before a single dylib has been copied, so it would carry an app that
still links ~/.local and /opt/homebrew — it installs cleanly and then
dies in dyld on the user's machine.

latest.json is written on `release: published` instead of alongside the
DMG because it has to carry the release notes, and those are typed into
the draft long after the tag build finished. A missing .sig fails the
release outright rather than drafting one the updater could never use.

Claude-Session: https://claude.ai/code/session_01NTrpqWnfR4k3BNVhAkeJe1
@kolebayev
kolebayev merged commit 550fe0b into dev Aug 21, 2026
4 checks passed
@kolebayev
kolebayev deleted the kolebayev/updates-delivery branch August 21, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant