Skip to content

feat(ui): channel-aware in-app self-updater for direct installs - #41

Merged
DevVig merged 1 commit into
mainfrom
feat/in-app-updater
Jul 18, 2026
Merged

feat(ui): channel-aware in-app self-updater for direct installs#41
DevVig merged 1 commit into
mainfrom
feat/in-app-updater

Conversation

@DevVig

@DevVig DevVig commented Jul 18, 2026

Copy link
Copy Markdown
Owner

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 .app that silently self-updates diverges from the formula version, and the next brew upgrade fights it. So the updater checks how the app was installed:

  • Homebrew installs (formula drops a .microbridge-brew marker at the bundle root) → "Check for Updates…" points the user at brew upgrade microbridge. Never self-replaced.
  • Direct / DMG installs → full Tauri flow: check() → confirm → downloadAndInstall()relaunch().

Staying honest with principle #2 ("Zero network, no update pings")

  • The daemon stays 100% network-free — the updater lives entirely in the UI app.
  • User-initiated by default. No background polling, no timers.
  • An opt-in "check once at launch" toggle exists (off by default); when on, it checks silently and only speaks up if an update is ready.

Changes

  • tauri-plugin-updater / -process / -dialog wired into the menu bar app (Rust + JS + capabilities).
  • update_channel + app_version commands (src-tauri/src/lib.rs).
  • "Check for Updates…" tray menu item → emits menu://check-updates; the always-loaded popover runs the flow.
  • New Updates settings tab: version, install channel, opt-in launch-check toggle, manual check button.
  • plugins.updater config with the committed public key + the stable GitHub releases/latest/download/latest.json endpoint.

Not in this PR (follow-up)

Release plumbing — bundle.createUpdaterArtifacts, signing the updater tarball in CI, and generating/uploading latest.json. Until that ships with v0.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 build on the tauri crate ✅ — compiles, and tauri's build script validates the new plugins.updater config and capability permissions.

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings July 18, 2026 14:55
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 25 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f1fe205-c6fb-4ef7-83e1-a1967e01b55e

📥 Commits

Reviewing files that changed from the base of the PR and between f0aec5f and e4a50da.

⛔ Files ignored due to path filters (2)
  • apps/microbridge-ui/package-lock.json is excluded by !**/package-lock.json
  • apps/microbridge-ui/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • apps/microbridge-ui/package.json
  • apps/microbridge-ui/src-tauri/Cargo.toml
  • apps/microbridge-ui/src-tauri/capabilities/default.json
  • apps/microbridge-ui/src-tauri/src/lib.rs
  • apps/microbridge-ui/src-tauri/tauri.conf.json
  • apps/microbridge-ui/src/App.tsx
  • apps/microbridge-ui/src/lib/updater.ts
  • apps/microbridge-ui/src/surfaces/Settings.tsx

Comment @coderabbitai help to get the list of available commands.

@DevVig
DevVig merged commit a02da6e into main Jul 18, 2026
7 checks passed
@DevVig
DevVig deleted the feat/in-app-updater branch July 18, 2026 14:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +64 to +74
// 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;
}
Comment on lines +75 to +77
const [version, setVersion] = useState<string | null>(null);
const [channel, setChannel] = useState<UpdateChannel | null>(null);
const [autoCheck, setAutoCheck] = useState<boolean>(() => autoCheckEnabled());
Comment on lines +491 to +495
{channel === "brew"
? "Managed by Homebrew — brew upgrade microbridge"
: channel === "direct"
? "Direct install — updates in place"
: "Checking install type…"}
DevVig added a commit that referenced this pull request Jul 18, 2026
## 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>
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.

2 participants