#!/usr/bin/env bash # Post-process a Tauri-built Linux AppImage to repair the Bun sidecar. # # WHY: Tauri's AppImage step runs linuxdeploy, which patchelf-injects a RUNPATH # ($ORIGIN/../lib) into every executable in usr/bin. The Bun-compiled sidecar # `xpbridge` is a self-contained binary (~91 MB, JS/assets appended past the ELF) # and does NOT survive ELF rewriting — the patched copy core-dumps on start, so # the app launches but the bridge never listens. The repo/standalone binary is # fine; only the AppImage copy is corrupt. # # FIX: replace the patched sidecar in the AppImage with the pristine repo binary, # repack (the appimage plugin does NOT patchelf), and regenerate the updater .sig. # # Run AFTER `tauri build ... --bundles appimage[,updater]`. Idempotent. set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" TARGET_DIR="${CARGO_TARGET_DIR:-$ROOT/target-linux}" BUNDLE_DIR="$TARGET_DIR/x86_64-unknown-linux-gnu/release/bundle/appimage" ORIG_SIDECAR="$ROOT/desktop/src-tauri/binaries/xpbridge-x86_64-unknown-linux-gnu" PLUGIN="$HOME/.cache/tauri/linuxdeploy-plugin-appimage.AppImage" [[ -f "$ORIG_SIDECAR" ]] || { echo "!! pristine sidecar missing: $ORIG_SIDECAR (run prep-desktop.sh)"; exit 1; } [[ -x "$PLUGIN" ]] || { echo "!! linuxdeploy-plugin-appimage missing: $PLUGIN (run a tauri appimage build once to fetch it)"; exit 1; } APPIMAGE="$(find "$BUNDLE_DIR" -maxdepth 1 -name '*.AppImage' | head -1)" [[ -n "$APPIMAGE" ]] || { echo "!! no AppImage found in $BUNDLE_DIR"; exit 1; } echo "==> repairing sidecar in: $(basename "$APPIMAGE")" WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT ( cd "$WORK" && APPIMAGE_EXTRACT_AND_RUN=1 "$APPIMAGE" --appimage-extract >/dev/null ) APPDIR="$WORK/squashfs-root" SC="$(find "$APPDIR" -name xpbridge -type f | head -1)" [[ -n "$SC" ]] || { echo "!! xpbridge not found inside AppImage"; exit 1; } cp -f "$ORIG_SIDECAR" "$SC"; chmod +x "$SC" echo "==> restored pristine sidecar ($(sha256sum "$SC" | cut -c1-12)...)" # Repack. The appimage plugin embeds the AppDir verbatim — no patchelf — so the # sidecar stays byte-identical to the repo binary. OUT="$WORK/out.AppImage" ( cd "$WORK" && APPIMAGE_EXTRACT_AND_RUN=1 NO_STRIP=1 ARCH=x86_64 LDAI_OUTPUT="$OUT" \ "$PLUGIN" --appdir "$APPDIR" >/dev/null ) [[ -f "$OUT" ]] || { echo "!! repack produced no AppImage"; exit 1; } # sanity: the repacked sidecar must run, not core-dump ( cd "$WORK" && rm -rf v && mkdir v && cd v && APPIMAGE_EXTRACT_AND_RUN=1 "$OUT" --appimage-extract >/dev/null ) NEWSC="$(find "$WORK/v/squashfs-root" -name xpbridge -type f | head -1)" if [[ "$(sha256sum "$NEWSC" | cut -d' ' -f1)" != "$(sha256sum "$ORIG_SIDECAR" | cut -d' ' -f1)" ]]; then echo "!! repacked sidecar hash != pristine — repack still mangled it"; exit 1 fi echo "==> verified: repacked sidecar is byte-identical to repo binary" mv -f "$OUT" "$APPIMAGE" echo "==> replaced original AppImage (same name, updater url unchanged)" # Regenerate the updater signature for the new file, if signing is configured. SIG="$APPIMAGE.sig" if [[ -f "$ROOT/desktop/.tauri-signing.key" && -f "$ROOT/desktop/.tauri-signing.pw" ]]; then # signer writes .sig next to the input automatically TAURI_SIGNING_PRIVATE_KEY="$(cat "$ROOT/desktop/.tauri-signing.key")" \ TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$(cat "$ROOT/desktop/.tauri-signing.pw")" \ npx --prefix desktop tauri signer sign "$APPIMAGE" >/dev/null echo "==> regenerated updater signature: $(basename "$SIG")" else [[ -f "$SIG" ]] && { rm -f "$SIG"; echo "==> no signing key — removed stale $(basename "$SIG")"; } fi echo "==> done. AppImage repaired."