From 09a07f5303e9eb67edc022de07b2018995f3271b Mon Sep 17 00:00:00 2001 From: Mollusk Date: Thu, 28 May 2026 16:10:02 -0400 Subject: [PATCH] feat(packaging): add a thin AppImage build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build-appimage.sh produces pixelpass--x86_64.AppImage via linuxdeploy. PixelPass links almost nothing (only libpipewire, which is excludelisted) and shells out to gst-launch-1.0/pactl/a player on the host PATH, while its GUI graphics libs are dlopen'd and also excludelisted — so the AppImage bundles just the binary, AppRun, desktop entry, and icon (~13 MB, zero bundled libs). The no-sandbox model lets the bundled binary spawn the host's tools, which is why AppImage fits this orchestrator better than Flatpak. AppRun opens --gui when launched with no args and no controlling terminal (file manager / .desktop), and passes through otherwise so the CLI, interactive menu, and viewer all work. README documents the host-deps contract + the glibc-baseline and VAAPI caveats. Verified: builds to 13 MB; --version/--help work; the GUI launches cleanly on a live Wayland session via both --gui and the no-tty AppRun path. Co-Authored-By: Claude Opus 4.8 --- packaging/appimage/.gitignore | 3 ++ packaging/appimage/AppRun | 20 ++++++++++ packaging/appimage/README.md | 41 ++++++++++++++++++++ packaging/appimage/build-appimage.sh | 57 ++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 packaging/appimage/.gitignore create mode 100755 packaging/appimage/AppRun create mode 100644 packaging/appimage/README.md create mode 100755 packaging/appimage/build-appimage.sh diff --git a/packaging/appimage/.gitignore b/packaging/appimage/.gitignore new file mode 100644 index 0000000..d05945f --- /dev/null +++ b/packaging/appimage/.gitignore @@ -0,0 +1,3 @@ +.tools/ +AppDir/ +*.AppImage diff --git a/packaging/appimage/AppRun b/packaging/appimage/AppRun new file mode 100755 index 0000000..b4a5b61 --- /dev/null +++ b/packaging/appimage/AppRun @@ -0,0 +1,20 @@ +#!/bin/sh +# AppRun for the PixelPass AppImage. +# +# PixelPass is an orchestrator: it shells out to gst-launch-1.0, pactl, and a +# player (mpv/vlc) found on the host PATH. We prepend our own usr/bin so any +# bundled helpers win, but the host's tools remain reachable — that's why this +# app suits AppImage (no sandbox) better than a Flatpak. +HERE="$(dirname "$(readlink -f "$0")")" +export PATH="$HERE/usr/bin:$PATH" + +BIN="$HERE/usr/bin/pixelpass" + +# With no arguments and no controlling terminal — i.e. launched from a file +# manager or the .desktop entry — open the GUI. From a terminal, or with any +# argument (a ticket, --host, --gui, --repair, …), pass through so the CLI and +# the interactive menu both work. +if [ "$#" -eq 0 ] && [ ! -t 0 ]; then + exec "$BIN" --gui +fi +exec "$BIN" "$@" diff --git a/packaging/appimage/README.md b/packaging/appimage/README.md new file mode 100644 index 0000000..997551f --- /dev/null +++ b/packaging/appimage/README.md @@ -0,0 +1,41 @@ +# PixelPass AppImage + +A "thin" AppImage: the gui-enabled `pixelpass` binary, a launcher (`AppRun`), +and the desktop entry + icon. Run `./build-appimage.sh` to produce +`pixelpass--x86_64.AppImage`. + +## Why thin + +`pixelpass` is an orchestrator — it links almost nothing (only `libpipewire`, +which is excludelisted because it must match the host daemon) and instead +**shells out** to `gst-launch-1.0`, `pactl`, and a player (`mpv`/`vlc`) found on +the host `PATH`. The GUI's graphics libraries (`libGL`, `libwayland-*`, +`libxkbcommon`, X11) are dlopen'd at runtime and are likewise on the AppImage +excludelist — every desktop already has a matching set. So there is nothing +useful to bundle, and bundling the graphics stack would only risk driver +mismatches. The AppImage therefore carries just the binary. + +This also explains why PixelPass suits AppImage better than Flatpak: the +no-sandbox model lets the bundled binary freely spawn the host's `gst-launch`, +`pactl`, and player, which a Flatpak sandbox would block. + +## Host requirements + +The AppImage runs on any reasonably current glibc-based distro that has: + +- **GStreamer + plugins** — `gst-launch-1.0`/`gst-inspect-1.0` plus base, + good/bad/ugly, libav, and the PipeWire plugin (the binary tells you the exact + package names for your distro if something is missing). +- **PipeWire** (with the PulseAudio shim, for `pactl`). +- **A player** — `mpv` (preferred) or `vlc` — for the viewer side. +- For X11 single-window capture: `xwininfo`. + +These are the same dependencies the Arch package lists; the AppImage just spares +you the Rust toolchain. + +## Caveats + +- **Built on a bleeding-edge glibc** (CachyOS), so the AppImage requires a host + glibc at least that new. For broad compatibility, rebuild on an older base. +- **Hardware encode (VAAPI `vah264enc`)** uses the host GPU driver; it can't be + bundled. The software path (`--no-hwencode`, x264) always works. diff --git a/packaging/appimage/build-appimage.sh b/packaging/appimage/build-appimage.sh new file mode 100755 index 0000000..1f9636e --- /dev/null +++ b/packaging/appimage/build-appimage.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Build a "thin" PixelPass AppImage: the gui-enabled release binary plus only +# its non-excludelisted shared libraries. The graphics stack (libGL, wayland, +# xkbcommon, X11) is intentionally left to the host — those libs are on the +# AppImage excludelist because they must match the host driver — and the +# runtime tools PixelPass shells out to (gst-launch-1.0, pactl, mpv/vlc) are +# expected on the host PATH, the same contract the Arch package documents. +# +# Usage: packaging/appimage/build-appimage.sh +# Output: packaging/appimage/pixelpass-x86_64.AppImage +set -euo pipefail + +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo="$(cd "$here/../.." && pwd)" +tools="$here/.tools" +appdir="$here/AppDir" +mkdir -p "$tools" + +# linuxdeploy is itself an AppImage; run it without FUSE so this works on hosts +# (and CI) that lack libfuse2. +export APPIMAGE_EXTRACT_AND_RUN=1 +# Embed the version from Cargo.toml into the AppImage filename metadata. +VERSION="$(grep -m1 '^version' "$repo/Cargo.toml" | sed -E 's/.*"(.*)".*/\1/')" +export VERSION + +echo ">> building release binary (--features gui)" +( cd "$repo" && cargo build --release --features gui ) +bin="$repo/target/release/pixelpass" + +echo ">> fetching linuxdeploy" +ld="$tools/linuxdeploy-x86_64.AppImage" +if [ ! -x "$ld" ]; then + curl -fL --retry 3 -o "$ld" \ + "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" + chmod +x "$ld" +fi + +echo ">> assembling AppDir" +rm -rf "$appdir" +mkdir -p "$appdir/usr/bin" +install -m755 "$bin" "$appdir/usr/bin/pixelpass" + +echo ">> running linuxdeploy (bundles libs, builds the AppImage)" +# -e: analyse this binary for libraries to bundle (only libpipewire et al. that +# aren't excludelisted will be copied; glibc + graphics libs are skipped). +# -d/-i: desktop entry + icon for desktop integration. +# --custom-apprun: our launcher that opens --gui from a file manager. +( cd "$here" && OUTPUT="pixelpass-${VERSION}-x86_64.AppImage" "$ld" \ + --appdir "$appdir" \ + -e "$bin" \ + -d "$repo/assets/pixelpass.desktop" \ + -i "$repo/assets/pixelpass-256.png" \ + --icon-filename pixelpass \ + --custom-apprun "$here/AppRun" \ + --output appimage ) + +echo ">> done: $here/pixelpass-${VERSION}-x86_64.AppImage"