Files
thoughtsync/desktop/packaging/install.sh
T
bvandeusenandClaude Opus 5 8a8b2b17e6
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m41s
desktop: prebuilt pacman package + verified .deb (tasks 2022, 2074)
Native packages the installer can actually fetch, before task 2014 wires up
the fetching.

Arch (task 2022, re-scoped): the source PKGBUILD is gone — asking every user
to install rust+node and compile for minutes isn't distribution. Replaced by
desktop/packaging/arch/package-prebuilt.sh, which wraps the binary the Linux
job already built into a .pkg.tar.zst. No second Rust build, no Arch CI image:
the binary bundles nothing and resolves webkit/gtk/soup by soname, identical
on both distros, with SQLite compiled in and glibc used in the safe
built-old/run-new direction. CI is Debian and has no pacman, so the step logs
.PKGINFO plus the full file listing for audit instead of pretending to verify.

Debian (task 2074): install.sh hands the .deb to every Debian/Ubuntu user and
nothing had ever inspected it. tauri.conf.json now declares
libwebkit2gtk-4.1-0 + libgtk-3-0 explicitly rather than trusting inference —
and deliberately declares no appindicator or sqlite dep, since tauri is built
with features=[] and rusqlite is "bundled". desktop/packaging/deb/verify.sh
prints the generated control file, cross-checks it against what the ELF
actually needs via dpkg-shlibdeps, confirms every declared dep exists in apt,
and clean-container installs when a docker CLI is available.

Both artifacts join the run artifact and the tagged release; install.sh grows
a pacman branch so Arch/CachyOS gets a native install instead of the AppImage
fallback. Still no release cut (rule 2).

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

152 lines
6.7 KiB
Bash
Executable File

#!/bin/sh
#
# ThoughtSync desktop — one-command Linux installer.
#
# curl -fsSL https://git.fabledsword.com/bvandeusen/thoughtsync/raw/branch/main/desktop/packaging/install.sh | sh
#
# Fetches the LATEST published release for this machine's architecture and
# installs it, ending with a working app + menu entry. Native-first:
# * Arch/CachyOS (pacman) -> the native .pkg.tar.zst (system libs; needs sudo).
# * Debian/Ubuntu (dpkg+apt) -> the native .deb (system libs; needs sudo).
# * everything else (Fedora/openSUSE/…) -> the de-bundled AppImage,
# installed user-locally (no sudo). The AppImage's graphics libs are
# stripped in CI (see debundle-graphics.sh), so it uses the host GPU stack
# and renders where a stock Tauri AppImage would black-window (issue 2021).
#
# POSIX sh (dash-safe) so `curl … | sh` works everywhere. Dependency-light and
# auditable on purpose — read it before you pipe it.
set -eu
INSTANCE="https://git.fabledsword.com"
REPO="bvandeusen/thoughtsync"
API="$INSTANCE/api/v1/repos/$REPO"
say() { printf '==> %s\n' "$1"; }
die() { printf 'error: %s\n' "$1" >&2; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }
have curl || die "curl is required."
# --- architecture gate ------------------------------------------------------
# Only x86_64 is built today; arm64 will be added when the CI matrix grows. The
# release-asset naming carries the arch, but since only one arch ships now we
# match by file extension below and just guard the arch here.
arch="$(uname -m)"
case "$arch" in
x86_64 | amd64) : ;;
*) die "ThoughtSync ships x86_64 Linux builds only right now (this machine: $arch)." ;;
esac
# --- resolve the latest release ---------------------------------------------
say "Finding the latest ThoughtSync release…"
json="$(curl -fsSL "$API/releases/latest" 2>/dev/null)" || die \
"no published release found at $INSTANCE/$REPO/releases — the maintainer publishes one by pushing a v* tag."
# Pull asset URLs straight out of the release JSON (no jq): the only
# .AppImage/.deb URLs present are the asset download links.
appimage_url="$(printf '%s' "$json" | grep -oE 'https?://[^"]+\.AppImage' | head -1 || true)"
deb_url="$(printf '%s' "$json" | grep -oE 'https?://[^"]+\.deb' | head -1 || true)"
pkg_url="$(printf '%s' "$json" | grep -oE 'https?://[^"]+\.pkg\.tar\.[a-z]+' | head -1 || true)"
version="$(printf '%s' "$json" | grep -oE '"tag_name":"[^"]+"' | head -1 | sed -E 's/.*:"([^"]+)".*/\1/')"
[ -n "$appimage_url" ] || [ -n "$deb_url" ] || [ -n "$pkg_url" ] ||
die "the latest release has no installable Linux asset."
say "Latest release: ${version:-unknown}"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT INT TERM
# Both native paths install system-wide, so they need root. Resolved once here
# rather than duplicated per branch; the AppImage path below never calls this.
need_root() {
if [ "$(id -u)" -eq 0 ]; then sudo=""; else
have sudo || die "a native install needs root; re-run as root or install sudo."
sudo="sudo"
fi
say "Installing (you may be prompted for your password)…"
}
# --- native pacman path (Arch/CachyOS/Manjaro) ------------------------------
# Preferred over the AppImage on Arch: pacman pulls webkit2gtk-4.1 itself and the
# app then runs against the host graphics stack, which is what keeps the
# EGL_BAD_PARAMETER black window (issue 2021) from coming back. It also means the
# app is tracked by the package manager and uninstalls cleanly.
if have pacman && [ -n "$pkg_url" ]; then
say "Arch-family system detected — installing the native pacman package"
# Keep the published filename: pacman -U expects a *.pkg.tar.* name and refuses
# a file that doesn't look like a package, whatever its actual contents.
pkg_file="$tmp/$(basename "$pkg_url")"
curl -fSL -o "$pkg_file" "$pkg_url"
need_root
$sudo pacman -U --noconfirm "$pkg_file"
say "Done. Launch ThoughtSync from your application menu, or run thoughtsync."
exit 0
fi
# --- native .deb path (Debian/Ubuntu) ---------------------------------------
if have dpkg && have apt-get && [ -n "$deb_url" ]; then
say "Debian-family system detected — installing the native .deb"
curl -fSL -o "$tmp/thoughtsync.deb" "$deb_url"
need_root
# apt-get resolves the .deb's dependencies (webkit2gtk etc.). dpkg is the
# fallback if this apt is too old for local-file installs — it leaves the deps
# unconfigured, so `apt-get -f install` is what actually completes that path.
$sudo apt-get install -y "$tmp/thoughtsync.deb" ||
{ $sudo dpkg -i "$tmp/thoughtsync.deb" || true; $sudo apt-get -f install -y; }
say "Done. Launch ThoughtSync from your application menu."
exit 0
fi
# --- universal AppImage path (user-local, no sudo) --------------------------
# Install into ~/Applications/ThoughtSync.AppImage — the SAME location the app's
# own self-integration uses (src/integration.rs) — so the running app sees
# itself already installed and never makes a second copy or menu entry.
say "Installing the de-bundled AppImage (user-local, no sudo)"
[ -n "$appimage_url" ] || die "no AppImage asset on the latest release."
apps_dir="$HOME/Applications"
dest="$apps_dir/ThoughtSync.AppImage"
mkdir -p "$apps_dir"
say "Downloading $(basename "$appimage_url")…"
curl -fSL -o "$tmp/ThoughtSync.AppImage" "$appimage_url"
chmod +x "$tmp/ThoughtSync.AppImage"
mv -f "$tmp/ThoughtSync.AppImage" "$dest"
# Menu entry — written to match integration.rs verbatim (same paths + fields),
# so the app reports is_integrated=true and won't duplicate it.
apps_menu="$HOME/.local/share/applications"
icons_dir="$HOME/.local/share/icons"
mkdir -p "$apps_menu" "$icons_dir"
# Best-effort: pull the real icon out of the AppImage (.DirIcon) so the menu
# entry looks right immediately. Extraction is a non-GUI unsquash (no FUSE, no
# black-window risk); if it fails we fall back to the themed name and the app
# writes its embedded icon on first launch anyway.
icon_ref="thoughtsync"
if ( cd "$tmp" && "$dest" --appimage-extract .DirIcon >/dev/null 2>&1 ) \
&& cp -L "$tmp/squashfs-root/.DirIcon" "$icons_dir/thoughtsync.png" 2>/dev/null; then
icon_ref="$icons_dir/thoughtsync.png"
fi
rm -rf "$tmp/squashfs-root" 2>/dev/null || true
cat > "$apps_menu/thoughtsync.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=ThoughtSync
Comment=Capture a fleeting thought in a second
Exec=$dest %U
Icon=$icon_ref
Terminal=false
Categories=Utility;Office;
StartupWMClass=ThoughtSync
EOF
have update-desktop-database && update-desktop-database "$apps_menu" >/dev/null 2>&1 || true
# Convenience CLI launcher.
mkdir -p "$HOME/.local/bin"
ln -sf "$dest" "$HOME/.local/bin/thoughtsync"
say "Installed to $dest"
printf ' Launch it from your application menu, or run \033[1mthoughtsync\033[0m'
printf ' (if ~/.local/bin is on your PATH).\n'