From af0121c7fdc654606969295a9160dbcf8840e122 Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Tue, 18 Aug 2026 10:49:42 +0200 Subject: [PATCH 1/4] feat(linux): embed a WebRTC-enabled WebKitGTK in wry builds Stock WebKitGTK ships without ENABLE_WEB_RTC on most distros, so wry Linux builds cannot place calls. Bundle the portable runtime built by the webkitgtk-webrtc pipeline next to the binary instead of relying on the host. scripts/webkit mirrors scripts/cef: fetch the pinned artifact, stage it into webkit/ beside the binary, and package deb/rpm/AppImage from there. build.rs adds the matching rpath and main.rs points the helper processes and GStreamer at the bundle, all inert when it is absent. copy-libs.sh refuses a runtime built without relocatable-exec-path.patch or with a glibc floor above the host's, since both fail silently at runtime. The CI job builds and uploads wry bundles as artifacts only; CEF stays the shipped Linux path. --- .github/workflows/tauri-build.yml | 91 ++++++++++++++++++++ nfpm.webkit.yaml | 69 +++++++++++++++ scripts/webkit/copy-libs.sh | 53 ++++++++++++ scripts/webkit/fetch.sh | 49 +++++++++++ scripts/webkit/package.sh | 135 ++++++++++++++++++++++++++++++ scripts/webkit/runtime.json | 7 ++ scripts/webkit/stage.sh | 39 +++++++++ src-tauri/build.rs | 8 ++ src-tauri/src/main.rs | 30 +++++++ 9 files changed, 481 insertions(+) create mode 100644 nfpm.webkit.yaml create mode 100755 scripts/webkit/copy-libs.sh create mode 100755 scripts/webkit/fetch.sh create mode 100755 scripts/webkit/package.sh create mode 100644 scripts/webkit/runtime.json create mode 100755 scripts/webkit/stage.sh diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index 9de2f3de2d..96e72c42a0 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -741,6 +741,97 @@ jobs: retention-days: 1 include-hidden-files: true + linux-wry: + name: Build Linux x86_64 (wry) + needs: setup-release + runs-on: ubuntu-22.04 + timeout-minutes: 90 + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + env: + ARCH: x86_64 + TAG: ${{ needs.setup-release.outputs.tag }} + VERSION: ${{ needs.setup-release.outputs.version }} + VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} + SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} + MISE_ENV: tauri + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.setup-release.outputs.ref }} + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + with: + tauri: 'true' + + - name: Cache Rust build + uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: src-tauri + key: wry-x86_64 + + - name: Cache WebKit runtime + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: src-tauri/target/webkit-runtime + key: webkit-runtime-${{ hashFiles('scripts/webkit/runtime.json') }} + + - name: Stamp release version into tauri.conf.json + shell: bash + env: + UPDATER_ENDPOINT: ${{ needs.setup-release.outputs.nightly == 'true' && format('https://github.com/{0}/releases/download/nightly/latest.json', github.repository) || '' }} + run: node .github/scripts/set-tauri-version.mjs "$VERSION" "$UPDATER_ENDPOINT" + + - name: Build and package Linux bundles + shell: bash + run: | + pnpm tauri:wry build --no-bundle + ! strings src-tauri/target/release/sable | grep -F 'http://localhost:8080' + cp src-tauri/target/release/sable src-tauri/target/release/sable-updater + pnpm tauri:wry build --no-bundle --no-updater + + DISPLAY_NAME=Sable + [ "$IS_NIGHTLY" = "true" ] && DISPLAY_NAME="Sable Nightly" + mise run webkit:package "$VERSION" \ + src-tauri/target/release/sable "$DISPLAY_NAME" \ + src-tauri/target/release/sable-updater + + test -f src-tauri/target/release/webkit-pkg/stage/runtime/webkit/WEBKITGTK-LICENSE.txt + + - name: Sign the AppImage for the updater + if: ${{ env.TAURI_SIGNING_PRIVATE_KEY != '' }} + shell: bash + run: | + for f in src-tauri/target/release/bundle/appimage/Sable-*-linux-${ARCH}.AppImage; do + [ -e "$f" ] || continue + pnpm tauri signer sign "$f" + done + + - name: Upload wry bundles as workflow artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: sable-linux-wry-${{ env.ARCH }} + path: | + src-tauri/target/release/bundle/deb/Sable-*.deb + src-tauri/target/release/bundle/rpm/Sable-*.rpm + src-tauri/target/release/bundle/appimage/Sable-*.AppImage + if-no-files-found: error + retention-days: 7 + include-hidden-files: true + distribute-nightly-aur: name: Publish nightly to AUR needs: [setup-release, linux] diff --git a/nfpm.webkit.yaml b/nfpm.webkit.yaml new file mode 100644 index 0000000000..49b289c983 --- /dev/null +++ b/nfpm.webkit.yaml @@ -0,0 +1,69 @@ +# yaml-language-server: $schema=https://nfpm.goreleaser.com/schema.json +# wry flavour. The embedded WebKit runtime carries its own private libraries +# (ICU, libsoup3, GStreamer, bwrap), so the only dependencies left are the ones +# the app and the engine must share a single instance of. + +name: sable +arch: ${PKG_ARCH} +platform: linux +version: ${PKG_VERSION} +release: ${PKG_RELEASE} +version_schema: semver +section: net +maintainer: SableClient +description: Sable, a Matrix client +homepage: https://sable.moe + +contents: + - src: ${PKGROOT}/opt/sable/ + dst: /opt/sable + type: tree + expand: true + - src: ${PKGROOT}/usr/ + dst: /usr + type: tree + expand: true + - src: src-tauri/packaging/deb/apparmor/sable + dst: /etc/apparmor.d/sable + type: config + packager: deb + +overrides: + deb: + scripts: + postinstall: src-tauri/packaging/deb/postinst + preremove: src-tauri/packaging/deb/prerm + depends: + - apparmor + - libgtk-3-0 + - libglib2.0-0 + - libcairo2 + - libpango-1.0-0 + - libgdk-pixbuf-2.0-0 + - libatk1.0-0 + - libharfbuzz0b + - libfreetype6 + - libfontconfig1 + - libx11-6 + - libgl1 + - libegl1 + - libxkbcommon0 + - xdg-utils + - libayatana-appindicator3-1 + rpm: + depends: + - gtk3 + - glib2 + - cairo + - pango + - gdk-pixbuf2 + - atk + - harfbuzz + - freetype + - fontconfig + - libX11 + - mesa-libGL + - mesa-libEGL + - libxkbcommon + - xdg-utils + - libayatana-appindicator3.so.1()(64bit) diff --git a/scripts/webkit/copy-libs.sh b/scripts/webkit/copy-libs.sh new file mode 100755 index 0000000000..3c032b8404 --- /dev/null +++ b/scripts/webkit/copy-libs.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +#MISE description="Copy the WebRTC WebKitGTK runtime next to the built binary" +# Copy the portable WebKitGTK runtime next to a built binary so wry builds get +# WebRTC regardless of the host's webkit2gtk. The bundle carries its own rpaths +# and private dependencies. +# +# Usage: scripts/webkit/copy-libs.sh [debug|release] [dest-dir] +# dest-dir defaults to src-tauri/target/ (beside the built binary). +set -euo pipefail +PROFILE="${1:-debug}" +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +DEST="${2:-$ROOT/src-tauri/target/$PROFILE}" + +if [ "$(uname -m)" != x86_64 ]; then + echo "❌ the WebKit runtime is only built for x86_64 (got $(uname -m))." >&2 + exit 1 +fi + +SRC="${SABLE_WEBKIT_RUNTIME_DIR:-}" +if [ -z "$SRC" ]; then + CACHE="$ROOT/src-tauri/target/webkit-runtime" + bash "$ROOT/scripts/webkit/fetch.sh" "$CACHE" + SRC="$CACHE/runtime" +fi + +WEBKIT_LIB="$SRC/libwebkit2gtk-4.1.so.0" +[ -f "$WEBKIT_LIB" ] || { + echo "❌ libwebkit2gtk-4.1.so.0 not found in $SRC." >&2 + exit 1 +} + +# Without the patch the library silently forks the host's helpers instead. +# Process substitution, not a pipe: grep -q exits early and SIGPIPEs strings, +# which trips pipefail. +if ! grep -qx WEBKIT_EXEC_PATH < <(strings -a "$WEBKIT_LIB"); then + echo "❌ $WEBKIT_LIB was built without relocatable-exec-path.patch." >&2 + echo " Re-pin scripts/webkit/runtime.json at a patched build." >&2 + exit 1 +fi + +NEED="$(node -e 'process.stdout.write(require(process.argv[1]).glibc_floor)' \ + "$SRC/runtime.json" 2>/dev/null || echo 0)" +HAVE="$(ldd --version | sed -n '1s/.*[^0-9]\([0-9]\+\.[0-9]\+\)$/\1/p')" +if [ "$(printf '%s\n%s\n' "$NEED" "$HAVE" | sort -V | tail -1)" != "$HAVE" ]; then + echo "❌ runtime needs glibc $NEED, this host has $HAVE." >&2 + exit 1 +fi + +rm -rf "$DEST/webkit" +mkdir -p "$DEST" +cp -a "$SRC" "$DEST/webkit" + +echo "✅ WebKit runtime staged in $DEST/webkit (glibc floor $NEED)" diff --git a/scripts/webkit/fetch.sh b/scripts/webkit/fetch.sh new file mode 100755 index 0000000000..a6291ba13b --- /dev/null +++ b/scripts/webkit/fetch.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +#MISE description="Download the WebRTC-enabled WebKitGTK runtime" +# Downloads the pinned webkitgtk-webrtc portable artifact into a cache dir. +# +# Usage: scripts/webkit/fetch.sh [cache-dir] +# cache-dir defaults to src-tauri/target/webkit-runtime. +# +# Override the source with SABLE_WEBKIT_ARTIFACT_URL, or point +# SABLE_WEBKIT_RUNTIME_DIR at an already-unpacked tree to skip this entirely. +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +CACHE="${1:-$ROOT/src-tauri/target/webkit-runtime}" +MANIFEST="$ROOT/scripts/webkit/runtime.json" + +read -r REPO RUN ARTIFACT VERSION < <( + node -e ' + const m = require(process.argv[1]); + process.stdout.write([m.repository, m.run, m.artifact, m.version].join(" ")); + ' "$MANIFEST" +) + +URL="${SABLE_WEBKIT_ARTIFACT_URL:-$REPO/actions/runs/$RUN/artifacts/$ARTIFACT}" +STAMP="$CACHE/.stamp" +WANT="$URL $VERSION" + +if [ -f "$STAMP" ] && [ "$(cat "$STAMP")" = "$WANT" ]; then + echo "→ WebKit runtime already cached in $CACHE" + exit 0 +fi + +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +echo "→ downloading $URL" +curl -fL --retry 3 --progress-bar -o "$WORK/artifact.zip" "$URL" +unzip -q -j "$WORK/artifact.zip" -d "$WORK" + +TARBALL="$(find "$WORK" -maxdepth 1 -name '*-portable-*.tar.zst' -print -quit)" +[ -n "$TARBALL" ] || { + echo "❌ no portable runtime tarball in the artifact." >&2 + exit 1 +} + +rm -rf "$CACHE" +mkdir -p "$CACHE" +tar --zstd -xf "$TARBALL" -C "$CACHE" + +echo "$WANT" > "$STAMP" +echo "✅ WebKit runtime $VERSION unpacked into $CACHE" diff --git a/scripts/webkit/package.sh b/scripts/webkit/package.sh new file mode 100755 index 0000000000..dfffd16794 --- /dev/null +++ b/scripts/webkit/package.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +#MISE description="Package the wry build with the embedded WebKit runtime" +#MISE tools={nfpm="2.47.0", "github:AppImage/appimagetool" = {version = "1.9.1", matching = ".AppImage"}} +# Usage: scripts/webkit/package.sh [version] [package-binary-path] [display-name] [appimage-binary-path] +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +VERSION="${1:-$(grep -m1 '"version":' src-tauri/tauri.conf.json | sed 's/.*: *"\(.*\)".*/\1/')}" +: "${VERSION:?version not found in src-tauri/tauri.conf.json}" +DEB_VERSION="$VERSION" +RPM_VERSION="$VERSION" +RPM_ITERATION=1 +if [[ "$VERSION" == *-* ]]; then + BASE_VERSION="${VERSION%%-*}" + PRERELEASE="${VERSION#*-}" + DEB_VERSION="${BASE_VERSION}~${PRERELEASE}" + RPM_VERSION="$BASE_VERSION" + RPM_ITERATION="0.${PRERELEASE}" +fi + +# The WebKit runtime is x86_64 only. +case "$(uname -m)" in + x86_64) export ARCH=x86_64; NFPM_ARCH=amd64 ;; + *) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;; +esac + +STAGE="$ROOT/src-tauri/target/release" +OUT="$STAGE/bundle" +WORK="$STAGE/webkit-pkg" + +BIN_PATH="${2:-}" +DISPLAY_NAME="${3:-}" +APPIMAGE_BIN_PATH="${4:-}" +if [ -z "$BIN_PATH" ]; then + for candidate in "$STAGE/Sable Nightly" "$STAGE/Sable" "$STAGE/sable" \ + "$ROOT/src-tauri/target/$ARCH-unknown-linux-gnu/release/sable"; do + [ -x "$candidate" ] || continue + BIN_PATH="$candidate" + break + done +fi +[ -n "$BIN_PATH" ] && [ -x "$BIN_PATH" ] || { + echo "no wry binary found; build it first (pnpm tauri:wry build --no-bundle)" >&2 + exit 1 +} +[ -n "$APPIMAGE_BIN_PATH" ] || APPIMAGE_BIN_PATH="$BIN_PATH" +[ -x "$APPIMAGE_BIN_PATH" ] || { + echo "AppImage binary is not executable: $APPIMAGE_BIN_PATH" >&2 + exit 1 +} +if [ -z "$DISPLAY_NAME" ]; then + case "$(basename "$BIN_PATH")" in + "Sable Nightly") DISPLAY_NAME="Sable Nightly" ;; + *) DISPLAY_NAME="Sable" ;; + esac +fi + +APPIMAGETOOL_CMD="" +if command -v appimagetool.AppImage >/dev/null 2>&1; then + APPIMAGETOOL_CMD="appimagetool.AppImage" +elif command -v appimagetool >/dev/null 2>&1; then + APPIMAGETOOL_CMD="appimagetool" +else + echo "appimagetool not found" >&2 + exit 1 +fi + +rm -rf "$WORK" +mkdir -p "$OUT/deb" "$OUT/rpm" "$OUT/appimage" + +bash scripts/webkit/stage.sh "$WORK/stage" "$DISPLAY_NAME" + +APPDIR="$WORK/Sable.AppDir" +mkdir -p "$APPDIR/usr/bin" +cp -a "$WORK/stage/runtime/." "$APPDIR/usr/bin/" +cp -f "$APPIMAGE_BIN_PATH" "$APPDIR/usr/bin/sable" +chmod 755 "$APPDIR/usr/bin/sable" + +# Bundle libayatana-appindicator3 and its closure for the tray, as CEF does. +stage_appindicator() { + local dest="$1" main dep + main="$(ldconfig -p 2>/dev/null | awk '$1=="libayatana-appindicator3.so.1"{v=$NF} END{print v}')" + [ -n "$main" ] || main="$(find /usr/lib /usr/lib64 /lib -name libayatana-appindicator3.so.1 2>/dev/null | sort | tail -n1)" + if [ -z "$main" ] || [ ! -e "$main" ]; then + echo "warning: libayatana-appindicator3.so.1 not found; tray disabled in the AppImage" >&2 + return 0 + fi + { + echo "$main" + ldd "$main" 2>/dev/null | awk '/=>/ {print $3}' | grep -iE 'ayatana|dbusmenu|indicator|ido' || true + } | sort -u | while read -r dep; do + if [ -e "$dep" ]; then + cp -Lf "$dep" "$dest/$(basename "$dep")" + fi + done +} +stage_appindicator "$APPDIR/usr/bin" + +if command -v nfpm >/dev/null 2>&1; then + PKGROOT="$WORK/pkgroot" + mkdir -p "$PKGROOT/opt/sable" "$PKGROOT/usr/bin" "$PKGROOT/usr/share/applications" + cp -a "$WORK/stage/runtime/." "$PKGROOT/opt/sable/" + cp -f "$BIN_PATH" "$PKGROOT/opt/sable/sable" + chmod 755 "$PKGROOT/opt/sable/sable" + cat > "$PKGROOT/usr/bin/sable" <<'EOF' +#!/bin/sh +exec /opt/sable/sable "$@" +EOF + chmod 755 "$PKGROOT/usr/bin/sable" + cp -a "$WORK/stage/share/." "$PKGROOT/usr/share/" + + PKGROOT="$PKGROOT" PKG_ARCH="$NFPM_ARCH" PKG_VERSION="$DEB_VERSION" PKG_RELEASE=1 nfpm pkg -f nfpm.webkit.yaml -p deb \ + -t "$OUT/deb/Sable-${VERSION}-linux-${ARCH}.deb" + PKGROOT="$PKGROOT" PKG_ARCH="$NFPM_ARCH" PKG_VERSION="$RPM_VERSION" PKG_RELEASE="$RPM_ITERATION" nfpm pkg -f nfpm.webkit.yaml -p rpm \ + -t "$OUT/rpm/Sable-${VERSION}-linux-${ARCH}.rpm" +else + echo "nfpm not found" >&2 + exit 1 +fi + +cp -f "$WORK/stage/share/applications/sable.desktop" "$APPDIR/sable.desktop" +cp -f src-tauri/icons/128x128.png "$APPDIR/sable.png" +cat > "$APPDIR/AppRun" <<'EOF' +#!/bin/sh +HERE="$(dirname "$(readlink -f "$0")")" +export LD_LIBRARY_PATH="$HERE/usr/bin${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +exec "$HERE/usr/bin/sable" "$@" +EOF +chmod 755 "$APPDIR/AppRun" + +APPIMAGE_EXTRACT_AND_RUN=1 "$APPIMAGETOOL_CMD" "$APPDIR" \ + "$OUT/appimage/Sable-${VERSION}-linux-${ARCH}.AppImage" + +echo "Packages in: $OUT" diff --git a/scripts/webkit/runtime.json b/scripts/webkit/runtime.json new file mode 100644 index 0000000000..f0346ba9d9 --- /dev/null +++ b/scripts/webkit/runtime.json @@ -0,0 +1,7 @@ +{ + "$comment": "Portable WebKitGTK runtime embedded in wry Linux builds. Built by the webkitgtk-webrtc portable pipeline (glibc 2.35 floor, relocatable exec path). Re-pin `run` after each rebuild.", + "repository": "https://git.erwanleboucher.dev/eleboucher/webkitgtk-webrtc", + "run": 13051, + "artifact": "webkit2gtk-4.1-portable", + "version": "2.52.5" +} diff --git a/scripts/webkit/stage.sh b/scripts/webkit/stage.sh new file mode 100755 index 0000000000..f68cf381b9 --- /dev/null +++ b/scripts/webkit/stage.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +#MISE description="Stage the WebKit runtime and desktop files for packaging" +# Usage: scripts/webkit/stage.sh [stage-dir] [display-name] [profile] +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +STAGE="${1:-$ROOT/src-tauri/target/webkit-stage}" +DISPLAY_NAME="${2:-Sable}" +PROFILE="${3:-release}" + +RUNTIME="$STAGE/runtime" +SHARE="$STAGE/share" +rm -rf "$STAGE" +mkdir -p "$RUNTIME" "$SHARE/applications" + +bash scripts/webkit/copy-libs.sh "$PROFILE" "$RUNTIME" + +cat > "$SHARE/applications/sable.desktop" < Date: Tue, 18 Aug 2026 14:34:09 +0200 Subject: [PATCH 2/4] fix(webkit): pin the built runtime and fix the manifest read Points at run 13100, the first portable build carrying the relocation patch. fetch.sh could never have worked: node wrote the manifest fields without a trailing newline, so read returned 1 at EOF and set -e killed the script before it printed anything. Every earlier test had bypassed fetch.sh via SABLE_WEBKIT_RUNTIME_DIR. --- scripts/webkit/fetch.sh | 2 +- scripts/webkit/runtime.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/webkit/fetch.sh b/scripts/webkit/fetch.sh index a6291ba13b..af9a524117 100755 --- a/scripts/webkit/fetch.sh +++ b/scripts/webkit/fetch.sh @@ -15,7 +15,7 @@ MANIFEST="$ROOT/scripts/webkit/runtime.json" read -r REPO RUN ARTIFACT VERSION < <( node -e ' const m = require(process.argv[1]); - process.stdout.write([m.repository, m.run, m.artifact, m.version].join(" ")); + process.stdout.write([m.repository, m.run, m.artifact, m.version].join(" ") + "\n"); ' "$MANIFEST" ) diff --git a/scripts/webkit/runtime.json b/scripts/webkit/runtime.json index f0346ba9d9..7c84b78145 100644 --- a/scripts/webkit/runtime.json +++ b/scripts/webkit/runtime.json @@ -1,7 +1,7 @@ { "$comment": "Portable WebKitGTK runtime embedded in wry Linux builds. Built by the webkitgtk-webrtc portable pipeline (glibc 2.35 floor, relocatable exec path). Re-pin `run` after each rebuild.", "repository": "https://git.erwanleboucher.dev/eleboucher/webkitgtk-webrtc", - "run": 13051, + "run": 13100, "artifact": "webkit2gtk-4.1-portable", "version": "2.52.5" } From c9dc4ccde6111f8331e9f61f724b4b4a4cb282ab Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Tue, 18 Aug 2026 15:26:53 +0200 Subject: [PATCH 3/4] fix(webkit): keep the host's GStreamer plugins out of the bundled runtime Setting only GST_PLUGIN_SYSTEM_PATH_1_0 left GST_PLUGIN_PATH_1_0 unset, so the host-probing block below filled it with /usr/lib/gstreamer-1.0. Those plugins then loaded against the bundle's older GStreamer core and failed on missing symbols, and one pulled in libsoup2 alongside WebKit's libsoup3, aborting the web process and leaving the window blank. Also drops the WebKit runtime cache step. CodeQL flagged it as cache poisoning reachable from workflow_dispatch, and caching a binary runtime we then ship is exactly the wrong thing to key on a file hash. The artifact is a 131 MB download that takes seconds. --- .github/workflows/tauri-build.yml | 6 ------ scripts/webkit/runtime.json | 2 +- src-tauri/src/main.rs | 13 +++++++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index 96e72c42a0..9df65cd757 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -783,12 +783,6 @@ jobs: workspaces: src-tauri key: wry-x86_64 - - name: Cache WebKit runtime - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: src-tauri/target/webkit-runtime - key: webkit-runtime-${{ hashFiles('scripts/webkit/runtime.json') }} - - name: Stamp release version into tauri.conf.json shell: bash env: diff --git a/scripts/webkit/runtime.json b/scripts/webkit/runtime.json index 7c84b78145..2111dec2cd 100644 --- a/scripts/webkit/runtime.json +++ b/scripts/webkit/runtime.json @@ -1,5 +1,5 @@ { - "$comment": "Portable WebKitGTK runtime embedded in wry Linux builds. Built by the webkitgtk-webrtc portable pipeline (glibc 2.35 floor, relocatable exec path). Re-pin `run` after each rebuild.", + "$comment": "Portable WebKitGTK runtime embedded in wry Linux builds. Built by the webkitgtk-webrtc portable pipeline (glibc 2.39 floor, relocatable exec path). Re-pin `run` after each rebuild.", "repository": "https://git.erwanleboucher.dev/eleboucher/webkitgtk-webrtc", "run": 13100, "artifact": "webkit2gtk-4.1-portable", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1eabf0b7a5..37f71f1e8d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -252,16 +252,21 @@ fn main() { ); } - // The bundle ships its own GStreamer; mixing it with the host's - // plugins loads two libgstreamer cores. + // Both paths, or the host-probing block below fills the one left + // unset and its plugins load against our older GStreamer core. let gst = runtime.join("gstreamer-1.0"); if gst.is_dir() { - if std::env::var_os("GST_PLUGIN_SYSTEM_PATH_1_0").is_none() { - std::env::set_var("GST_PLUGIN_SYSTEM_PATH_1_0", &gst); + for key in ["GST_PLUGIN_SYSTEM_PATH_1_0", "GST_PLUGIN_PATH_1_0"] { + if std::env::var_os(key).is_none() { + std::env::set_var(key, &gst); + } } if std::env::var_os("GST_PLUGIN_SCANNER").is_none() { std::env::set_var("GST_PLUGIN_SCANNER", gst.join("gst-plugin-scanner")); } + if std::env::var_os("GST_REGISTRY_1_0").is_none() { + std::env::set_var("GST_REGISTRY_1_0", runtime.join("gst-registry.bin")); + } } } From bdfa905721f29ab7b5710d5cb46ed224fdff5311 Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Tue, 18 Aug 2026 18:32:17 +0200 Subject: [PATCH 4/4] fix(webkit): declare the host libraries the runtime actually needs Derived by auditing the bundled runtime inside Ubuntu, Debian and Fedora containers with a desktop package set, rather than assuming. The engine needs libXtst, libXScrnSaver, libatomic, harfbuzz-icu, nghttp2, rsvg, psl, tasn1, wayland-client/egl and xcb from the host, and none of them were declared. Also re-pins the runtime at run 13112. --- nfpm.webkit.yaml | 22 ++++++++++++++++++++++ scripts/webkit/runtime.json | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/nfpm.webkit.yaml b/nfpm.webkit.yaml index 49b289c983..a245807527 100644 --- a/nfpm.webkit.yaml +++ b/nfpm.webkit.yaml @@ -48,6 +48,17 @@ overrides: - libgl1 - libegl1 - libxkbcommon0 + - libxtst6 + - libxss1 + - libatomic1 + - libharfbuzz-icu0 + - libnghttp2-14 + - librsvg2-2 + - libpsl5 + - libtasn1-6 + - libwayland-client0 + - libwayland-egl1 + - libxcb1 - xdg-utils - libayatana-appindicator3-1 rpm: @@ -65,5 +76,16 @@ overrides: - mesa-libGL - mesa-libEGL - libxkbcommon + - libXtst + - libXScrnSaver + - libatomic + - harfbuzz-icu + - libnghttp2 + - librsvg2 + - libpsl + - libtasn1 + - libwayland-client + - libwayland-egl + - libxcb - xdg-utils - libayatana-appindicator3.so.1()(64bit) diff --git a/scripts/webkit/runtime.json b/scripts/webkit/runtime.json index 2111dec2cd..e20b52f6a4 100644 --- a/scripts/webkit/runtime.json +++ b/scripts/webkit/runtime.json @@ -1,7 +1,7 @@ { "$comment": "Portable WebKitGTK runtime embedded in wry Linux builds. Built by the webkitgtk-webrtc portable pipeline (glibc 2.39 floor, relocatable exec path). Re-pin `run` after each rebuild.", "repository": "https://git.erwanleboucher.dev/eleboucher/webkitgtk-webrtc", - "run": 13100, + "run": 13112, "artifact": "webkit2gtk-4.1-portable", "version": "2.52.5" }