feat(ui): channel-aware in-app self-updater for direct installs - #41
Conversation
Adds a Tauri self-updater alongside the existing Homebrew path, without letting the two drift: - Wire tauri-plugin-updater/-process/-dialog into the menu bar app. - `update_channel` command detects Homebrew's `.microbridge-brew` marker. Brew installs are routed to `brew upgrade` and never self-replaced, so the formula version and the on-disk bundle stay in sync. - "Check for Updates…" tray item + a new Updates settings tab (version, install channel, opt-in launch check, manual check button). - User-initiated by default; no background polling. The daemon stays 100% network-free — only the UI app reaches out, and only on an explicit check. Release plumbing (createUpdaterArtifacts + latest.json) lands in a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 25 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (8)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds a channel-aware in-app updater to the Microbridge Tauri UI so direct (DMG) installs can self-update while Homebrew-managed installs are routed to brew upgrade, avoiding version drift between the formula and the on-disk app bundle.
Changes:
- Introduces a new UI-side updater module that checks install channel, runs the Tauri updater flow for direct installs, and defers Homebrew installs to Terminal.
- Adds an “Updates” settings tab plus a tray “Check for Updates…” menu item wired through a frontend event listener.
- Wires Tauri updater/process/dialog plugins and capabilities, plus updater endpoint/pubkey configuration.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/microbridge-ui/src/surfaces/Settings.tsx | Adds an “Updates” tab with version/channel display, manual check button, and launch auto-check toggle |
| apps/microbridge-ui/src/lib/updater.ts | New channel-aware update logic (invoke commands + Tauri updater/dialog/process plugins) |
| apps/microbridge-ui/src/App.tsx | Adds popover-only listener + optional silent launch check |
| apps/microbridge-ui/src-tauri/tauri.conf.json | Configures updater plugin (pubkey + endpoint) |
| apps/microbridge-ui/src-tauri/src/lib.rs | Adds update_channel/app_version commands, updater plugins, and tray menu item event wiring |
| apps/microbridge-ui/src-tauri/Cargo.toml | Adds Rust dependencies for updater/process/dialog plugins |
| apps/microbridge-ui/src-tauri/Cargo.lock | Locks new transitive dependencies introduced by updater/process/dialog plugins |
| apps/microbridge-ui/src-tauri/capabilities/default.json | Grants updater/process/dialog capabilities to the UI |
| apps/microbridge-ui/package.json | Adds JS dependencies for updater/process/dialog plugins |
| apps/microbridge-ui/package-lock.json | Locks the added JS plugin dependencies |
Files not reviewed (1)
- apps/microbridge-ui/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Homebrew owns this bundle — defer to brew, never self-replace. | ||
| if ((await updateChannel()) === "brew") { | ||
| if (!silent) { | ||
| const { message } = await import("@tauri-apps/plugin-dialog"); | ||
| await message( | ||
| "Microbridge was installed with Homebrew.\n\nUpdate it from Terminal:\n\n brew update && brew upgrade microbridge", | ||
| { title: "Update Microbridge", kind: "info" }, | ||
| ); | ||
| } | ||
| return; | ||
| } |
| const [version, setVersion] = useState<string | null>(null); | ||
| const [channel, setChannel] = useState<UpdateChannel | null>(null); | ||
| const [autoCheck, setAutoCheck] = useState<boolean>(() => autoCheckEnabled()); |
| {channel === "brew" | ||
| ? "Managed by Homebrew — brew upgrade microbridge" | ||
| : channel === "direct" | ||
| ? "Direct install — updates in place" | ||
| : "Checking install type…"} |
## What Completes the in-app self-updater (follow-up to #41) by producing the artifacts it consumes at release time. ### Release plumbing - **`bundle.createUpdaterArtifacts: true`** → `tauri build` emits the signed `Microbridge.app.tar.gz` + `.sig` updater bundle alongside the `.app`/DMG. - **Signing secrets** (`TAURI_SIGNING_PRIVATE_KEY` + `_PASSWORD`) exported into the UI build. Already set on the repo. Forks without the key still build — updater artifacts auto-disable via a `--config` override, so releases never hard-fail. - **Per-arch packing**: the updater tarball is renamed `Microbridge-<target>.app.tar.gz` so the two matrix legs don't collide as release assets. - **`latest.json`** generated in the publish job (via `jq`, from the `.sig` contents) with `darwin-aarch64` + `darwin-x86_64` entries, and uploaded as a release asset. The app's endpoint `releases/latest/download/latest.json` always resolves to the newest release. ### Docs - README principle #2 reworded: the **daemon stays zero-network**; the app's *only* network call is the **opt-in, user-triggered** update check. No background pings. - INSTALL.md documents the in-app update path for direct installs and the brew-managed behavior. ## Channel safety (recap from #41) Brew installs carry a `.microbridge-brew` marker; the app routes those to `brew upgrade` and never self-replaces, so the formula version and the on-disk bundle can't drift. Direct/DMG installs self-update. ## How it's exercised This runs on the next `v*` tag (targeting `v0.2.0`). Merge order matters only in that both this and #41 must be on `main` before tagging — no release is cut before then. ## Verification - `release.yml` validated as YAML; `tauri.conf.json` valid JSON with `createUpdaterArtifacts: true`. - `cargo build` on the tauri crate passes with the config change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
What
Adds a Tauri in-app self-updater alongside the existing Homebrew path — and makes it channel-aware so the two can't drift.
The drift problem this avoids
A brew-installed
.appthat silently self-updates diverges from the formula version, and the nextbrew upgradefights it. So the updater checks how the app was installed:.microbridge-brewmarker at the bundle root) → "Check for Updates…" points the user atbrew upgrade microbridge. Never self-replaced.check()→ confirm →downloadAndInstall()→relaunch().Staying honest with principle #2 ("Zero network, no update pings")
Changes
tauri-plugin-updater/-process/-dialogwired into the menu bar app (Rust + JS + capabilities).update_channel+app_versioncommands (src-tauri/src/lib.rs).menu://check-updates; the always-loaded popover runs the flow.plugins.updaterconfig with the committed public key + the stable GitHubreleases/latest/download/latest.jsonendpoint.Not in this PR (follow-up)
Release plumbing —
bundle.createUpdaterArtifacts, signing the updater tarball in CI, and generating/uploadinglatest.json. Until that ships withv0.2.0,check()simply finds no manifest and no-ops gracefully. The updater signing keypair + GitHub secrets are already in place.Verification
npm run build(tsc + vite) ✅cargo buildon the tauri crate ✅ — compiles, and tauri's build script validates the newplugins.updaterconfig and capability permissions.🤖 Generated with Claude Code