From 539577ae7746c1ad50886c97a373f71ddb356722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Wed, 7 Oct 2026 12:57:36 +0200 Subject: [PATCH 1/3] fix(release): don't bundle libwayland into the Tauri AppImage, and gate it A libwayland copied from the Ubuntu build image clashes with the host's newer Mesa: crashes or a blank webview on e.g. Arch/KDE Wayland (tauri-apps/tauri#15665, tauri-apps/tauri#15976). Set LINUXDEPLOY_EXCLUDED_LIBRARIES=libwayland-* for the Tauri builds (linuxdeploy honors it from @tauri-apps/cli 2.12, tauri-apps/tauri#16062), and run the host-ABI gate on the Tauri AppImage. host-abi-gate.sh gains HOST_ABI_LIBS_LIST so this bundle can keep glib, which its GTK/WebKitGTK needs. --- .github/workflows/release.yml | 14 ++++++++++++++ scripts/package/host-abi-gate.sh | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d861d7d13..2c0ede08b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1258,6 +1258,12 @@ jobs: run: | echo "RELEASE=${{ startsWith(github.ref_name, 'v') || github.ref_name == 'master' }}" >> "$GITHUB_ENV" echo "TAURI_BUILD=true" >> "$GITHUB_ENV" + # Don't bundle libwayland into the Tauri AppImage: a copy from the + # Ubuntu build image clashes with the host's newer Mesa (crashes or a + # blank webview on e.g. Arch/KDE Wayland; tauri-apps/tauri#15665, + # tauri-apps/tauri#15976). Read by linuxdeploy, which Tauri's bundler + # runs; honored from @tauri-apps/cli 2.12 (tauri-apps/tauri#16062). + echo "LINUXDEPLOY_EXCLUDED_LIBRARIES=libwayland-*" >> "$GITHUB_ENV" - name: Set tag metadata if: startsWith(github.ref, 'refs/tags/v') @@ -1550,6 +1556,14 @@ jobs: esac done + - name: Host-ABI gate — verify the Tauri AppImage + if: runner.os == 'Linux' + # Only libwayland for now: the AppImage's bundled GTK 3 / WebKitGTK + # (Ubuntu 24.04) needs its glib, so glib stays bundled here. + env: + HOST_ABI_LIBS_LIST: libwayland-client libwayland-cursor libwayland-egl libwayland-server + run: bash scripts/package/host-abi-gate.sh dist/activitywatch-tauri*-linux-*.AppImage + - name: Package dmg if: runner.os == 'macOS' run: | diff --git a/scripts/package/host-abi-gate.sh b/scripts/package/host-abi-gate.sh index c8d8cb57c..af545a7f6 100755 --- a/scripts/package/host-abi-gate.sh +++ b/scripts/package/host-abi-gate.sh @@ -26,6 +26,12 @@ HOST_ABI_LIBS=( libglib-2.0 libgobject-2.0 ) +# HOST_ABI_LIBS_LIST (space-separated) replaces the list, e.g. for a bundle that +# must keep glib (the Tauri AppImage's GTK/WebKitGTK needs the glib it was built +# against). +if [ -n "${HOST_ABI_LIBS_LIST:-}" ]; then + read -r -a HOST_ABI_LIBS <<< "$HOST_ABI_LIBS_LIST" +fi if [ "$#" -eq 0 ]; then echo "Usage: $0 [...]" >&2 From 585ca9581078d41380458b2c028a26ed202359a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Wed, 7 Oct 2026 13:08:40 +0200 Subject: [PATCH 2/3] test(package): cover host-abi-gate.sh's default list and HOST_ABI_LIBS_LIST override --- .github/workflows/release.yml | 3 ++ scripts/tests/test_host_abi_gate.py | 61 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 scripts/tests/test_host_abi_gate.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c0ede08b..bbb304b5a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -463,6 +463,9 @@ jobs: - name: Test updater channel isolation and release versions run: python3 -m pytest scripts/tests/test_generate_latest_json.py scripts/tests/test_configure_tauri_release.py -q + - name: Test host-ABI gate + run: python3 -m pytest scripts/tests/test_host_abi_gate.py -q + build-qt: name: Build Qt artifacts (${{ matrix.os }}${{ (matrix.research || (github.event_name == 'workflow_dispatch' && inputs.edition == 'research') || endsWith(github.ref_name, '-research')) && ', research' || '' }})${{ needs.nightly-submodules.outputs.latest == 'true' && ' [latest submodules]' || '' }} needs: nightly-submodules diff --git a/scripts/tests/test_host_abi_gate.py b/scripts/tests/test_host_abi_gate.py new file mode 100644 index 000000000..5c9d480ce --- /dev/null +++ b/scripts/tests/test_host_abi_gate.py @@ -0,0 +1,61 @@ +"""Tests for scripts/package/host-abi-gate.sh on fake bundle trees.""" + +import os +import subprocess +from pathlib import Path +from typing import Optional + +import pytest + +GATE = Path(__file__).parents[1] / "package" / "host-abi-gate.sh" +WAYLAND_ONLY = "libwayland-client libwayland-cursor libwayland-egl libwayland-server" + + +def run_gate(bundle: Path, libs_list: Optional[str] = None) -> int: + env = {k: v for k, v in os.environ.items() if k != "HOST_ABI_LIBS_LIST"} + if libs_list is not None: + env["HOST_ABI_LIBS_LIST"] = libs_list + return subprocess.run( + ["bash", str(GATE), str(bundle)], env=env, capture_output=True + ).returncode + + +def make_bundle(tmp_path: Path, *libs: str) -> Path: + lib_dir = tmp_path / "bundle" / "usr" / "lib" + lib_dir.mkdir(parents=True) + (lib_dir / "libgtk-3.so.0").touch() # never flagged + for name in libs: + (lib_dir / name).touch() + return tmp_path / "bundle" + + +def test_clean_bundle_passes(tmp_path: Path): + assert run_gate(make_bundle(tmp_path)) == 0 + + +@pytest.mark.parametrize("lib", ["libglib-2.0.so.0", "libgio-2.0.so.0", "libwayland-client.so.0"]) +def test_default_list_rejects_host_abi_libs(tmp_path: Path, lib: str): + assert run_gate(make_bundle(tmp_path, lib)) == 1 + + +def test_override_rejects_wayland(tmp_path: Path): + bundle = make_bundle(tmp_path, "libglib-2.0.so.0", "libwayland-egl.so.1") + assert run_gate(bundle, WAYLAND_ONLY) == 1 + + +def test_override_replaces_rather_than_extends_default(tmp_path: Path): + # The Tauri AppImage keeps glib on purpose: with the wayland-only list, + # glib must pass, while the default list still rejects it. + bundle = make_bundle(tmp_path, "libglib-2.0.so.0", "libgobject-2.0.so.0") + assert run_gate(bundle, WAYLAND_ONLY) == 0 + assert run_gate(bundle) == 1 + + +def test_empty_override_keeps_default(tmp_path: Path): + assert run_gate(make_bundle(tmp_path, "libglib-2.0.so.0"), "") == 1 + + +def test_symlinked_name_is_flagged(tmp_path: Path): + bundle = make_bundle(tmp_path, "libwayland-client.so.0.22.0") + (bundle / "usr" / "lib" / "libwayland-client.so.0").symlink_to("libwayland-client.so.0.22.0") + assert run_gate(bundle, WAYLAND_ONLY) == 1 From 0fadc0400067e55d474bd0056b0b4c5d0ea87e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Wed, 7 Oct 2026 13:39:24 +0200 Subject: [PATCH 3/3] test(package): make the symlink case depend on the gate checking symlinks --- scripts/tests/test_host_abi_gate.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/tests/test_host_abi_gate.py b/scripts/tests/test_host_abi_gate.py index 5c9d480ce..5687eda1e 100644 --- a/scripts/tests/test_host_abi_gate.py +++ b/scripts/tests/test_host_abi_gate.py @@ -56,6 +56,8 @@ def test_empty_override_keeps_default(tmp_path: Path): def test_symlinked_name_is_flagged(tmp_path: Path): - bundle = make_bundle(tmp_path, "libwayland-client.so.0.22.0") - (bundle / "usr" / "lib" / "libwayland-client.so.0").symlink_to("libwayland-client.so.0.22.0") + # The forbidden name exists only as a link to a harmless file, so this + # fails if the gate stops checking symlinks. + bundle = make_bundle(tmp_path) + (bundle / "usr" / "lib" / "libwayland-client.so.0").symlink_to("libgtk-3.so.0") assert run_gate(bundle, WAYLAND_ONLY) == 1