mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
fix(desktop): make Linux AppImage GStreamer work on non-Debian distros (#2176)
This commit is contained in:
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -629,7 +629,7 @@ jobs:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
|
||||
- name: Fix AppImage (remove infra libs, symlink system GStreamer)
|
||||
- name: Fix AppImage (remove infra libs, shim host GStreamer)
|
||||
run: |
|
||||
mapfile -t APPIMAGES < <(find desktop/src-tauri/target/release/bundle/appimage -name '*.AppImage' -type f)
|
||||
if [[ ${#APPIMAGES[@]} -eq 0 ]]; then
|
||||
|
||||
@@ -19,12 +19,19 @@
|
||||
# skew causes eglGetDisplay to return EGL_BAD_PARAMETER under Wayland, which
|
||||
# WebKitWebProcess treats as fatal and aborts before the window ever appears.
|
||||
#
|
||||
# 2. GStreamer crash: linuxdeploy also bundles libgst*.so* (GStreamer core libs).
|
||||
# AppRun unconditionally sets GST_PLUGIN_SYSTEM_PATH_1_0 to a dir inside the
|
||||
# AppImage that the bundler never populates (bundleMediaFramework is false by
|
||||
# default), so GStreamer's plugin discovery yields an empty registry. The
|
||||
# "GStreamer element appsink not found" error kills the render process; as a
|
||||
# side effect the broken run poisons ~/.cache/gstreamer-1.0/registry.x86_64.bin.
|
||||
# 2. GStreamer crash: linuxdeploy's compiled AppRun.wrapped force-sets
|
||||
# GST_PLUGIN_SYSTEM_PATH_1_0 to $APPDIR/usr/lib/gstreamer-1.0 -- a dir the
|
||||
# bundler never populates (bundleMediaFramework is off, and we strip the
|
||||
# bundled libgst* core below to use the host's). Crucially, once that variable
|
||||
# is set it *replaces* GStreamer's compiled-in default search path rather than
|
||||
# adding to it, so the app finds ZERO plugins on every distro:
|
||||
# "GStreamer element appsink not found" kills the WebKitWebProcess and the
|
||||
# window never paints. An earlier revision of this script hid the failure on
|
||||
# Debian only by symlinking usr/lib/gstreamer-1.0 to the Debian multiarch dir
|
||||
# (/usr/lib/x86_64-linux-gnu/gstreamer-1.0); that symlink dangles on Arch and
|
||||
# Fedora, and the "safe fallback to default discovery" it assumed does not
|
||||
# exist -- a set GST_PLUGIN_SYSTEM_PATH_1_0 disables the default. A broken run
|
||||
# also poisons ~/.cache/gstreamer-1.0/registry.x86_64.bin.
|
||||
#
|
||||
# 3. WebKit helper mismatch (latent): the bundled WebKit helpers
|
||||
# (WebKitNetworkProcess/WebKitWebProcess) have RUNPATH=$ORIGIN only, and
|
||||
@@ -35,11 +42,17 @@
|
||||
# wrong -- spawning nothing, dying on unresolved bundled libs, or spawning
|
||||
# the system helpers -- and the window never appears.
|
||||
#
|
||||
# Fix: remove the offending libs so the app uses the system copies (which are
|
||||
# newer and ABI-compatible on any distro shipping glib >= 2.72 / Ubuntu 22.04+),
|
||||
# and symlink the system GStreamer plugin directory so discovery works correctly.
|
||||
# No tauri.conf.json knob can do this — bundle.linux.appimage only exposes
|
||||
# bundleMediaFramework, files (copy-only, no remove/symlink), and bundleXdgOpen.
|
||||
# Fix: (a) remove the offending libs so the app uses the system copies (newer and
|
||||
# ABI-compatible on any distro shipping glib >= 2.72 / Ubuntu 22.04+), and
|
||||
# (b) install a launcher shim in front of the app binary that strips the
|
||||
# bundle-pointing GST_PLUGIN_* overrides AppRun.wrapped injects, letting the host
|
||||
# GStreamer resolve plugins via its own default path (correct on Debian, Arch, and
|
||||
# Fedora alike). The shim has to run *after* AppRun.wrapped: the wrapper rewrites
|
||||
# the variable last -- after every apprun-hook -- so any value set before it is
|
||||
# discarded (verified empirically; a runtime GST_PLUGIN_SYSTEM_PATH_1_0 passed
|
||||
# into the AppImage does not survive). No tauri.conf.json knob can do this --
|
||||
# bundle.linux.appimage only exposes bundleMediaFramework, files (copy-only, no
|
||||
# remove/symlink), and bundleXdgOpen.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -61,16 +74,6 @@ APPIMAGE_NAME="$(basename "$APPIMAGE_ABS")"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DESKTOP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Detect multiarch triplet for GStreamer plugin path.
|
||||
case "$(uname -m)" in
|
||||
x86_64) MULTIARCH="x86_64-linux-gnu" ;;
|
||||
aarch64) MULTIARCH="aarch64-linux-gnu" ;;
|
||||
*)
|
||||
echo "Error: unsupported architecture: $(uname -m)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
WORKDIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
@@ -107,12 +110,58 @@ rm -f \
|
||||
"$LIBDIR"/libelf.so* \
|
||||
"$LIBDIR"/libffi.so*
|
||||
|
||||
echo "==> Symlinking system GStreamer plugin directory"
|
||||
# On distros without the Debian multiarch layout (e.g. Arch), this symlink
|
||||
# dangles — GStreamer then falls back to its default plugin discovery, which
|
||||
# is a safe degradation (unlike the original empty in-bundle dir).
|
||||
rm -rf "$LIBDIR/gstreamer-1.0"
|
||||
ln -s "/usr/lib/$MULTIARCH/gstreamer-1.0" "$LIBDIR/gstreamer-1.0"
|
||||
echo "==> Installing GStreamer launcher shim on the app binary"
|
||||
# AppRun.wrapped force-sets GST_PLUGIN_SYSTEM_PATH_1_0 (and the 0.10-era
|
||||
# GST_PLUGIN_SYSTEM_PATH) to $APPDIR/usr/lib/gstreamer-1.0 — a dir we bundle no
|
||||
# plugins into. Because a set path *replaces* GStreamer's default instead of
|
||||
# extending it, the app finds zero plugins on any distro and WebKit aborts. The
|
||||
# wrapper rewrites the variable after every apprun-hook, so the only place to undo
|
||||
# it is a shim between AppRun.wrapped and the real binary. First confirm the
|
||||
# wrapper still injects the override; if a tauri/linuxdeploy bump drops it, the
|
||||
# shim becomes a harmless no-op, but we want a human to re-verify rather than
|
||||
# silently ship — so fail loudly (mirrors the libwayland guard above).
|
||||
APPRUN_WRAPPED="$WORKDIR/squashfs-root/AppRun.wrapped"
|
||||
if ! grep -aq "GST_PLUGIN_SYSTEM_PATH_1_0" "$APPRUN_WRAPPED"; then
|
||||
echo "Error: AppRun.wrapped is missing or no longer references GST_PLUGIN_SYSTEM_PATH_1_0 — GStreamer path injection changed; re-verify fix-appimage.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APP_BIN="$WORKDIR/squashfs-root/usr/bin/buzz-desktop"
|
||||
if [[ ! -f "$APP_BIN" ]]; then
|
||||
echo "Error: app binary usr/bin/buzz-desktop not found — bundler layout changed; update fix-appimage.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -e "$APP_BIN.bin" ]]; then
|
||||
echo "Error: usr/bin/buzz-desktop.bin already exists — shim already installed?" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The real binary moves aside; buzz-desktop becomes a shim AppRun.wrapped execs.
|
||||
mv "$APP_BIN" "$APP_BIN.bin"
|
||||
cat > "$APP_BIN" <<'SHIM'
|
||||
#!/usr/bin/env bash
|
||||
# GStreamer shim installed by desktop/scripts/fix-appimage.sh.
|
||||
#
|
||||
# linuxdeploy's AppRun.wrapped force-sets GST_PLUGIN_SYSTEM_PATH_1_0 to an empty
|
||||
# in-bundle dir ($APPDIR/usr/lib/gstreamer-1.0). A set path *replaces* the host's
|
||||
# default GStreamer search path, so the app finds zero plugins and WebKit aborts
|
||||
# (blank window). Drop the bundle-pointing GST_PLUGIN_* overrides so the system
|
||||
# GStreamer — which we use, having removed the bundled core libs — resolves
|
||||
# plugins via its own default path on any distro. Values that don't point into
|
||||
# this AppImage are the user's own and are preserved.
|
||||
here="$(dirname "$(readlink -f "$0")")"
|
||||
appdir="$(readlink -f "$here/../..")"
|
||||
for var in GST_PLUGIN_SYSTEM_PATH_1_0 GST_PLUGIN_SYSTEM_PATH \
|
||||
GST_PLUGIN_PATH_1_0 GST_PLUGIN_PATH \
|
||||
GST_PLUGIN_SCANNER GST_PLUGIN_SCANNER_1_0; do
|
||||
val="${!var-}"
|
||||
if [[ -n "$val" && "$val" == *"$appdir/"* ]]; then
|
||||
unset "$var"
|
||||
fi
|
||||
done
|
||||
exec -a "buzz-desktop" "$here/buzz-desktop.bin" "$@"
|
||||
SHIM
|
||||
chmod +x "$APP_BIN"
|
||||
|
||||
echo "==> Repacking AppImage"
|
||||
# Pass a pinned type2 runtime when provided (CI sets APPIMAGETOOL_RUNTIME_FILE);
|
||||
|
||||
@@ -855,7 +855,14 @@ pub(crate) fn collect_same_instance_orphans(
|
||||
|
||||
/// Binary names for the Buzz desktop/Tauri process. Used by dead-instance
|
||||
/// detection to confirm the owning desktop is still alive.
|
||||
const DESKTOP_BINARY_NAMES: &[&str] = &["Buzz", "buzz-desktop", "buzz_desktop"];
|
||||
const DESKTOP_BINARY_NAMES: &[&str] = &[
|
||||
"Buzz",
|
||||
"buzz-desktop",
|
||||
"buzz_desktop",
|
||||
// Linux limits /proc/<pid>/comm to 15 visible bytes, truncating the
|
||||
// AppImage shim's real executable name, `buzz-desktop.bin`.
|
||||
"buzz-desktop.bi",
|
||||
];
|
||||
|
||||
/// Check if a process name matches a known Buzz desktop binary.
|
||||
fn is_desktop_binary(name: &str) -> bool {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
use crate::managed_agents::known_acp_runtime;
|
||||
|
||||
// ── desktop binary name tests ───────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn appimage_binary_matches_truncated_linux_comm_name() {
|
||||
assert!(super::is_desktop_binary("buzz-desktop.bi"));
|
||||
}
|
||||
|
||||
// ── buffer_contains_identifier tests ────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user