diff --git a/.forgejo/workflows/desktop.yml b/.forgejo/workflows/desktop.yml index bd6c862..41beadc 100644 --- a/.forgejo/workflows/desktop.yml +++ b/.forgejo/workflows/desktop.yml @@ -62,6 +62,15 @@ jobs: run: cargo tauri build --config '{"build":{"beforeBuildCommand":""}}' working-directory: desktop/src-tauri + # Tauri's AppImage bundles the build host's graphics/display libs + # (libEGL/libGL/libdrm/libgbm/libwayland-*), which clash with end-user GPU + # drivers and abort to a black window (EGL_BAD_PARAMETER, issue 2021). + # Strip that host-coupled stack so the app uses the running system's + # graphics libs; webkit/gtk stay bundled. Runs from the repo root (the + # script resolves its own paths), overwriting the AppImage in place. + - name: De-bundle AppImage graphics libraries + run: bash desktop/packaging/appimage/debundle-graphics.sh + # Make the built .deb + .AppImage downloadable from the run (for hand-testing). # continue-on-error: the Forgejo artifact backend may not be configured yet; a # failed upload must not fail the build itself. diff --git a/desktop/packaging/appimage/debundle-graphics.sh b/desktop/packaging/appimage/debundle-graphics.sh new file mode 100755 index 0000000..8ad247f --- /dev/null +++ b/desktop/packaging/appimage/debundle-graphics.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# +# De-bundle host-coupled graphics/display libraries from the built Tauri AppImage. +# +# WHY: Tauri's AppImage bundles the BUILD host's libEGL/libGL/libdrm/libgbm/ +# libwayland-* into usr/lib. On many end-user systems (Arch/CachyOS, NVIDIA, +# Wayland) those bundled copies clash with the running kernel driver + Mesa and +# abort with "Could not create default EGL display: EGL_BAD_PARAMETER" -> a black +# window (issue 2021). They load before any renderer choice, so the runtime env +# fallbacks (WEBKIT_DISABLE_DMABUF_RENDERER etc.) cannot rescue it. Per the +# AppImage excludelist these libraries MUST come from the host. We strip exactly +# that graphics/display subset -- keeping webkit/gtk/javascriptcore bundled for +# portability -- so the dynamic loader falls through to the system copies (the +# AppRun keeps usr/lib first on LD_LIBRARY_PATH, then system paths). +# +# CI-only post-build step: extract -> remove libs -> repackage, overwriting the +# original AppImage in place so the desktop.yml artifact glob emits the +# de-bundled one. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +BUNDLE_DIR="$REPO_ROOT/desktop/src-tauri/target/release/bundle/appimage" + +# appimagetool is published only under the rolling "continuous" tag (the project +# cuts no semver releases), so this URL is the pinned distribution channel. +# Preferred source is Tauri's own already-downloaded copy (see below); this is +# the network fallback. +APPIMAGETOOL_URL="https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" + +# Graphics/display libraries that must come from the host, per the AppImage +# excludelist (github.com/AppImage/pkg2appimage/blob/master/excludelist), plus +# the libwayland-* family that slips in through the GTK/WebKit dependency chain. +# Globs (.so*) catch every versioned variant. Only the GPU/driver stack is +# stripped; webkit/gtk stay bundled. +REMOVE_GLOBS=( + 'libGL.so*' 'libEGL.so*' 'libGLdispatch.so*' 'libGLX.so*' 'libOpenGL.so*' + 'libglapi.so*' 'libgbm.so*' 'libdrm.so*' + 'libxcb.so*' 'libxcb-dri2.so*' 'libxcb-dri3.so*' + 'libX11.so*' 'libX11-xcb.so*' + 'libwayland-client.so*' 'libwayland-cursor.so*' + 'libwayland-egl.so*' 'libwayland-server.so*' +) + +echo "==> Locating built AppImage under $BUNDLE_DIR" +shopt -s nullglob +appimages=("$BUNDLE_DIR"/*.AppImage) +if [ ${#appimages[@]} -eq 0 ]; then + echo "ERROR: no .AppImage in $BUNDLE_DIR (did the tauri build run?)" >&2 + exit 1 +fi +APPIMAGE="${appimages[0]}" +echo " $APPIMAGE" + +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +echo "==> Extracting AppImage (FUSE-free)" +cp "$APPIMAGE" "$WORK/app.AppImage" +chmod +x "$WORK/app.AppImage" +(cd "$WORK" && ./app.AppImage --appimage-extract >/dev/null) +LIBDIR="$WORK/squashfs-root/usr/lib" +[ -d "$LIBDIR" ] || { + echo "ERROR: no usr/lib in the extracted AppImage" >&2 + exit 1 +} + +echo "==> Removing host-coupled graphics/display libraries" +before=$(find "$LIBDIR" -maxdepth 1 -type f -name '*.so*' | wc -l) +removed=0 +for pat in "${REMOVE_GLOBS[@]}"; do + for f in "$LIBDIR"/$pat; do + echo " - $(basename "$f")" + rm -f "$f" + removed=$((removed + 1)) + done +done +if [ -d "$LIBDIR/dri" ]; then + n=$(find "$LIBDIR/dri" -type f | wc -l) + echo " - dri/ (${n} Mesa GPU drivers)" + rm -rf "$LIBDIR/dri" + removed=$((removed + 1)) +fi +after=$(find "$LIBDIR" -maxdepth 1 -type f -name '*.so*' | wc -l) +echo " removed ${removed} entries (usr/lib .so files: ${before} -> ${after})" +if [ "$removed" -eq 0 ]; then + echo "WARNING: nothing removed -- excludelist libs were not bundled here." >&2 +fi + +echo "==> Locating appimagetool" +# Prefer the copy Tauri already downloaded during the build (no network, and +# version-matched to the toolchain that produced the AppImage). +APPIMAGETOOL="$(find "$REPO_ROOT/desktop/src-tauri/target" -name 'appimagetool-*.AppImage' -type f 2>/dev/null | head -n1 || true)" +if [ -z "${APPIMAGETOOL:-}" ]; then + echo " not cached by Tauri; downloading from continuous channel" + APPIMAGETOOL="$WORK/appimagetool" + if command -v curl >/dev/null 2>&1; then + curl -fsSL -o "$APPIMAGETOOL" "$APPIMAGETOOL_URL" + elif command -v wget >/dev/null 2>&1; then + wget -qO "$APPIMAGETOOL" "$APPIMAGETOOL_URL" + else + echo "ERROR: need curl or wget to fetch appimagetool" >&2 + exit 1 + fi +else + echo " reusing Tauri's copy: $APPIMAGETOOL" +fi +chmod +x "$APPIMAGETOOL" + +echo "==> Repackaging AppImage (overwriting original)" +rm -f "$APPIMAGE" +# APPIMAGE_EXTRACT_AND_RUN lets appimagetool (itself an AppImage) run without +# FUSE in the CI container; ARCH is required by the embedded runtime. +export APPIMAGE_EXTRACT_AND_RUN=1 +ARCH=x86_64 "$APPIMAGETOOL" "$WORK/squashfs-root" "$APPIMAGE" + +echo "==> Done: de-bundled AppImage at" +ls -lh "$APPIMAGE"