From 336ab0a24f8db9a3361ffd72f4c77c0e890e06f2 Mon Sep 17 00:00:00 2001 From: Youwes09 Date: Sun, 26 Apr 2026 21:41:24 -0500 Subject: [PATCH] Chore: Appimage-Workflow (V1) --- .github/workflows/build-linux.yml | 178 ++++++++++++++++++ src-tauri/binaries/suwayomi-launcher-linux.sh | 112 +++++++++++ src-tauri/tauri.linux.conf.json | 13 ++ 3 files changed, 303 insertions(+) create mode 100644 .github/workflows/build-linux.yml create mode 100644 src-tauri/binaries/suwayomi-launcher-linux.sh create mode 100644 src-tauri/tauri.linux.conf.json diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml new file mode 100644 index 0000000..e4bc4d9 --- /dev/null +++ b/.github/workflows/build-linux.yml @@ -0,0 +1,178 @@ +name: Build Linux + +on: + workflow_dispatch: + inputs: + version: + description: "Version to build (e.g. 0.9.0)" + required: true + +permissions: + contents: write + +jobs: + frontend: + name: Build frontend + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: latest + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Upload dist + uses: actions/upload-artifact@v4 + with: + name: frontend-dist-linux + path: dist/ + retention-days: 1 + + tauri: + name: Tauri (Linux x64) + needs: frontend + # Pin to 22.04 — libwebkit2gtk-4.1 ABI changed in 24.04 and AppImages + # built there will not run on older distros. + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + + - name: Download frontend dist + uses: actions/download-artifact@v4 + with: + name: frontend-dist-linux + path: dist/ + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libappindicator3-dev \ + librsvg2-dev \ + patchelf + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-unknown-linux-gnu + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - uses: pnpm/action-setup@v4 + with: + version: latest + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install JS dependencies + run: pnpm install --frozen-lockfile + + - name: Download Suwayomi (Linux x64) + run: | + curl -fsSL \ + "https://github.com/Suwayomi/Suwayomi-Server/releases/download/v2.1.1867/Suwayomi-Server-v2.1.1867-linux-x64.tar.gz" \ + -o suwayomi-linux.tar.gz + + echo "b2344bd73c4e26bede63cdb4b44b1b4168d8a8500b3b2b1a0219519a3ef708fe suwayomi-linux.tar.gz" | sha256sum -c - + + mkdir -p suwayomi-extracted + tar -xzf suwayomi-linux.tar.gz -C suwayomi-extracted --strip-components=1 + + - name: Stage Suwayomi bundle + run: | + mkdir -p src-tauri/binaries + + # Actual layout after --strip-components=1: + # bin/Suwayomi-Server.jar + # bin/catch_abort.so ← already compiled, ship it directly + # jre/bin/java + # jre/lib/... + # electron/ ← ignored + JAR="suwayomi-extracted/bin/Suwayomi-Server.jar" + JAVA="suwayomi-extracted/jre/bin/java" + CATCH="suwayomi-extracted/bin/catch_abort.so" + + for f in "$JAR" "$JAVA" "$CATCH"; do + if [ ! -e "$f" ]; then + echo "ERROR: expected file not found: $f" + find suwayomi-extracted -type f | head -40 + exit 1 + fi + done + + echo "JAR=$JAR JAVA=$JAVA CATCH=$CATCH" + + cp -r suwayomi-extracted src-tauri/binaries/suwayomi-bundle + chmod +x src-tauri/binaries/suwayomi-bundle/jre/bin/java + + - name: Stage Linux launcher sidecar + run: | + # Tauri names externalBin sidecars as - at build time. + cp src-tauri/binaries/suwayomi-launcher-linux.sh \ + src-tauri/binaries/suwayomi-launcher-linux-x86_64-unknown-linux-gnu + chmod +x src-tauri/binaries/suwayomi-launcher-linux-x86_64-unknown-linux-gnu + + - name: Patch tauri.conf.json for CI + run: | + sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json + + - name: Build Tauri app + run: pnpm tauri build --target x86_64-unknown-linux-gnu --config src-tauri/tauri.linux.conf.json + + - name: Upload Linux artifacts to release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ github.event.inputs.version }} + run: | + # Wait for the Windows workflow to have created the draft release. + for i in $(seq 1 12); do + RELEASE_ID=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + "https://api.github.com/repos/moku-project/Moku/releases" \ + | jq -r '.[] | select(.tag_name == "v'"$VERSION"'") | .id' | head -1) + if [ -n "$RELEASE_ID" ]; then break; fi + echo "Waiting for release to exist... attempt $i" + sleep 15 + done + + if [ -z "$RELEASE_ID" ]; then + echo "ERROR: Could not find release for v$VERSION after waiting" + exit 1 + fi + + echo "Found release ID: $RELEASE_ID" + + upload_asset() { + local file="$1" + local name="$2" + echo "Uploading $name..." + curl -s -X POST \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @"$file" \ + "https://uploads.github.com/repos/moku-project/Moku/releases/$RELEASE_ID/assets?name=$name" + } + + APPIMAGE=$(find src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/appimage -name "*.AppImage" | head -1) + DEB=$(find src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb -name "*.deb" | head -1) + + [ -n "$APPIMAGE" ] && upload_asset "$APPIMAGE" "moku-linux-x64-${VERSION}.AppImage" + [ -n "$DEB" ] && upload_asset "$DEB" "moku-linux-x64-${VERSION}.deb" \ No newline at end of file diff --git a/src-tauri/binaries/suwayomi-launcher-linux.sh b/src-tauri/binaries/suwayomi-launcher-linux.sh new file mode 100644 index 0000000..5b4fabb --- /dev/null +++ b/src-tauri/binaries/suwayomi-launcher-linux.sh @@ -0,0 +1,112 @@ +#!/bin/sh +# Moku — Suwayomi launcher for Linux AppImage/deb. +# Tauri resolves this via resolve_server_binary() in lib.rs, which looks for +# "suwayomi-launcher" or "suwayomi-launcher.sh" in the resource directory. +set -e + +# ── Locate our resource directory ───────────────────────────────────────────── +# In an AppImage: resources sit at /resources/ +# In a deb install: /usr/lib/moku/resources/ (Tauri's default) +# We resolve relative to this script's own location. +SELF="$0" +while [ -L "$SELF" ]; do + SELF="$(readlink "$SELF")" +done +SCRIPT_DIR="$(cd "$(dirname "$SELF")" && pwd)" + +# Tauri places resources one level up from the binary on Linux. +# Try a few candidates so this works in both AppImage and installed layouts. +find_resource() { + for candidate in \ + "${SCRIPT_DIR}" \ + "${SCRIPT_DIR}/../resources" \ + "${SCRIPT_DIR}/resources" + do + if [ -f "${candidate}/Suwayomi-Server.jar" ]; then + echo "$(cd "$candidate" && pwd)" + return 0 + fi + done + return 1 +} + +RESOURCE_DIR=$(find_resource) || { + echo "[launcher] ERROR: cannot locate Suwayomi-Server.jar relative to $SCRIPT_DIR" >&2 + exit 1 +} + +JAR="${RESOURCE_DIR}/Suwayomi-Server.jar" +JAVA="${RESOURCE_DIR}/jre/bin/java" +CATCH_ABORT="${RESOURCE_DIR}/catch_abort.so" + +echo "[launcher] RESOURCE_DIR=$RESOURCE_DIR" >&2 +echo "[launcher] JAVA=$JAVA" >&2 +echo "[launcher] JAR=$JAR" >&2 + +if [ ! -x "$JAVA" ]; then + echo "[launcher] ERROR: java not executable at $JAVA" >&2 + exit 1 +fi +if [ ! -f "$JAR" ]; then + echo "[launcher] ERROR: jar not found at $JAR" >&2 + exit 1 +fi + +# ── Data directory ───────────────────────────────────────────────────────────── +DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/moku/tachidesk" +mkdir -p "$DATA_DIR" + +# ── Seed server.conf on first run ────────────────────────────────────────────── +if [ ! -f "$DATA_DIR/server.conf" ]; then + cat > "$DATA_DIR/server.conf" << 'EOF' +server.ip = "127.0.0.1" +server.port = 4567 +server.webUIEnabled = false +server.initialOpenInBrowserEnabled = false +server.systemTrayEnabled = false +server.webUIInterface = "browser" +server.webUIFlavor = "WebUI" +server.webUIChannel = "stable" +server.electronPath = "" +server.debugLogsEnabled = false +server.downloadAsCbz = true +server.autoDownloadNewChapters = false +server.globalUpdateInterval = 12 +server.maxSourcesInParallel = 6 +server.extensionRepos = [] +EOF +fi + +# ── Force-patch the three keys that cause JCEF/GUI crashes ──────────────────── +sed -i \ + -e 's|server\.webUIEnabled.*|server.webUIEnabled = false|' \ + -e 's|server\.initialOpenInBrowserEnabled.*|server.initialOpenInBrowserEnabled = false|' \ + -e 's|server\.systemTrayEnabled.*|server.systemTrayEnabled = false|' \ + "$DATA_DIR/server.conf" + +# Append keys if absent (e.g. user-managed conf missing them) +grep -q 'server\.webUIEnabled' "$DATA_DIR/server.conf" || echo 'server.webUIEnabled = false' >> "$DATA_DIR/server.conf" +grep -q 'server\.initialOpenInBrowserEnabled' "$DATA_DIR/server.conf" || echo 'server.initialOpenInBrowserEnabled = false' >> "$DATA_DIR/server.conf" +grep -q 'server\.systemTrayEnabled' "$DATA_DIR/server.conf" || echo 'server.systemTrayEnabled = false' >> "$DATA_DIR/server.conf" + +# ── Suppress any GUI environment that would confuse the JVM ─────────────────── +unset DISPLAY +unset WAYLAND_DISPLAY + +export _JAVA_OPTIONS="-Djava.awt.headless=true" +export JAVA_TOOL_OPTIONS="-Djava.awt.headless=true" + +# ── LD_PRELOAD catch_abort.so if present ────────────────────────────────────── +# Catches SIGTRAP/SIGILL from KCEF/Webview so a bad extension can't +# bring down the whole server process (mirrors the Flatpak build). +if [ -f "$CATCH_ABORT" ]; then + export LD_PRELOAD="${CATCH_ABORT}${LD_PRELOAD:+:$LD_PRELOAD}" +fi + +exec "$JAVA" \ + -Djava.awt.headless=true \ + -Dapple.awt.UIElement=true \ + -Dsun.java2d.noddraw=true \ + -Dsun.awt.disablegui=true \ + -Dsuwayomi.tachidesk.config.server.rootDir="$DATA_DIR" \ + -jar "$JAR" \ No newline at end of file diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json new file mode 100644 index 0000000..0961644 --- /dev/null +++ b/src-tauri/tauri.linux.conf.json @@ -0,0 +1,13 @@ +{ + "bundle": { + "targets": ["appimage", "deb"], + "externalBin": [ + "binaries/suwayomi-launcher-linux" + ], + "resources": { + "binaries/suwayomi-bundle/bin/Suwayomi-Server.jar": "Suwayomi-Server.jar", + "binaries/suwayomi-bundle/bin/catch_abort.so": "catch_abort.so", + "binaries/suwayomi-bundle/jre": "jre" + } + } +} \ No newline at end of file