mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-31 03:12:16 +00:00
The installed desktop app polls `desktop-latest/latest.json`. Previously every push to `main` (autotag -> v*.devN -> desktop.yml dispatch) rebuilt and republished `desktop-latest` as a DEV prerelease, so stable users were auto-updated onto unvetted dev builds, and any manual stable mirror was clobbered on the next merge. Split the streams so the app's channel only ever serves vetted stable: - Dev/rolling builds (v* autotag + manual workflow_dispatch) now publish to a new `desktop-edge` pre-release. The shipped app does not poll edge, so dev builds never auto-install onto stable users. - Stable `desktop-v*` builds publish the user-facing release as before, then a new `refresh-stable-channel` job copies that release's signed `latest.json` into `desktop-latest` (mirror; URLs already point at the desktop-v* assets). Cut a `desktop-v*` tag to ship an update. - `clean-release` now targets `desktop-edge`; `desktop-latest` is never wiped by CI. No app/tauri.conf.json change — the updater endpoint stays `desktop-latest`. Doc updated to describe the now-implemented stable/edge split. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
113 lines
4.4 KiB
Markdown
113 lines
4.4 KiB
Markdown
# Desktop auto-update
|
|
|
|
The OpenJarvis desktop app ships with [Tauri's updater
|
|
plugin](https://v2.tauri.app/plugin/updater/), which checks for new
|
|
versions on launch and every 30 minutes. When a newer signed build is
|
|
available, the app prompts the user to download and install it.
|
|
|
|
## How it works
|
|
|
|
```
|
|
on launch / every 30 min
|
|
│
|
|
▼
|
|
GET https://github.com/open-jarvis/OpenJarvis/releases/download/desktop-latest/latest.json
|
|
│
|
|
▼
|
|
Parse manifest: { "version": "X.Y.Z", "platforms": { ... } }
|
|
│
|
|
▼
|
|
If manifest.version > installed_version:
|
|
download signed .dmg / .deb / .msi from manifest.platforms[target].url
|
|
verify against the minisign pubkey baked into the app
|
|
prompt user to install
|
|
```
|
|
|
|
The frontend code lives in
|
|
[`frontend/src/components/Desktop/UpdateChecker.tsx`](../frontend/src/components/Desktop/UpdateChecker.tsx);
|
|
the Tauri wiring is in
|
|
[`frontend/src-tauri/tauri.conf.json`](../frontend/src-tauri/tauri.conf.json)
|
|
under `plugins.updater`.
|
|
|
|
## How releases reach the update endpoint
|
|
|
|
The `Desktop Build & Release` GitHub Action
|
|
([`.github/workflows/desktop.yml`](../.github/workflows/desktop.yml))
|
|
builds signed binaries plus a `latest.json` manifest with the
|
|
`tauri-action` step (`includeUpdaterJson: true` generates the manifest
|
|
automatically). Where it publishes depends on the trigger.
|
|
|
|
Three release streams exist:
|
|
|
|
- **`desktop-latest`** (stable auto-update channel): **this is the
|
|
channel the installed app polls.** It is *not* built directly —
|
|
instead, when a stable `desktop-vX.Y.Z` release is published, the
|
|
`refresh-stable-channel` job copies that release's `latest.json`
|
|
into `desktop-latest`. So the app is only ever offered vetted stable
|
|
builds, and `latest.json` here points at the current `desktop-v*`
|
|
assets.
|
|
- **`desktop-vX.Y.Z`** (tagged stable): created when someone pushes a
|
|
`desktop-v*` git tag. The user-facing stable release with full
|
|
installers; also the source of truth the stable channel mirrors.
|
|
- **`desktop-edge`** (rolling pre-release): rebuilt on every push to
|
|
`main` (via the `autotag` → `desktop.yml` dispatch) and on manual
|
|
`workflow_dispatch`. Carries the most recent CI build for testers.
|
|
The shipped app does **not** poll this stream, so dev builds never
|
|
auto-install onto stable users.
|
|
|
|
This split means security and telemetry-policy fixes reach users on
|
|
the next **stable** `desktop-v*` tag — cut one to ship an update.
|
|
Edge builds are available for anyone who wants to test `main` ahead of
|
|
a stable tag, without risking the stable population.
|
|
|
|
## Signing
|
|
|
|
Binaries are signed by `tauri-action` using the minisign key pair
|
|
referenced via these GitHub Actions secrets:
|
|
|
|
| Secret | Purpose |
|
|
|---|---|
|
|
| `TAURI_SIGNING_PRIVATE_KEY` | Private key (PEM-formatted minisign) |
|
|
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | Passphrase for the private key |
|
|
|
|
The matching public key is baked into the app at
|
|
`tauri.conf.json:plugins.updater.pubkey`. If you ever need to rotate
|
|
the key, replace the public key in the JSON file *and* update both
|
|
secrets atomically — mismatched keys cause every update download to
|
|
fail signature verification with no recovery path other than a manual
|
|
reinstall.
|
|
|
|
## Disabling the updater locally
|
|
|
|
For frontend development, set `VITE_OPENJARVIS_NO_UPDATER=1` in your
|
|
shell before running `npm run tauri dev`. Vite injects any
|
|
`VITE_`-prefixed env var into `import.meta.env`, and the
|
|
`UpdateChecker.tsx` component honors it to skip the 30-minute poll.
|
|
|
|
```bash
|
|
export VITE_OPENJARVIS_NO_UPDATER=1
|
|
npm run tauri dev
|
|
```
|
|
|
|
This is purely a dev escape hatch — it has no effect on production
|
|
builds (where `import.meta.env.VITE_OPENJARVIS_NO_UPDATER` will be
|
|
`undefined` unless you explicitly set it at build time).
|
|
|
|
## Verifying a release manually
|
|
|
|
```bash
|
|
# Download the latest manifest and confirm it parses cleanly
|
|
curl -fsSL https://github.com/open-jarvis/OpenJarvis/releases/download/desktop-latest/latest.json | jq .
|
|
|
|
# Fields:
|
|
# version — semver string, must match the tag (without leading "v")
|
|
# notes — release notes string
|
|
# pub_date — RFC3339 timestamp
|
|
# platforms — map keyed by "<target>-<arch>" e.g. "darwin-aarch64"
|
|
# each entry has { signature: "...", url: "..." }
|
|
```
|
|
|
|
A 404 on the manifest URL means the most recent desktop CI run
|
|
didn't complete or didn't have signing secrets — check the
|
|
`Desktop Build & Release` workflow logs.
|