feat(packaging): add a thin AppImage build

build-appimage.sh produces pixelpass-<version>-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 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 16:10:02 -04:00
parent eb077d81f0
commit 09a07f5303
4 changed files with 121 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
.tools/
AppDir/
*.AppImage
+20
View File
@@ -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" "$@"
+41
View File
@@ -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-<version>-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.
+57
View File
@@ -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"