Files
thoughtsync/desktop/packaging/arch/package-prebuilt.sh
T
bvandeusenandClaude Opus 5 3ca3eba6d5
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m32s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m26s
Desktop (Tauri) / Update manifest (push) Successful in 4s
packaging: stamp the pacman package with the version actually built
It read the version straight out of tauri.conf.json, which was correct until
dev builds started overriding the version on the command line — the file still
says 0.1.0, so release `dev` came out carrying a pacman package labelled 0.1.0
around a binary that reports 0.1.132.

Nothing breaks from it (a pacman install can't self-update anyway), but a
package that lies about its version is exactly what makes a later "which build
is this?" impossible to answer. Now uses the same build-version.sh the bundles
and the manifest do.

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

159 lines
7.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Build a PREBUILT Arch package (.pkg.tar.zst) from the binary the desktop CI job
# already compiled — no makepkg, no Arch container, no second Rust build.
#
# WHY prebuilt: the source PKGBUILD this replaces required every user to install
# rust + nodejs + npm and compile for minutes before they could open the app.
# Distribution means shipping a package people can just install, so the one-command
# installer (install.sh) can `pacman -U` a real native package on Arch/CachyOS
# instead of falling back to the AppImage.
#
# WHY it's safe to package a Debian-compiled binary for Arch: the binary bundles
# nothing. It resolves libwebkit2gtk-4.1.so.0 / libgtk-3.so.0 / libsoup-3.0.so.0 by
# SONAME at runtime, and those sonames are identical on both distros; SQLite is
# compiled in (rusqlite "bundled"). glibc symbol versioning is forward-compatible,
# and we build on Debian's older glibc and run on Arch's newer one — the safe
# direction. Using the host's graphics stack is also exactly what keeps issue 2021
# (bundled-lib EGL_BAD_PARAMETER black window) fixed.
#
# A .pkg.tar.* is just a tar whose FIRST entry is .PKGINFO, so we assemble it
# directly rather than nesting an Arch container over the docker socket.
#
# CI cannot verify this end-to-end: the runner is Debian and has no pacman. The
# step logs .PKGINFO and the full file listing so the package is auditable from the
# build log; `pacman -U` on a real Arch box is the final proof.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
SRC_TAURI="$REPO_ROOT/desktop/src-tauri"
BINARY="$SRC_TAURI/target/release/thoughtsync-desktop"
OUT_DIR="${1:-$SRC_TAURI/target/release/bundle/arch}"
PKGNAME="thoughtsync-desktop"
PKGREL=1
PKGDESC="ThoughtSync desktop — local-first Keep-style thought capture"
URL="https://git.fabledsword.com/bvandeusen/thoughtsync"
LICENSE="MIT"
PACKAGER="ThoughtSync CI <noreply@fabledsword.com>"
# Runtime deps. Deliberately minimal and grounded in what the binary actually
# links: tauri is built with features=[] (no tray -> no libayatana-appindicator)
# and rusqlite is "bundled" (no sqlite package). Everything else — glib2, cairo,
# pango, gdk-pixbuf2, libsoup3 — arrives transitively via these two, so listing
# them would just be noise that can drift out of date.
DEPENDS=(webkit2gtk-4.1 gtk3)
[ -f "$BINARY" ] || {
echo "ERROR: no binary at $BINARY — run the tauri/cargo build first." >&2
exit 1
}
# Single source of truth for the version: the SAME helper the bundle build uses.
#
# It used to read tauri.conf.json directly, which was right until dev builds began
# overriding the version on the command line (M10.9) — the file still says 0.1.0, so
# the pacman package came out stamped 0.1.0 around a binary reporting 0.1.132. A
# package that lies about its version is exactly what makes a later "which build is
# this?" question unanswerable.
# `|| true` so a miss falls through to the explicit error below rather than
# aborting on pipefail with no explanation.
PKGVER="$(sh "$SCRIPT_DIR/../build-version.sh" || true)"
[ -n "$PKGVER" ] || { echo "ERROR: could not determine the build version" >&2; exit 1; }
# Reproducible-ish: prefer the commit date over "now" so rebuilding the same
# commit produces the same builddate.
BUILDDATE="$(git -C "$REPO_ROOT" log -1 --format=%ct 2>/dev/null || date +%s)"
echo "==> Packaging $PKGNAME $PKGVER-$PKGREL (x86_64) for pacman"
STAGE="$(mktemp -d)"
trap 'rm -rf "$STAGE"' EXIT INT TERM
# --- lay out the filesystem tree --------------------------------------------
# /usr/bin/thoughtsync (not thoughtsync-desktop): matches the CLI name the
# AppImage installer symlinks into ~/.local/bin, so the command is the same
# whichever way the app was installed.
install -Dm755 "$BINARY" "$STAGE/usr/bin/thoughtsync"
install -Dm644 "$SCRIPT_DIR/thoughtsync.desktop" \
"$STAGE/usr/share/applications/thoughtsync.desktop"
# Themed icons, so the menu entry's `Icon=thoughtsync` resolves at every size the
# desktop asks for; pixmaps is the legacy fallback for older launchers.
install -Dm644 "$SRC_TAURI/icons/32x32.png" \
"$STAGE/usr/share/icons/hicolor/32x32/apps/thoughtsync.png"
install -Dm644 "$SRC_TAURI/icons/128x128.png" \
"$STAGE/usr/share/icons/hicolor/128x128/apps/thoughtsync.png"
install -Dm644 "$SRC_TAURI/icons/128x128@2x.png" \
"$STAGE/usr/share/icons/hicolor/256x256/apps/thoughtsync.png"
install -Dm644 "$SRC_TAURI/icons/icon.png" \
"$STAGE/usr/share/icons/hicolor/512x512/apps/thoughtsync.png"
install -Dm644 "$SRC_TAURI/icons/icon.png" "$STAGE/usr/share/pixmaps/thoughtsync.png"
# Installed size in KiB, as pacman reports it.
INSTALLED_SIZE="$(du -sb "$STAGE" | cut -f1)"
# --- .PKGINFO ---------------------------------------------------------------
{
echo "# Generated by desktop/packaging/arch/package-prebuilt.sh"
echo "pkgname = $PKGNAME"
echo "pkgbase = $PKGNAME"
echo "pkgver = $PKGVER-$PKGREL"
echo "pkgdesc = $PKGDESC"
echo "url = $URL"
echo "builddate = $BUILDDATE"
echo "packager = $PACKAGER"
echo "size = $INSTALLED_SIZE"
echo "arch = x86_64"
echo "license = $LICENSE"
for d in "${DEPENDS[@]}"; do echo "depend = $d"; done
} >"$STAGE/.PKGINFO"
# --- .MTREE (optional) ------------------------------------------------------
# pacman uses .MTREE for `pacman -Qkk` file verification; it is NOT required to
# install, and generating it needs bsdtar (libarchive-tools), which the Debian CI
# image may not carry. Emit it when we can, skip loudly when we can't, rather
# than adding an apt install to the job (rule 5: the image is the toolchain).
MTREE_ENTRY=()
if command -v bsdtar >/dev/null 2>&1; then
( cd "$STAGE" && LANG=C bsdtar -czf .MTREE --format=mtree \
--options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' \
.PKGINFO usr )
MTREE_ENTRY=(.MTREE)
echo " .MTREE generated (bsdtar present)"
else
echo " NOTE: bsdtar absent — packaging without .MTREE."
echo " pacman installs this fine; only 'pacman -Qkk' file verification is degraded."
fi
# --- compress ---------------------------------------------------------------
# pacman reads whatever libarchive can decompress, so degrade gracefully instead
# of hard-requiring zstd in the build image. zstd is the modern default.
if command -v zstd >/dev/null 2>&1; then
COMP_EXT="zst"; COMP_CMD=(zstd -c -T0 -19 -)
elif command -v xz >/dev/null 2>&1; then
COMP_EXT="xz"; COMP_CMD=(xz -c -T0 -)
else
COMP_EXT="gz"; COMP_CMD=(gzip -c -)
fi
mkdir -p "$OUT_DIR"
PKGFILE="$OUT_DIR/$PKGNAME-$PKGVER-$PKGREL-x86_64.pkg.tar.$COMP_EXT"
# .PKGINFO MUST be the first entry in the archive — pacman reads it as a stream
# and gives up if the metadata isn't up front. Listing it first is what guarantees
# that ordering; `usr` is appended after.
TAR=tar
command -v bsdtar >/dev/null 2>&1 && TAR=bsdtar
( cd "$STAGE" && "$TAR" -cf - .PKGINFO "${MTREE_ENTRY[@]}" usr ) | "${COMP_CMD[@]}" >"$PKGFILE"
# --- report (the CI log IS the audit trail — see header) --------------------
echo "==> Built $(basename "$PKGFILE") ($(du -h "$PKGFILE" | cut -f1), compression: $COMP_EXT)"
echo "--- .PKGINFO ---"
sed 's/^/ /' "$STAGE/.PKGINFO"
echo "--- contents ---"
"$TAR" -tf "$PKGFILE" | sed 's/^/ /'
echo "==> Install on Arch with: sudo pacman -U $(basename "$PKGFILE")"