fa43c2f4e95effdb0c481fe3c6119f9262bfdaa5
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
43ebb6eceb |
packaging: the rolling-release prune was eating the Android client
Run 4092 published `thoughtsync.apk` to the `dev` release. Run 4098 removed it, four minutes later, and both runs were green. `write-manifest.sh` prunes the rolling channel to stop ~100 MB AppImages accumulating forever, keeping `latest.json` and anything whose name contains the current `$APP_VERSION`. The Android assets deliberately have no version in their names — a fixed name is the only addressable URL on a tag that never moves, which is the entire reason the `dev` release exists — so they matched neither rule and were swept. They would have been swept even if they HAD carried a version: Android is a different workflow with its own run number, so its version never equals the desktop's `$APP_VERSION` in this script. The keep-list is now about fixed names rather than about `latest.json` specifically, which is what the rule always meant. A fixed-name asset is self-limiting — each publish replaces the same name — so the accumulation this prune exists to prevent cannot happen to one. Worth noting how this presented: two green runs and a missing file. Nothing failed, and the only way to see it was to ask the release what it actually held rather than trusting that a step named "Publish" had published. |
||
|
|
6f47af8d96 |
ci: point the manifest at THIS build, and stop the dev release growing forever
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 |
||
|
|
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 |