From 7f435ffed26a0acab20297e71b781d8aa3d5235d Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Mon, 6 Jul 2026 23:21:19 -0400 Subject: [PATCH 1/2] fix(linux): make AppImage work on Mesa 25+ / GLib 2.88 distros Vanilla tauri-bundler (no bundle.linux config) ships libwayland-client, libglib/gio/gobject, all libgst*.so*, and other infra libs into the AppImage. On newer distros (Ubuntu 26.04, Mesa 25+), the bundled versions clash with the system: libwayland-client 1.22 causes eglGetDisplay to return EGL_BAD_PARAMETER under Wayland (WebKitWebProcess aborts before rendering), and the empty bundled gstreamer-1.0 dir disables GStreamer plugin discovery. No tauri.conf.json knob exists to remove or symlink bundled libs; the bundle.linux.appimage surface only exposes bundleMediaFramework/files/bundleXdgOpen. Add desktop/scripts/fix-appimage.sh to post-process the AppImage: extracts it, removes the conflicting infra libs, symlinks the system GStreamer plugin dir, repacks with appimagetool, and re-signs if TAURI_SIGNING_PRIVATE_KEY is present (handling both .AppImage.sig and .AppImage.tar.gz.sig updater artifact formats). Wire it into release.yml after the tauri build step, and switch the job to container: ubuntu:22.04 so linuxdeploy links against the oldest-supported GLIBC (per tauri docs; avoids the ubuntu-22.04 runner deprecation starting Sept 2026). --- .github/workflows/release.yml | 90 ++++++++++++++++++---- desktop/scripts/fix-appimage.sh | 130 ++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 15 deletions(-) create mode 100755 desktop/scripts/fix-appimage.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f07fde754..b0bb51c46 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -445,42 +445,41 @@ jobs: name: Release Linux if: github.repository == 'block/buzz' runs-on: ubuntu-latest + container: ubuntu:22.04 needs: setup timeout-minutes: 60 permissions: contents: write + env: + # AppImage tools (linuxdeploy, appimagetool) are themselves AppImages. + # Containers lack FUSE, so we must use the extract-and-run fallback. + APPIMAGE_EXTRACT_AND_RUN: "1" outputs: archive_name: ${{ steps.linux-artifacts.outputs.archive_name }} sig: ${{ steps.read-sig.outputs.sig }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - with: - ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }} - persist-credentials: false - - - uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1 - - - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 - with: - workspaces: desktop/src-tauri - lookup-only: true - - - name: Install Tauri dependencies (Linux) + - name: Install system dependencies env: DEBIAN_FRONTEND: noninteractive run: | - sudo apt-get update \ + # Must run first: bare ubuntu:22.04 ships without curl, wget, git, or + # ca-certificates. activate-hermit bootstraps via curl+HTTPS (needs + # both), and actions/checkout falls back to a REST tarball without git. + # Running as root — no sudo needed. + apt-get update \ -o Acquire::Retries=3 \ -o Acquire::http::Timeout=30 \ -o Acquire::https::Timeout=30 - sudo apt-get install -y --no-install-recommends \ + apt-get install -y --no-install-recommends \ -o Acquire::Retries=3 \ -o Acquire::http::Timeout=30 \ -o Acquire::https::Timeout=30 \ -o DPkg::Lock::Timeout=120 \ build-essential \ + ca-certificates \ curl \ file \ + git \ libasound2-dev \ libayatana-appindicator3-dev \ libgtk-3-dev \ @@ -489,7 +488,52 @@ jobs: libwebkit2gtk-4.1-dev \ libxdo-dev \ patchelf \ + pkg-config \ wget + # Install GitHub CLI — preinstalled on runners but absent in containers. + # wget and ca-certificates are now available from the step above. + mkdir -p -m 755 /etc/apt/keyrings + wget -qO /usr/share/keyrings/githubcli-archive-keyring.gpg \ + https://cli.github.com/packages/githubcli-archive-keyring.gpg + chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ + > /etc/apt/sources.list.d/github-cli.list + apt-get update + apt-get install -y --no-install-recommends gh + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + ref: ${{ github.event_name == 'push' && github.ref || inputs.ref }} + persist-credentials: false + + - uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1 + + - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 + with: + workspaces: desktop/src-tauri + lookup-only: true + + - name: Install appimagetool + run: | + # Pin to an immutable release tag to avoid supply-chain drift from the + # mutable `continuous` tag. Tag: 1.9.1, asset: appimagetool-.AppImage + # (https://github.com/AppImage/appimagetool/releases/tag/1.9.1) + case "$(uname -m)" in + x86_64) ARCH_SUFFIX="x86_64" ;; + aarch64) ARCH_SUFFIX="aarch64" ;; + *) + echo "::error::Unsupported architecture: $(uname -m)" + exit 1 + ;; + esac + wget -qO /tmp/appimagetool \ + "https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${ARCH_SUFFIX}.AppImage" + # SHA256 integrity check — only enforced for x86_64 (the only arch CI builds today). + # If an aarch64 runner is added, compute and add its expected hash here. + if [[ "$ARCH_SUFFIX" == "x86_64" ]]; then + echo "ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 /tmp/appimagetool" | sha256sum -c + fi + install -m 755 /tmp/appimagetool /usr/local/bin/appimagetool - name: Install desktop dependencies run: just desktop-install-ci @@ -521,6 +565,22 @@ jobs: TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + - name: Fix AppImage (remove infra libs, symlink system GStreamer) + run: | + mapfile -t APPIMAGES < <(find desktop/src-tauri/target/release/bundle/appimage -name '*.AppImage' -type f) + if [[ ${#APPIMAGES[@]} -eq 0 ]]; then + echo "::error::No AppImage found to post-process" + exit 1 + fi + if [[ ${#APPIMAGES[@]} -gt 1 ]]; then + echo "::error::Expected exactly one AppImage, found ${#APPIMAGES[@]}: ${APPIMAGES[*]}" + exit 1 + fi + bash desktop/scripts/fix-appimage.sh "${APPIMAGES[0]}" + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + - name: Locate Linux build artifacts id: linux-artifacts run: | diff --git a/desktop/scripts/fix-appimage.sh b/desktop/scripts/fix-appimage.sh new file mode 100755 index 000000000..98cd9c178 --- /dev/null +++ b/desktop/scripts/fix-appimage.sh @@ -0,0 +1,130 @@ +#!/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 +# +# 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. +# +# 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 " >&2 + exit 1 +fi + +APPIMAGE_ABS="$(realpath "$1")" +APPIMAGE_DIR="$(dirname "$APPIMAGE_ABS")" +APPIMAGE_NAME="$(basename "$APPIMAGE_ABS")" + +if [[ ! -f "$APPIMAGE_ABS" ]]; then + echo "Error: file not found: $APPIMAGE_ABS" >&2 + exit 1 +fi + +# 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" + +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" +rm -rf "$LIBDIR/gstreamer-1.0" +ln -s "/usr/lib/$MULTIARCH/gstreamer-1.0" "$LIBDIR/gstreamer-1.0" + +echo "==> Repacking AppImage" +APPIMAGE_EXTRACT_AND_RUN=1 ARCH="$(uname -m)" appimagetool \ + "$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: .AppImage + .AppImage.sig (sign the AppImage directly) +# Old: .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. +if [[ -n "${TAURI_SIGNING_PRIVATE_KEY:-}" ]]; then + echo "==> Re-signing AppImage" + (cd "$DESKTOP_DIR" && pnpm tauri signer sign \ + ${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:+--password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD"} \ + "$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 \ + ${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:+--password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD"} \ + "$TARBALL") + fi +else + echo "==> TAURI_SIGNING_PRIVATE_KEY not set — skipping signing (local build)" +fi + +echo "==> Done: $APPIMAGE_ABS" From 4b4d527504fd15a0ed58618a88c70ec26e1f2fbd Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Tue, 7 Jul 2026 17:16:10 -0400 Subject: [PATCH 2/2] fix(linux): harden AppImage repack guards and pin release-job downloads Fail loudly instead of silently shipping an unfixed artifact when the bundler layout changes, and refuse unverified appimagetool binaries on new arches. Keep the signing password off argv (the signer reads it from the env). Pin the remaining mutable release-job inputs: container image digest, gh keyring hash, and the AppImage type2 runtime, which appimagetool otherwise fetches from its mutable continuous tag at repack time. Document the AppImage host floor in RELEASING.md and fix the stale Linux auto-update note. --- .github/workflows/release.yml | 27 ++++++++++++++---- RELEASING.md | 22 +++++++++++---- desktop/scripts/fix-appimage.sh | 50 +++++++++++++++++++++++++-------- 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0bb51c46..9a9dc14c0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -445,7 +445,8 @@ jobs: name: Release Linux if: github.repository == 'block/buzz' runs-on: ubuntu-latest - container: ubuntu:22.04 + # Digest-pinned like the SHA-pinned actions below; Renovate keeps it fresh. + container: ubuntu:22.04@sha256:0e0a0fc6d18feda9db1590da249ac93e8d5abfea8f4c3c0c849ce512b5ef8982 needs: setup timeout-minutes: 60 permissions: @@ -493,8 +494,11 @@ jobs: # Install GitHub CLI — preinstalled on runners but absent in containers. # wget and ca-certificates are now available from the step above. mkdir -p -m 755 /etc/apt/keyrings - wget -qO /usr/share/keyrings/githubcli-archive-keyring.gpg \ + wget -q --tries=3 --timeout=30 -O /usr/share/keyrings/githubcli-archive-keyring.gpg \ https://cli.github.com/packages/githubcli-archive-keyring.gpg + # Pin the keyring like appimagetool below. If GitHub rotates the + # keyring this fails loudly — recompute and update the hash. + echo "6084d5d7bd8e288441e0e94fc6275570895da18e6751f70f057485dc2d1a811b /usr/share/keyrings/githubcli-archive-keyring.gpg" | sha256sum -c chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \ > /etc/apt/sources.list.d/github-cli.list @@ -526,14 +530,27 @@ jobs: exit 1 ;; esac - wget -qO /tmp/appimagetool \ + wget -q --tries=3 --timeout=30 -O /tmp/appimagetool \ "https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${ARCH_SUFFIX}.AppImage" - # SHA256 integrity check — only enforced for x86_64 (the only arch CI builds today). - # If an aarch64 runner is added, compute and add its expected hash here. + # SHA256 integrity check. Refuse to run an unverified binary: if a new + # arch (e.g. aarch64) is enabled in CI, compute its hash and add it here. if [[ "$ARCH_SUFFIX" == "x86_64" ]]; then echo "ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 /tmp/appimagetool" | sha256sum -c + else + echo "::error::No pinned SHA256 for appimagetool-${ARCH_SUFFIX} — add it before enabling this architecture" + exit 1 fi install -m 755 /tmp/appimagetool /usr/local/bin/appimagetool + # appimagetool otherwise fetches the AppImage type2 runtime from the + # MUTABLE `continuous` tag at repack time — the runtime is the first + # code users execute, so pin it too. Tag: 20251108, hash is for the + # x86_64 asset (non-x86_64 already hard-fails above). + # (https://github.com/AppImage/type2-runtime/releases/tag/20251108) + wget -q --tries=3 --timeout=30 -O /tmp/appimage-runtime \ + "https://github.com/AppImage/type2-runtime/releases/download/20251108/runtime-${ARCH_SUFFIX}" + echo "2fca8b443c92510f1483a883f60061ad09b46b978b2631c807cd873a47ec260d /tmp/appimage-runtime" | sha256sum -c + install -D -m 644 /tmp/appimage-runtime /usr/local/lib/appimage-runtime + echo "APPIMAGETOOL_RUNTIME_FILE=/usr/local/lib/appimage-runtime" >> "$GITHUB_ENV" - name: Install desktop dependencies run: just desktop-install-ci diff --git a/RELEASING.md b/RELEASING.md index ea0bd897b..98d5dd9c8 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -174,8 +174,9 @@ Each release produces two GitHub releases: (macOS). 2. **`buzz-desktop-latest`** — a rolling pre-release for the Tauri - auto-updater containing `latest.json`, the signed `.tar.gz` archive, - and its `.sig` signature. + auto-updater containing `latest.json` and each platform's signed + updater artifact plus its `.sig` signature (`.tar.gz` on macOS, + `.AppImage` on Linux, and `_alpha-unsigned.exe` on Windows). --- @@ -187,6 +188,15 @@ Silicon (`darwin-aarch64`, the `release` job) and Intel `.AppImage`. Both macOS DMGs are codesigned, notarized, and attached to the same `v` release. Intel users download the `_x64.dmg`. +The Linux AppImage is post-processed by `desktop/scripts/fix-appimage.sh`, +which strips infra libraries over-bundled by linuxdeploy (they crash on +Mesa 25+ / GLib 2.88 distros — see +[tauri-apps/tauri#15665](https://github.com/tauri-apps/tauri/issues/15665)) +and re-signs the artifact. As a result the AppImage relies on the +host's Wayland/GStreamer/graphics stack and requires GLib >= 2.72 +(Ubuntu 22.04 or newer). The `release-linux` job builds inside a +`ubuntu:22.04` container for broad GLIBC compatibility. + --- ## Prerequisites @@ -219,7 +229,7 @@ The version string must be valid semver: `MAJOR.MINOR.PATCH` with an optional pr ### Auto-updater reports "no update available" Verify that the `buzz-desktop-latest` release exists and contains a -valid `latest.json`. The auto-updater manifest currently lists -`darwin-aarch64` only, so Intel and Linux users do not yet receive -auto-updates — they download new versions manually from the release -page. (Adding their entries to `latest.json` is a follow-up.) +valid `latest.json`. The manifest covers all four platform keys +(`darwin-aarch64`, `darwin-x86_64`, `linux-x86_64`, +`windows-x86_64`); a missing entry usually means that platform's +release job failed — check the workflow run. diff --git a/desktop/scripts/fix-appimage.sh b/desktop/scripts/fix-appimage.sh index 98cd9c178..45f537bb7 100755 --- a/desktop/scripts/fix-appimage.sh +++ b/desktop/scripts/fix-appimage.sh @@ -8,6 +8,10 @@ # 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 @@ -44,15 +48,15 @@ if [[ $# -lt 1 ]]; then 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")" -if [[ ! -f "$APPIMAGE_ABS" ]]; then - echo "Error: file not found: $APPIMAGE_ABS" >&2 - exit 1 -fi - # Locate the desktop/ directory (this script lives at desktop/scripts/). SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DESKTOP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" @@ -75,6 +79,15 @@ echo "==> Extracting $APPIMAGE_NAME" 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* \ @@ -95,11 +108,22 @@ rm -f \ "$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. @@ -108,20 +132,24 @@ APPIMAGE_EXTRACT_AND_RUN=1 ARCH="$(uname -m)" appimagetool \ # Old: .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//cmdline. echo "==> Re-signing AppImage" - (cd "$DESKTOP_DIR" && pnpm tauri signer sign \ - ${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:+--password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD"} \ - "$APPIMAGE_ABS") + (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 \ - ${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:+--password "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD"} \ - "$TARBALL") + (cd "$DESKTOP_DIR" && pnpm tauri signer sign "$TARBALL") fi else echo "==> TAURI_SIGNING_PRIVATE_KEY not set — skipping signing (local build)"