Files
bvandeusenandClaude Opus 5 c268ae4f23
CI & Build / Build now, or wait for Android? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 7s
CI & Build / Python tests (push) Successful in 11s
CI & Build / integration (push) Successful in 17s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m21s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 6m36s
Desktop (Tauri) / Update manifest (push) Successful in 4s
Android / Kotlin + Rust (APK) (push) Successful in 9m24s
ci: main publishes, so a tag stops being required — and :latest stops shipping a dev client
Step 3 of M314. Note 3127 §0's diagnostic is "is `main` publishing
sufficient for a user to receive the build" — and here it was not. The
desktop and Android lanes BUILT on main and published nothing: `Publish
release` was gated on `refs/tags/v*`, the channel publishes on
`refs/heads/dev`, the manifest job on dev-or-tag. So the stable channel
moved only when somebody cut a tag, which made a `v*` tag load-bearing
rather than the optional bookmark the model wants.

Both channels are rolling fixed-tag releases now. `dev` from dev, `stable`
from main, same machinery — `publish-release.sh` already took RELEASE_TAG,
`write-manifest.sh` already pruned, and both already PATCHed a stale
description on 409 (#2182). This is wiring, not new mechanism.

## The defect this carried

`ci.yml`'s "Fetch the Android client to bake in" read
`releases/download/dev` UNCONDITIONALLY, on every branch. Every image baked
in the dev APK — `:latest` included — so a stable server served a
dev-channel client to anyone who downloaded it from there. That has nothing
to do with versioning; it is fixed here because this is the step that
finally gives `stable` an APK to point at.

It also means Android needs no channel machinery of its own. The APK is
served FROM the image, so the channel is already a property of which image
you run — note 3127 §7's "nothing to hand off" shape, arrived at here by
accident. One branch-conditional line, not a second channel in
`client_dist.py` as this milestone first assumed.

## The break this nearly shipped

`install.sh --channel stable` read the version out of `stable/latest.json`
and then fetched `releases/tags/v<version>` for the bundles — correct while
stable was a manifest-only pointer, and broken the moment stable holds its
own. Stable is the DEFAULT channel, so `curl … | sh` would have failed for
everyone between this commit and the first merge to main.

Both channels are one lookup now: fetch the fixed-tag release, install what
is on it. A transitional fallback covers the window where `stable` still
has no bundles, marked for deletion in step 7 — without it the default
channel is broken for however long it takes to merge, and that window is
gated on an operator request rather than on this lane.

## The two writers problem

`stable`'s manifest was written by tag builds. It is written by main now,
and the tag path stops writing it — two writers for one channel is a race
with no winner worth having. A `v*` tag still writes its own versioned
manifest; its build consequence goes entirely in step 7.

Also corrected: `update.rs`'s header still described stable as following
`v*` tags. Nothing in that file moved — it only ever read
`<channel>/latest.json` — but the comment was a lie, and it is the file
somebody reads to understand the feed.

#3143

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 18:22:31 -04:00

2.9 KiB

ThoughtSync desktop — Arch package

A prebuilt native pacman package, published as an asset on every ThoughtSync release. Nothing to compile, no toolchain to install.

Installing natively on Arch matters for more than tidiness: pacman pulls webkit2gtk-4.1 itself and the app runs against your system's graphics stack, which is what keeps the bundled-library EGL_BAD_PARAMETER black window (issue 2021) from coming back. It also means the app is package-manager tracked and uninstalls cleanly.

Install

Easiest — the one-command installer picks this package automatically on any pacman system:

curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/dev/desktop/packaging/install.sh | sh

That installs the newest build from main. To follow the rolling development channel instead, pass the flag through the pipe:

curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/dev/desktop/packaging/install.sh | sh -s -- --channel dev

Or grab the .pkg.tar.* from the releases page and install it directly:

sudo pacman -U thoughtsync-*-x86_64.pkg.tar.*

The compression suffix depends on what the build image provides — .zst when zstd is installed, otherwise .xz (today's builds are .xz). pacman reads all of them; only the filename differs.

Either way you get:

  • /usr/bin/thoughtsync — the app
  • /usr/share/applications/thoughtsync.desktop — the menu entry
  • /usr/share/icons/hicolor/*/apps/thoughtsync.png — themed icons

Launch ThoughtSync from your app menu, or run thoughtsync.

Uninstall: sudo pacman -R thoughtsync.

The package was called thoughtsync-desktop before; it declares replaces/ conflicts on that name, so an upgrade from it is a normal pacman -U and leaves nothing behind.

How the package is built

package-prebuilt.sh runs in CI (.forgejo/workflows/desktop.yml) and wraps the binary the Linux build already produced into a pacman package — it does not compile anything a second time.

Packaging a Debian-compiled binary for Arch is safe here because the binary bundles nothing: it resolves libwebkit2gtk-4.1.so.0, libgtk-3.so.0 and libsoup-3.0.so.0 by SONAME at runtime and those are identical on both distros, SQLite is compiled in (rusqlite "bundled"), and glibc's forward compatibility means building on Debian's older glibc and running on Arch's newer one is the safe direction.

CI is Debian and has no pacman, so it cannot install-test the result. The build step instead logs the package's .PKGINFO and complete file listing, so the package is auditable from the run log; a real pacman -U is the final proof.

Previously

This directory used to hold a source-build PKGBUILD requiring makepkg -si with rust + nodejs + npm installed. It was removed once prebuilt packages shipped — asking every user to install a compiler toolchain isn't distribution.