From ba38e7241970bbd1018b62dd1fcecafc3185c0c0 Mon Sep 17 00:00:00 2001 From: mechmyday <39288630+mechmyday@users.noreply.github.com> Date: Tue, 19 May 2026 13:34:35 -0700 Subject: [PATCH] fix(appimage): unbreak Linux launch on modern GPUs / glibc (#2052) Co-authored-by: mechmyday Co-authored-by: Steven Enamakel --- .github/workflows/build-desktop.yml | 27 ++- .../release/strip-appimage-graphics-libs.sh | 200 ++++++++++++++++++ 2 files changed, 224 insertions(+), 3 deletions(-) create mode 100755 scripts/release/strip-appimage-graphics-libs.sh diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index ce4c0d6ae..4320b2e94 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -120,7 +120,7 @@ jobs: args: --target x86_64-apple-darwin target: x86_64-apple-darwin artifact_suffix: x86_64-apple-darwin - - platform: ubuntu-22.04 + - platform: ubuntu-24.04 args: --target x86_64-unknown-linux-gnu --bundles deb appimage target: x86_64-unknown-linux-gnu artifact_suffix: ubuntu @@ -162,7 +162,7 @@ jobs: with: targets: ${{ matrix.settings.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} - name: Install Tauri dependencies (ubuntu only) - if: matrix.settings.platform == 'ubuntu-22.04' + if: matrix.settings.platform == 'ubuntu-24.04' run: | sudo apt-get update sudo apt-get install -y \ @@ -458,7 +458,7 @@ jobs: # ensures this still runs when `cargo tauri build` failed at bundling # (the binary itself is produced before bundling starts). - name: Dump linked libraries of built binary (ubuntu debug) - if: always() && matrix.settings.platform == 'ubuntu-22.04' + if: always() && matrix.settings.platform == 'ubuntu-24.04' shell: bash env: PROFILE: ${{ inputs.build_profile }} @@ -478,6 +478,27 @@ jobs: fi done + # Strip host graphics libraries (Mesa/libdrm/libva/libgbm) that lib4bin + # ldd-walks into the AppImage. Those drivers must come from the user's + # host system — even on a current Ubuntu runner the bundled Mesa can + # fail to initialize newer GPUs (RDNA3, Intel Arc, Lovelace) and + # crashes CEF on launch because the bundled libs shadow the host's + # working ones on LD_LIBRARY_PATH. CEF's own SwiftShader subdir is + # untouched. Re-signs the AppImage + updater tarball when signing + # is enabled. + - name: Strip host graphics libs from AppImage + if: matrix.settings.platform == 'ubuntu-24.04' + shell: bash + env: + MATRIX_TARGET: ${{ matrix.settings.target }} + PROFILE: ${{ inputs.build_profile }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY || secrets.UPDATER_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD || secrets.UPDATER_PRIVATE_KEY_PASSWORD }} + run: | + bash scripts/release/strip-appimage-graphics-libs.sh \ + "app/src-tauri/target/${MATRIX_TARGET}/${PROFILE}/bundle" \ + "target/${MATRIX_TARGET}/${PROFILE}/bundle" + # Regression guard for #1403: if @sentry/vite-plugin silently no-op'd # (e.g. SENTRY_AUTH_TOKEN missing, or the `sourcemaps.assets` glob # didn't match dist/assets) production events arrive in Sentry as diff --git a/scripts/release/strip-appimage-graphics-libs.sh b/scripts/release/strip-appimage-graphics-libs.sh new file mode 100755 index 000000000..05f448200 --- /dev/null +++ b/scripts/release/strip-appimage-graphics-libs.sh @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# Strip host graphics libraries from AppImage bundles so they load the user's +# system Mesa/libdrm/libva at launch instead of the older versions baked in by +# lib4bin's ldd-walk on the ubuntu-22.04 build runner. +# +# Without this, AppImages built on Mesa 22.x fail to initialize on systems +# with newer GPUs (RDNA3, Intel Arc, Lovelace) because the bundled drivers +# can't talk to the host kernel/driver stack. AppImage convention is to never +# ship graphics drivers — they must come from the host. See: +# https://github.com/AppImageCommunity/pkg2appimage/blob/master/excludelist +# +# Only top-level lib directories are swept. CEF's own subdirs (swiftshader/, +# locales/, libcef.so neighbors) are left alone — CEF ships its own +# GLES/EGL implementation that must stay bundled. +# +# Usage: strip-appimage-graphics-libs.sh [bundle-root...] +# where contains an `appimage/` subdir with *.AppImage files. +# +# Env: +# TAURI_SIGNING_PRIVATE_KEY — re-sign modified artifacts when set +# TAURI_SIGNING_PRIVATE_KEY_PASSWORD — passphrase for the key (may be empty) +# APPIMAGETOOL_URL — override appimagetool download URL +# APPIMAGETOOL_SHA256 — expected SHA256 of the download +# (verified before use when set; rotate +# alongside APPIMAGETOOL_URL) + +set -euo pipefail + +EXCLUDE_PATTERNS=( + 'libGL.so.*' + 'libGLX.so.*' + 'libGLdispatch.so.*' + 'libGLESv1_CM.so.*' + 'libGLESv2.so.*' + 'libEGL.so.*' + 'libgbm.so.*' + 'libdrm.so.*' + 'libdrm_*.so.*' + 'libva.so.*' + 'libva-drm.so.*' + 'libva-glx.so.*' + 'libva-x11.so.*' + 'libvdpau.so.*' + 'libxcb-dri2.so.*' + 'libxcb-dri3.so.*' + 'libxcb-glx.so.*' + 'libxcb-present.so.*' +) + +# Default to a pinned release tag rather than the mutable `continuous` asset so +# CI builds are reproducible and resistant to upstream replacement. Override via +# APPIMAGETOOL_URL (and bump APPIMAGETOOL_SHA256 alongside it). +APPIMAGETOOL_URL="${APPIMAGETOOL_URL:-https://github.com/AppImage/appimagetool/releases/download/1.9.0/appimagetool-x86_64.AppImage}" +APPIMAGETOOL_SHA256="${APPIMAGETOOL_SHA256:-}" + +ensure_appimagetool() { + if command -v appimagetool >/dev/null 2>&1; then + APPIMAGETOOL_BIN="$(command -v appimagetool)" + return + fi + local tool=/tmp/appimagetool.AppImage + if [ ! -x "$tool" ]; then + echo "[strip-libs] Downloading appimagetool from $APPIMAGETOOL_URL" + curl -fsSL "$APPIMAGETOOL_URL" -o "$tool" + if [ -n "$APPIMAGETOOL_SHA256" ]; then + echo "[strip-libs] Verifying appimagetool sha256" + if ! echo "${APPIMAGETOOL_SHA256} ${tool}" | sha256sum -c -; then + echo "[strip-libs] ERROR: appimagetool sha256 mismatch — refusing to run" >&2 + rm -f "$tool" + exit 1 + fi + else + echo "[strip-libs] WARNING: APPIMAGETOOL_SHA256 not set — skipping integrity check" >&2 + fi + chmod +x "$tool" + fi + APPIMAGETOOL_BIN="$tool" +} + +strip_one_appimage() { + local img="$1" + local original + original="$(realpath "$img")" + local name + name="$(basename "$original")" + local workdir + workdir="$(mktemp -d)" + + echo "[strip-libs] Processing $original" + ( + cd "$workdir" + chmod +x "$original" + if ! "$original" --appimage-extract >/dev/null; then + echo "[strip-libs] ERROR: --appimage-extract failed for $original" >&2 + exit 1 + fi + ) + + local appdir="$workdir/squashfs-root" + local removed=0 + local lib_roots=() + for candidate in \ + "$appdir/usr/lib" \ + "$appdir/usr/lib/x86_64-linux-gnu" \ + "$appdir/shared/lib" \ + "$appdir/shared/lib/x86_64-linux-gnu" \ + "$appdir/lib" \ + "$appdir/lib/x86_64-linux-gnu"; do + [ -d "$candidate" ] && lib_roots+=("$candidate") + done + + if [ "${#lib_roots[@]}" -eq 0 ]; then + echo "[strip-libs] WARNING: no known lib roots inside $original — layout changed?" >&2 + rm -rf "$workdir" + return + fi + + for root in "${lib_roots[@]}"; do + for pattern in "${EXCLUDE_PATTERNS[@]}"; do + while IFS= read -r -d '' f; do + echo "[strip-libs] removing ${f#"$appdir"/}" + rm -f "$f" + removed=$((removed + 1)) + done < <(find "$root" -maxdepth 1 -name "$pattern" -print0) + done + done + + if [ "$removed" -eq 0 ]; then + echo "[strip-libs] No graphics libs found in $original — leaving unchanged." + rm -rf "$workdir" + return + fi + echo "[strip-libs] Removed $removed file(s); repacking AppImage." + + local rebuilt="$workdir/$name" + ( + cd "$workdir" + ARCH=x86_64 "$APPIMAGETOOL_BIN" --appimage-extract-and-run \ + --no-appstream squashfs-root "$rebuilt" >/dev/null + ) + mv "$rebuilt" "$original" + rm -rf "$workdir" + STRIPPED_PATHS+=("$original") +} + +resign_artifact() { + local file="$1" + if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then + return + fi + if ! command -v cargo-tauri >/dev/null 2>&1; then + echo "[strip-libs] WARNING: cargo-tauri not on PATH; cannot re-sign $file" >&2 + return + fi + echo "[strip-libs] Re-signing $file" + rm -f "$file.sig" + cargo tauri signer sign \ + --private-key "$TAURI_SIGNING_PRIVATE_KEY" \ + --password "${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:-}" \ + "$file" >/dev/null +} + +main() { + if [ $# -lt 1 ]; then + echo "Usage: $0 [bundle-root...]" >&2 + exit 2 + fi + ensure_appimagetool + shopt -s nullglob + STRIPPED_PATHS=() + local found_any=0 + for root in "$@"; do + [ -d "$root/appimage" ] || continue + for img in "$root/appimage"/*.AppImage; do + found_any=1 + strip_one_appimage "$img" + done + done + if [ "$found_any" -eq 0 ]; then + echo "[strip-libs] No AppImages found under any provided bundle root." >&2 + return + fi + + # Re-sign each modified .AppImage and rebuild its updater tarball + sig. + # The updater tarball is just a gzipped tar of the .AppImage (Tauri convention), + # so its contents are stale the moment we mutate the AppImage. + for original in "${STRIPPED_PATHS[@]:-}"; do + [ -n "$original" ] || continue + resign_artifact "$original" + + local tar="$original.tar.gz" + if [ -e "$tar" ]; then + echo "[strip-libs] Rebuilding $(basename "$tar")" + tar -C "$(dirname "$original")" -czf "$tar" "$(basename "$original")" + resign_artifact "$tar" + fi + done +} + +main "$@"