mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
159 lines
6.9 KiB
Bash
Executable File
159 lines
6.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# fix-appimage.sh — Remove infra libs from a Tauri-produced AppImage that crash
|
|
# on Mesa 25+ / GLib 2.88 distros (Ubuntu 26.04, Fedora 42+, etc.).
|
|
#
|
|
# Usage: fix-appimage.sh <path-to.AppImage>
|
|
#
|
|
# Set TAURI_SIGNING_PRIVATE_KEY / TAURI_SIGNING_PRIVATE_KEY_PASSWORD to
|
|
# re-sign after repacking (CI release builds). Without them the script
|
|
# repacks but skips signing, which is fine for local testing.
|
|
#
|
|
# Set APPIMAGETOOL_RUNTIME_FILE to a pre-downloaded AppImage type2 runtime to
|
|
# avoid appimagetool fetching one from its mutable `continuous` tag (CI pins
|
|
# this; unset is fine for local testing).
|
|
#
|
|
# Root cause — three interlocking failures (upstream: https://github.com/tauri-apps/tauri/issues/15665):
|
|
#
|
|
# 1. EGL crash: linuxdeploy bundles libwayland-client.so.0 (1.22) alongside
|
|
# the app. Mesa 25's libEGL calls the bundled version at runtime; the version
|
|
# 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.
|
|
#
|
|
# 3. WebKit helper mismatch (latent): the bundled WebKit helpers
|
|
# (WebKitNetworkProcess/WebKitWebProcess) have RUNPATH=$ORIGIN only, and
|
|
# linuxdeploy string-patches /usr -> ././ inside libwebkit2gtk so the helper
|
|
# dir is resolved relative to the process cwd. AppRun's chdir($APPDIR/usr)
|
|
# makes this work; any launch that bypasses AppRun (extracted-AppDir usage,
|
|
# repack workflows, dbus/systemd activation with cwd=/) resolves the helpers
|
|
# 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.
|
|
|
|
set -euo pipefail
|
|
|
|
if [[ $# -lt 1 ]]; then
|
|
echo "Usage: fix-appimage.sh <path-to.AppImage>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$1" ]]; then
|
|
echo "Error: file not found: $1" >&2
|
|
exit 1
|
|
fi
|
|
|
|
APPIMAGE_ABS="$(realpath "$1")"
|
|
APPIMAGE_DIR="$(dirname "$APPIMAGE_ABS")"
|
|
APPIMAGE_NAME="$(basename "$APPIMAGE_ABS")"
|
|
|
|
# Locate the desktop/ directory (this script lives at desktop/scripts/).
|
|
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
|
|
|
|
echo "==> Extracting $APPIMAGE_NAME"
|
|
(cd "$WORKDIR" && APPIMAGE_EXTRACT_AND_RUN=1 "$APPIMAGE_ABS" --appimage-extract)
|
|
|
|
LIBDIR="$WORKDIR/squashfs-root/usr/lib"
|
|
|
|
# Guard against a bundler layout change: if the primary offending lib is not
|
|
# where we expect it, the rm globs below would silently no-op and we'd ship
|
|
# an unfixed artifact. Fail loudly instead so a tauri/linuxdeploy upgrade
|
|
# that changes the bundled lib set gets noticed here, not by users.
|
|
if ! compgen -G "$LIBDIR/libwayland-client.so*" > /dev/null; then
|
|
echo "Error: libwayland-client not found in $LIBDIR — bundler layout changed; update fix-appimage.sh" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> Removing infra libs that conflict with system Mesa / GLib / GStreamer"
|
|
rm -f \
|
|
"$LIBDIR"/libwayland-client.so* \
|
|
"$LIBDIR"/libwayland-cursor.so* \
|
|
"$LIBDIR"/libwayland-egl.so* \
|
|
"$LIBDIR"/libwayland-server.so* \
|
|
"$LIBDIR"/libglib-2.0.so* \
|
|
"$LIBDIR"/libgio-2.0.so* \
|
|
"$LIBDIR"/libgobject-2.0.so* \
|
|
"$LIBDIR"/libgmodule-2.0.so* \
|
|
"$LIBDIR"/libmount.so* \
|
|
"$LIBDIR"/libblkid.so* \
|
|
"$LIBDIR"/libselinux.so* \
|
|
"$LIBDIR"/libpcre2-8.so* \
|
|
"$LIBDIR"/libgst*.so* \
|
|
"$LIBDIR"/libzstd.so* \
|
|
"$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 "==> Repacking AppImage"
|
|
# Pass a pinned type2 runtime when provided (CI sets APPIMAGETOOL_RUNTIME_FILE);
|
|
# without it appimagetool downloads the runtime from its mutable `continuous`
|
|
# tag at repack time — acceptable for local testing, not for release builds.
|
|
RUNTIME_ARGS=()
|
|
if [[ -n "${APPIMAGETOOL_RUNTIME_FILE:-}" ]]; then
|
|
RUNTIME_ARGS=(--runtime-file "$APPIMAGETOOL_RUNTIME_FILE")
|
|
fi
|
|
APPIMAGE_EXTRACT_AND_RUN=1 ARCH="$(uname -m)" appimagetool \
|
|
"${RUNTIME_ARGS[@]}" \
|
|
"$WORKDIR/squashfs-root" "$APPIMAGE_ABS"
|
|
|
|
# Re-sign after repack so the updater can verify the artifact.
|
|
# Tauri 2.11 with createUpdaterArtifacts=true produces two possible formats:
|
|
# New: <name>.AppImage + <name>.AppImage.sig (sign the AppImage directly)
|
|
# Old: <name>.AppImage.tar.gz + .tar.gz.sig (tar-wrapped, then signed)
|
|
# We handle both: always re-sign the AppImage; if a .tar.gz sibling exists
|
|
# alongside it, recreate it from the freshly repacked AppImage and re-sign that.
|
|
# Our release config pins createUpdaterArtifacts: true (build-release-config.mjs),
|
|
# so the tar.gz branch is dead in CI today — kept deliberately because the
|
|
# workflow's artifact-locate step prefers a tar.gz when one exists; dropping
|
|
# this branch could publish a stale tarball containing the unfixed AppImage.
|
|
if [[ -n "${TAURI_SIGNING_PRIVATE_KEY:-}" ]]; then
|
|
# `tauri signer sign` reads TAURI_SIGNING_PRIVATE_KEY and
|
|
# TAURI_SIGNING_PRIVATE_KEY_PASSWORD from the environment (same as the
|
|
# macOS jobs in release.yml) — never pass the password via argv, where
|
|
# it would be visible in /proc/<pid>/cmdline.
|
|
echo "==> Re-signing AppImage"
|
|
(cd "$DESKTOP_DIR" && pnpm tauri signer sign "$APPIMAGE_ABS")
|
|
|
|
TARBALL="$APPIMAGE_ABS.tar.gz"
|
|
if [[ -f "$TARBALL" ]]; then
|
|
echo "==> Recreating updater archive $TARBALL"
|
|
tar -czf "$TARBALL" -C "$APPIMAGE_DIR" "$APPIMAGE_NAME"
|
|
echo "==> Re-signing updater archive"
|
|
(cd "$DESKTOP_DIR" && pnpm tauri signer sign "$TARBALL")
|
|
fi
|
|
else
|
|
echo "==> TAURI_SIGNING_PRIVATE_KEY not set — skipping signing (local build)"
|
|
fi
|
|
|
|
echo "==> Done: $APPIMAGE_ABS"
|