diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c266881..7f21e6a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,9 +12,21 @@ env: CARGO_TERM_COLOR: always jobs: - build-and-release: - name: Build and Release - runs-on: ubuntu-latest + build: + name: Build (${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runner: ubuntu-latest + deb_arch: amd64 + rpm_arch: x86_64 + - arch: aarch64 + runner: ubuntu-24.04-arm + deb_arch: arm64 + rpm_arch: aarch64 steps: - name: Checkout repository uses: actions/checkout@v6 @@ -25,8 +37,6 @@ jobs: id: get_version run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - # --- BUILD STEPS --- - - name: Setup Node.js uses: actions/setup-node@v6 with: @@ -45,7 +55,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ src-tauri/target/ - key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-${{ matrix.arch }}-cargo-release-${{ hashFiles('**/Cargo.lock') }} - name: Install system dependencies run: | @@ -58,13 +68,9 @@ jobs: run: | VERSION=${{ steps.get_version.outputs.VERSION }} echo "Syncing version to $VERSION" - # Update package.json npm version $VERSION --no-git-tag-version --allow-same-version - # Update tauri.conf.json (handles semver with pre-release like 1.0.0-beta.1) sed -i 's/"version": "[^"]*"/"version": "'"$VERSION"'"/' src-tauri/tauri.conf.json - # Update Cargo.toml (first occurrence only, handles semver with pre-release) sed -i '0,/^version = "[^"]*"/s//version = "'"$VERSION"'"/' src-tauri/Cargo.toml - # Verify versions were updated echo "=== Versions after sync ===" echo "package.json: $(grep '"version"' package.json | head -1)" echo "tauri.conf.json: $(grep '"version"' src-tauri/tauri.conf.json | head -1)" @@ -76,8 +82,37 @@ jobs: - name: Build Tauri app run: npm run tauri:build - # --- RELEASE STEP --- - + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-${{ matrix.arch }} + path: | + src-tauri/target/release/bundle/deb/*.deb + src-tauri/target/release/bundle/appimage/*.AppImage + src-tauri/target/release/bundle/rpm/*.rpm + retention-days: 1 + + release: + name: Create Release + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Get version from tag + id: get_version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: List artifacts + run: find artifacts -type f | head -50 + - name: Create Release and Upload Assets uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') @@ -88,21 +123,28 @@ jobs: draft: false prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc') || contains(github.ref_name, 'dev') }} files: | - src-tauri/target/release/bundle/deb/*.deb - src-tauri/target/release/bundle/appimage/*.AppImage - src-tauri/target/release/bundle/rpm/*.rpm + artifacts/**/*.deb + artifacts/**/*.AppImage + artifacts/**/*.rpm body: | ## Windows 11 Clipboard History For Linux ${{ github.ref_name }} A Windows 11-style Clipboard History Manager for Linux. + ### Supported Architectures + + | Architecture | Debian/Ubuntu | Fedora/RHEL | AppImage | + |---|---|---|---| + | x86_64 (Intel/AMD) | ✅ | ✅ | ✅ | + | aarch64 (ARM64) | ✅ | ✅ | ✅ | + ### 🚀 Quick Installation (Recommended) ```bash curl -fsSL https://raw.githubusercontent.com/gustavosett/Windows-11-Clipboard-History-For-Linux/master/scripts/install.sh | bash ``` - This script auto-detects your distro and sets up everything, including paste permissions. + This script auto-detects your distro and architecture, then sets up everything including paste permissions. --- @@ -115,7 +157,7 @@ jobs: sudo setfacl -m u:$USER:rw /dev/uinput # For immediate paste access ``` - Or download the `.deb` directly: + Or download the `.deb` directly (amd64 for x86_64, arm64 for ARM64): ```bash sudo apt install ./win11-clipboard-history_${{ steps.get_version.outputs.VERSION }}_amd64.deb sudo setfacl -m u:$USER:rw /dev/uinput @@ -128,7 +170,7 @@ jobs: sudo setfacl -m u:$USER:rw /dev/uinput # For immediate paste access ``` - Or download the `.rpm` directly: + Or download the `.rpm` directly (x86_64 for Intel/AMD, aarch64 for ARM64): ```bash sudo dnf install ./win11-clipboard-history-${{ steps.get_version.outputs.VERSION }}-1.x86_64.rpm sudo setfacl -m u:$USER:rw /dev/uinput @@ -141,7 +183,7 @@ jobs: #### AppImage (Universal - any distro) ```bash - # Download, make executable, and set up paste permissions + # Download the correct AppImage for your architecture chmod +x win11-clipboard-history_${{ steps.get_version.outputs.VERSION }}_amd64.AppImage sudo setfacl -m u:$USER:rw /dev/uinput # Required for paste simulation ./win11-clipboard-history_${{ steps.get_version.outputs.VERSION }}_amd64.AppImage @@ -172,15 +214,29 @@ jobs: echo "Uploading STABLE release v$VERSION to Cloudsmith..." - # Upload .deb - cloudsmith push deb gustavosett/clipboard-manager/any-distro/any-version \ - src-tauri/target/release/bundle/deb/*.deb \ - --api-key $CLOUDSMITH_API_KEY || echo "Cloudsmith .deb upload failed (may already exist)" + # Upload all .deb files (x86_64 and arm64) + deb_files=$(find artifacts -name '*.deb' 2>/dev/null) + if [ -n "$deb_files" ]; then + echo "$deb_files" | while read -r deb; do + echo "Uploading $deb..." + cloudsmith push deb gustavosett/clipboard-manager/any-distro/any-version \ + "$deb" --api-key $CLOUDSMITH_API_KEY || echo "Cloudsmith .deb upload failed for $deb (may already exist)" + done + else + echo "No .deb files found to upload" + fi - # Upload .rpm - cloudsmith push rpm gustavosett/clipboard-manager/any-distro/any-version \ - src-tauri/target/release/bundle/rpm/*.rpm \ - --api-key $CLOUDSMITH_API_KEY || echo "Cloudsmith .rpm upload failed (may already exist)" + # Upload all .rpm files (x86_64 and aarch64) + rpm_files=$(find artifacts -name '*.rpm' 2>/dev/null) + if [ -n "$rpm_files" ]; then + echo "$rpm_files" | while read -r rpm; do + echo "Uploading $rpm..." + cloudsmith push rpm gustavosett/clipboard-manager/any-distro/any-version \ + "$rpm" --api-key $CLOUDSMITH_API_KEY || echo "Cloudsmith .rpm upload failed for $rpm (may already exist)" + done + else + echo "No .rpm files found to upload" + fi - name: Update AUR Package if: ${{ !contains(github.ref_name, 'alpha') && !contains(github.ref_name, 'beta') && !contains(github.ref_name, 'rc') && !contains(github.ref_name, 'dev') }} @@ -194,10 +250,25 @@ jobs: fi VERSION=${{ steps.get_version.outputs.VERSION }} - DEB_FILE=$(ls src-tauri/target/release/bundle/deb/*.deb | head -1) - SHA256=$(sha256sum "$DEB_FILE" | cut -d' ' -f1) - echo "Updating AUR package to version $VERSION with checksum $SHA256" + # Compute checksums for each architecture + DEB_X86_64=$(find artifacts -name "*_amd64.deb" | head -1) + DEB_ARM64=$(find artifacts -name "*_arm64.deb" | head -1) + + if [ -z "$DEB_X86_64" ]; then + echo "x86_64 .deb not found, skipping AUR update" + exit 0 + fi + + SHA256_X86_64=$(sha256sum "$DEB_X86_64" | cut -d' ' -f1) + SHA256_ARM64="" + if [ -n "$DEB_ARM64" ]; then + SHA256_ARM64=$(sha256sum "$DEB_ARM64" | cut -d' ' -f1) + fi + + echo "Updating AUR package to version $VERSION" + echo "x86_64 checksum: $SHA256_X86_64" + echo "arm64 checksum: ${SHA256_ARM64:-SKIP}" # Setup SSH for AUR mkdir -p ~/.ssh @@ -212,10 +283,19 @@ jobs: git clone ssh://aur@aur.archlinux.org/win11-clipboard-history-bin.git /tmp/aur-pkg cd /tmp/aur-pkg - # Update PKGBUILD + # Use aur/PKGBUILD from the repo as the single source of truth + cp "$GITHUB_WORKSPACE/aur/PKGBUILD" PKGBUILD + cp "$GITHUB_WORKSPACE/aur/win11-clipboard-history-bin.install" . 2>/dev/null || true + + # Update version and checksums via sed sed -i "s/^pkgver=.*/pkgver=$VERSION/" PKGBUILD sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD - sed -i "s/sha256sums_x86_64=.*/sha256sums_x86_64=('$SHA256')/" PKGBUILD + sed -i "s/sha256sums_x86_64=.*/sha256sums_x86_64=('$SHA256_X86_64')/" PKGBUILD + if [ -n "$SHA256_ARM64" ]; then + sed -i "s/sha256sums_aarch64=.*/sha256sums_aarch64=('$SHA256_ARM64')/" PKGBUILD + else + sed -i "s/sha256sums_aarch64=.*/sha256sums_aarch64=('SKIP')/" PKGBUILD + fi # Generate .SRCINFO docker run --rm -v "$PWD:/pkg" archlinux bash -c " @@ -223,30 +303,39 @@ jobs: cd /pkg makepkg --printsrcinfo > .SRCINFO " || { - # Fallback: generate manually if docker fails - echo "pkgbase = win11-clipboard-history-bin - pkgdesc = Windows 11-style Clipboard History Manager for Linux - pkgver = $VERSION - pkgrel = 1 - url = https://github.com/gustavosett/Windows-11-Clipboard-History-For-Linux - arch = x86_64 - license = MIT - depends = webkit2gtk-4.1 - depends = gtk3 - depends = libayatana-appindicator - depends = xclip - depends = wl-clipboard - depends = acl - optdepends = libappindicator-gtk3: Legacy tray icon support - provides = win11-clipboard-history - conflicts = win11-clipboard-history - options = !strip - options = !debug - source_x86_64 = https://github.com/gustavosett/Windows-11-Clipboard-History-For-Linux/releases/download/v$VERSION/win11-clipboard-history_${VERSION}_amd64.deb - sha256sums_x86_64 = $SHA256 - install = win11-clipboard-history-bin.install + echo "Docker failed, generating .SRCINFO manually" + TAB=$'\t' + cat > .SRCINFO << SRCINFO_EOF + pkgbase = win11-clipboard-history-bin + ${TAB}pkgdesc = Windows 11-style Clipboard History Manager for Linux + ${TAB}pkgver = $VERSION + ${TAB}pkgrel = 1 + ${TAB}url = https://github.com/gustavosett/Windows-11-Clipboard-History-For-Linux + ${TAB}install = win11-clipboard-history-bin.install + ${TAB}arch = x86_64 + ${TAB}arch = aarch64 + ${TAB}license = MIT + ${TAB}depends = webkit2gtk-4.1 + ${TAB}depends = gtk3 + ${TAB}depends = libayatana-appindicator + ${TAB}depends = xclip + ${TAB}depends = xdotool + ${TAB}depends = wl-clipboard + ${TAB}depends = acl + ${TAB}depends = polkit + ${TAB}optdepends = libappindicator-gtk3: Legacy tray icon support + ${TAB}provides = win11-clipboard-history + ${TAB}conflicts = win11-clipboard-history + ${TAB}options = !strip + ${TAB}options = !debug + ${TAB}source_x86_64 = https://github.com/gustavosett/Windows-11-Clipboard-History-For-Linux/releases/download/v$VERSION/win11-clipboard-history_${VERSION}_amd64.deb + ${TAB}sha256sums_x86_64 = $SHA256_X86_64 + ${TAB}source_aarch64 = https://github.com/gustavosett/Windows-11-Clipboard-History-For-Linux/releases/download/v$VERSION/win11-clipboard-history_${VERSION}_arm64.deb + ${TAB}sha256sums_aarch64 = ${SHA256_ARM64:-SKIP} - pkgname = win11-clipboard-history-bin" > .SRCINFO + pkgname = win11-clipboard-history-bin + SRCINFO_EOF + sed -i 's/^ //' .SRCINFO } # Commit and push diff --git a/README.md b/README.md index 717ead0..01dec8e 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,9 @@ sudo setfacl -m u:$USER:rw /dev/uinput **Option 2: Direct Download** ```bash -# Download and install (replace VERSION with actual version) -sudo apt install ./win11-clipboard-history_VERSION_amd64.deb +# Download and install (replace VERSION and ARCH with actual values) +# ARCH: amd64 for x86_64, arm64 for ARM64/aarch64 +sudo apt install ./win11-clipboard-history_VERSION_ARCH.deb # The package sets up udev rules automatically. # For immediate paste access (without logout): @@ -101,8 +102,9 @@ sudo setfacl -m u:$USER:rw /dev/uinput **Option 2: Direct Download** ```bash -# Download and install (replace VERSION with actual version) -sudo dnf install ./win11-clipboard-history-VERSION-1.x86_64.rpm +# Download and install (replace VERSION and ARCH with actual values) +# ARCH: x86_64 for Intel/AMD, aarch64 for ARM64 +sudo dnf install ./win11-clipboard-history-VERSION-1.ARCH.rpm # For immediate paste access (without logout): sudo setfacl -m u:$USER:rw /dev/uinput diff --git a/aur/PKGBUILD b/aur/PKGBUILD index 6641c31..97d48b2 100644 --- a/aur/PKGBUILD +++ b/aur/PKGBUILD @@ -3,7 +3,7 @@ pkgname=win11-clipboard-history-bin pkgver=0.5.5 pkgrel=1 pkgdesc="Windows 11-style Clipboard History Manager for Linux" -arch=('x86_64') +arch=('x86_64' 'aarch64') url="https://github.com/gustavosett/Windows-11-Clipboard-History-For-Linux" license=('MIT') depends=( @@ -23,7 +23,9 @@ provides=('win11-clipboard-history') conflicts=('win11-clipboard-history') options=('!strip' '!debug') source_x86_64=("${url}/releases/download/v${pkgver}/win11-clipboard-history_${pkgver}_amd64.deb") +source_aarch64=("${url}/releases/download/v${pkgver}/win11-clipboard-history_${pkgver}_arm64.deb") sha256sums_x86_64=('SKIP') +sha256sums_aarch64=('SKIP') install="${pkgname}.install" package() { diff --git a/scripts/install.sh b/scripts/install.sh index e43470d..6707a13 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -128,25 +128,46 @@ install_via_package_manager() { # Clean up any previous AppImage installation to prevent PATH conflicts cleanup_appimage_installation - # 1. Check for Arch Family (Arch, Manjaro, CachyOS, Endeavour, etc) - if [[ "$SYSTEM_FAMILY_INFO" =~ "arch" ]] || command -v pacman &>/dev/null; then - install_aur + # IMPORTANT: Check distro ID/family FIRST before falling back to command detection. + # This prevents misdetection when tools like pacman are installed on non-Arch systems. + + # 1. Check for Fedora/RHEL Family + # Note: fedora-asahi-remix is a Fedora-based distro for Apple Silicon Macs + if [[ "$DISTRO_ID" == "fedora-asahi-remix" || "$SYSTEM_FAMILY_INFO" =~ "fedora" || "$SYSTEM_FAMILY_INFO" =~ "rhel" || "$SYSTEM_FAMILY_INFO" =~ "centos" ]]; then + install_rpm return 0 # 2. Check for Debian/Ubuntu Family - elif [[ "$SYSTEM_FAMILY_INFO" =~ "debian" || "$SYSTEM_FAMILY_INFO" =~ "ubuntu" ]] || command -v apt-get &>/dev/null; then + elif [[ "$SYSTEM_FAMILY_INFO" =~ "debian" || "$SYSTEM_FAMILY_INFO" =~ "ubuntu" ]]; then install_deb return 0 - - # 3. Check for Fedora/RHEL Family - elif [[ "$SYSTEM_FAMILY_INFO" =~ "fedora" || "$SYSTEM_FAMILY_INFO" =~ "rhel" || "$SYSTEM_FAMILY_INFO" =~ "centos" ]] || command -v dnf &>/dev/null; then + + # 3. Check for OpenSUSE Family + elif [[ "$SYSTEM_FAMILY_INFO" =~ "suse" ]]; then + install_rpm_suse + return 0 + + # 4. Check for Arch Family (Arch, Manjaro, CachyOS, Endeavour, etc) + # Check this AFTER other distros to avoid false positives from pacman being installed + elif [[ "$SYSTEM_FAMILY_INFO" =~ "arch" ]]; then + install_aur + return 0 + fi + + # Fallback: Check for package managers if distro detection failed + # This handles edge cases where /etc/os-release is incomplete + if command -v dnf &>/dev/null; then install_rpm return 0 - - # 4. Check for OpenSUSE Family - elif [[ "$SYSTEM_FAMILY_INFO" =~ "suse" ]] || command -v zypper &>/dev/null; then + elif command -v apt-get &>/dev/null; then + install_deb + return 0 + elif command -v zypper &>/dev/null; then install_rpm_suse return 0 + elif command -v pacman &>/dev/null; then + install_aur + return 0 fi return 1 # Unknown system family @@ -187,7 +208,9 @@ install_deb() { BASE_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/$RELEASE_TAG" log "Downloading $FILE..." - curl -L -o "$FILE" "$BASE_URL/$FILE" --progress-bar + if ! curl -L -o "$FILE" "$BASE_URL/$FILE" --progress-bar --fail; then + error "Failed to download $FILE" + fi chmod 644 "$FILE" log "Installing dependencies..." @@ -203,8 +226,29 @@ install_deb() { install_rpm() { log "Setting up RPM repository (Cloudsmith)..." + local env_args=() + if [[ "$DISTRO_ID" == "fedora-asahi-remix" ]]; then + log "Detected Fedora Asahi Remix - using standard Fedora repository..." + local fedora_version="" + if [ -f /etc/os-release ]; then + fedora_version="$(awk -F= '$1=="VERSION_ID"{gsub(/"/,"",$2);print $2}' /etc/os-release)" + fi + env_args=("distro=fedora" "codename=") + if [[ -n "$fedora_version" ]]; then + env_args+=("version=$fedora_version") + fi + fi + # Try Cloudsmith repository first (enables auto-updates) - if curl -1sLf "https://dl.cloudsmith.io/public/${CLOUDSMITH_REPO}/setup.rpm.sh" | sudo -E bash 2>/dev/null; then + local setup_cmd="curl -1sLf 'https://dl.cloudsmith.io/public/${CLOUDSMITH_REPO}/setup.rpm.sh' | sudo -E bash" + local repo_setup_success=false + if [[ ${#env_args[@]} -gt 0 ]]; then + env "${env_args[@]}" bash -c "$setup_cmd" 2>/dev/null && repo_setup_success=true + else + bash -c "$setup_cmd" 2>/dev/null && repo_setup_success=true + fi + + if [ "$repo_setup_success" = true ]; then log "Installing win11-clipboard-history from repository..." if sudo dnf install -y win11-clipboard-history; then success "Installed via DNF repository! (auto-updates enabled)" @@ -230,7 +274,9 @@ install_rpm() { BASE_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/$RELEASE_TAG" log "Downloading $FILE..." - curl -L -o "$FILE" "$BASE_URL/$FILE" --progress-bar + if ! curl -L -o "$FILE" "$BASE_URL/$FILE" --progress-bar --fail; then + error "Failed to download $FILE" + fi chmod 644 "$FILE" log "Installing dependencies..." @@ -272,7 +318,9 @@ install_rpm_suse() { BASE_URL="https://github.com/$REPO_OWNER/$REPO_NAME/releases/download/$RELEASE_TAG" log "Downloading $FILE..." - curl -L -o "$FILE" "$BASE_URL/$FILE" --progress-bar + if ! curl -L -o "$FILE" "$BASE_URL/$FILE" --progress-bar --fail; then + error "Failed to download $FILE" + fi chmod 644 "$FILE" log "Installing dependencies..." @@ -306,11 +354,24 @@ install_aur() { install_appimage() { log "Installing AppImage (universal fallback)..." - # Fetch latest version + local arch_name + arch_name=$(uname -m) + + # Fetch latest version, filtering by architecture LATEST_URL=$(curl -s https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/latest \ | grep "browser_download_url.*AppImage" \ + | grep -i "${DEB_ARCH}\|${arch_name}" \ + | head -1 \ | cut -d '"' -f 4) + # Fallback: try any AppImage if no arch-specific match found + if [ -z "$LATEST_URL" ]; then + LATEST_URL=$(curl -s https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/latest \ + | grep "browser_download_url.*AppImage" \ + | head -1 \ + | cut -d '"' -f 4) + fi + if [ -z "$LATEST_URL" ]; then error "Could not find AppImage download URL" fi @@ -330,21 +391,54 @@ install_appimage() { curl -fsSL -o "$HOME/.local/share/icons/hicolor/128x128/apps/win11-clipboard-history.png" \ "https://raw.githubusercontent.com/$REPO_OWNER/$REPO_NAME/master/src-tauri/icons/128x128.png" 2>/dev/null || true - # Wrapper script - cat > "$HOME/.local/bin/win11-clipboard-history" << 'EOF' + # Wrapper script — mirrors src-tauri/bundle/linux/wrapper.sh sanitization + cat > "$HOME/.local/bin/win11-clipboard-history" << 'WRAPPER_EOF' #!/bin/bash -# Clean up environment to avoid Snap/Flatpak library conflicts +# AppImage wrapper for win11-clipboard-history +# Sanitizes Snap/Flatpak environment leaks, then launches the AppImage. + +# Always clear library/runtime overrides from sandbox parents unset LD_LIBRARY_PATH unset LD_PRELOAD unset GTK_PATH unset GIO_MODULE_DIR +unset GTK_IM_MODULE_FILE +unset GTK_EXE_PREFIX +unset LOCPATH +unset GSETTINGS_SCHEMA_DIR + +# Fix XDG_DATA_DIRS only when contaminated by sandbox paths +sanitize_xdg_data_dirs() { + local xdg="${XDG_DATA_DIRS:-}" + local system_dirs="/usr/local/share:/usr/share:/var/lib/snapd/desktop" + + if [[ -z "${SNAP:-}" && -z "${FLATPAK_ID:-}" && "$xdg" != *"/snap/"* && "$xdg" != *"/flatpak/"* ]]; then + return + fi + + local cleaned="" + local entry + IFS=':' read -ra entries <<< "$xdg" + for entry in "${entries[@]}"; do + case "$entry" in + */snap/*|*/flatpak/*) continue ;; + esac + case ":$system_dirs:" in + *":$entry:"*) continue ;; + esac + cleaned="${cleaned:+$cleaned:}$entry" + done + + export XDG_DATA_DIRS="${system_dirs}${cleaned:+:$cleaned}" +} +sanitize_xdg_data_dirs export GDK_SCALE="${GDK_SCALE:-1}" export GDK_DPI_SCALE="${GDK_DPI_SCALE:-1}" export NO_AT_BRIDGE=1 exec "$HOME/.local/bin/win11-clipboard-history.AppImage" "$@" -EOF +WRAPPER_EOF chmod +x "$HOME/.local/bin/win11-clipboard-history" # .desktop file with proper icon diff --git a/src-tauri/bundle/linux/wrapper.sh b/src-tauri/bundle/linux/wrapper.sh index 4da6034..dce0f84 100755 --- a/src-tauri/bundle/linux/wrapper.sh +++ b/src-tauri/bundle/linux/wrapper.sh @@ -33,12 +33,65 @@ if [ -z "$BINARY" ]; then exit 1 fi -# Clean up environment to avoid Snap/Flatpak library conflicts +# --------------------------------------------------------------------------- +# Runtime environment sanitization +# --------------------------------------------------------------------------- +# When launched from a Snap terminal (e.g. VS Code Snap) or a Flatpak host, +# the parent process may inject library/schema paths from its confined runtime +# into child processes. These paths point to sandbox-internal libraries that +# are incompatible with the host GTK/WebKit stack this app links against, +# causing crashes, missing schemas, or wrong icon themes. +# --------------------------------------------------------------------------- + +# Always clear library/runtime overrides — they must never leak from a sandbox +# into the host-linked Tauri binary. unset LD_LIBRARY_PATH unset LD_PRELOAD unset GTK_PATH unset GIO_MODULE_DIR +unset GTK_IM_MODULE_FILE +unset GTK_EXE_PREFIX +unset LOCPATH +unset GSETTINGS_SCHEMA_DIR +# Fix XDG_DATA_DIRS only when contaminated by sandbox paths. +# Snap terminals inject entries like /snap/code/*/usr/share which cause the +# app to resolve wrong GSettings schemas, icons, or .desktop files. +# When contaminated we strip sandbox entries and place system dirs first +# (matching run-dev.sh) so host resources always win. +sanitize_xdg_data_dirs() { + local xdg="${XDG_DATA_DIRS:-}" + local system_dirs="/usr/local/share:/usr/share:/var/lib/snapd/desktop" + + # Detect contamination: $SNAP/$FLATPAK_ID set, or paths contain sandbox dirs + if [[ -z "${SNAP:-}" && -z "${FLATPAK_ID:-}" && "$xdg" != *"/snap/"* && "$xdg" != *"/flatpak/"* ]]; then + return # Environment is clean — leave XDG_DATA_DIRS untouched + fi + + # Rebuild: keep only non-sandbox entries that aren't already in system_dirs + local cleaned="" + local entry + IFS=':' read -ra entries <<< "$xdg" + for entry in "${entries[@]}"; do + # Skip sandbox-injected paths + case "$entry" in + */snap/*|*/flatpak/*) continue ;; + esac + # Skip if already covered by system_dirs (avoid duplicates) + case ":$system_dirs:" in + *":$entry:"*) continue ;; + esac + cleaned="${cleaned:+$cleaned:}$entry" + done + + # System dirs first (highest precedence), then remaining clean dirs + export XDG_DATA_DIRS="${system_dirs}${cleaned:+:$cleaned}" +} +sanitize_xdg_data_dirs + +# --------------------------------------------------------------------------- +# Display & rendering defaults +# --------------------------------------------------------------------------- export GDK_SCALE="${GDK_SCALE:-1}" export GDK_DPI_SCALE="${GDK_DPI_SCALE:-1}" @@ -48,7 +101,7 @@ export TAURI_TRAY="${TAURI_TRAY:-libayatana-appindicator3}" export NO_AT_BRIDGE=1 # Force software rendering in virtualized environments to avoid GPU issues -if systemd-detect-virt -q; then +if systemd-detect-virt -q 2>/dev/null; then export LIBGL_ALWAYS_SOFTWARE=1 fi