Commit Graph
2 Commits
Author SHA1 Message Date
bvandeusenandClaude Opus 5 6f47af8d96 ci: point the manifest at THIS build, and stop the dev release growing forever
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m40s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m29s
Desktop (Tauri) / Update manifest (push) Successful in 7s
Two halves of one mistake, both visible on the dev release right now: the
manifest said 0.1.134 and pointed at ThoughtSync_0.1.132_amd64.AppImage.

The rolling channel accumulates every build's assets, and the manifest picked
its bundle by file extension with `head -1` — the OLDEST match. A client would
have been told 0.1.134 was available, downloaded 0.1.132, installed it, and
been offered 0.1.134 again. Forever.

Signature verification could not have caught it. The old bundle's signature is
perfectly valid for the old bundle; nothing about it says "this isn't the build
the manifest claims". Selection is now matched on the build's own version
string, so the manifest can only ever describe the binary it was written for.

The accumulation is the other half. Nothing can reach a superseded build once
the manifest moves on, and an AppImage is ~100 MB — three pushes had already
left 300 MB of unreachable binaries on the Git host. A rolling channel now
prunes everything but the current build once the manifest points at it.
Versioned releases are untouched: that IS the archive, and the stable pointer's
URLs aim into it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
2026-07-27 15:40:54 -04:00
bvandeusenandClaude Opus 5 d6734cf7a0 desktop: in-app updates, two channels, signed, fed by fixed-tag releases
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Successful in 10s
CI & Build / Build & push image (push) Successful in 30s
Desktop (Tauri) / Tauri desktop (Linux) (push) Failing after 1m59s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m23s
Desktop (Tauri) / Update manifest (push) Has been skipped
There was no in-place update anywhere. The app never checked, downloaded or
applied anything, and the only published release predates the whole sync arc —
so `install.sh` would hand out a build with no sync in it. Installing from
per-run CI artifacts, which is what's been happening, is not something an
updater can point at: ephemeral, auth-gated, no stable URL.

Two channels, switchable in the app: `stable` follows tagged releases, `dev`
follows every green push.

The feed is a Fabled-Git release asset, not a ThoughtSync server route. This
reverses the lean recorded in task 1998, and the reason matters — a
server-hosted feed can only reach a desktop that has linked a server, and
local-first-with-no-server is the whole premise. An unlinked install has to be
able to update itself.

Each channel reads a `latest.json` on a release whose TAG NEVER MOVES.
That's forced, not stylistic: Forgejo has no /releases/latest/download/<asset>
route (verified — it 404s with no redirect), so "newest" cannot be named in a
URL. `dev` carries the rolling bundles; `stable` is a pointer release holding
only the manifest, whose URLs aim at the versioned release's assets, so nothing
is duplicated.

The manifest is written by a third job that runs after both bundle jobs. They
build in separate workspaces and neither can see the other's output, but one
manifest has to describe both platforms — generating it inside either job would
silently omit the other, and a missing platform reads to a user as "no update
available" rather than as a broken feed. It reads what actually landed on the
release, so it can never advertise a bundle that failed to upload.

Signing is gated on the secret existing, in the script rather than an `if:`
(the secrets context isn't reliably available to step conditions). No key means
no updater artifacts and no publish: a feed the app would refuse to verify is
worse than no feed, because it looks like it works. CI stays green until the
key lands.

On Linux the updater can only replace an AppImage — a deb or pacman install is
owned by its package manager and must never be overwritten underneath it. The
app detects that case up front and says so, instead of failing halfway through
with a permissions error nobody can read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SreJkbxB4gx8pPsu8QbLPi
2026-07-26 19:03:12 -04:00