mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Feat: Bundle Suwayomi JRE for Windows/Linux
This commit is contained in:
@@ -0,0 +1,177 @@
|
|||||||
|
name: Build Linux
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version to build (e.g. 0.4.0)"
|
||||||
|
required: true
|
||||||
|
|
||||||
|
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
|
||||||
|
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 \
|
||||||
|
libgtk-3-dev \
|
||||||
|
libayatana-appindicator3-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
|
||||||
|
|
||||||
|
echo "Extracted bundle contents (top-level):"
|
||||||
|
ls -la suwayomi-extracted/
|
||||||
|
|
||||||
|
- name: Stage Suwayomi bundle
|
||||||
|
run: |
|
||||||
|
mkdir -p src-tauri/binaries
|
||||||
|
|
||||||
|
JAVA=$(find suwayomi-extracted -path "*/jre/bin/java" | head -1)
|
||||||
|
JAR=$(find suwayomi-extracted -name "Suwayomi-Launcher.jar" | head -1)
|
||||||
|
|
||||||
|
if [ -z "$JAVA" ]; then
|
||||||
|
echo "ERROR: jre/bin/java not found. Bundle contents:"
|
||||||
|
find suwayomi-extracted -type f | head -50
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAR" ]; then
|
||||||
|
echo "ERROR: Suwayomi-Launcher.jar not found. Bundle contents:"
|
||||||
|
find suwayomi-extracted -type f | head -50
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found java: $JAVA"
|
||||||
|
echo "Found jar: $JAR"
|
||||||
|
|
||||||
|
# Copy full bundle as resources so jar + jre tree are available
|
||||||
|
# at runtime via resource_dir/suwayomi-bundle/.
|
||||||
|
cp -r suwayomi-extracted src-tauri/binaries/suwayomi-bundle
|
||||||
|
|
||||||
|
- name: Validate staging
|
||||||
|
run: |
|
||||||
|
echo "--- Validating bundle java ---"
|
||||||
|
find src-tauri/binaries/suwayomi-bundle -path "*/jre/bin/java" \
|
||||||
|
| grep -q . \
|
||||||
|
|| (echo "ERROR: jre/bin/java missing" && exit 1)
|
||||||
|
|
||||||
|
echo "--- Validating bundle jar ---"
|
||||||
|
find src-tauri/binaries/suwayomi-bundle -name "Suwayomi-Launcher.jar" \
|
||||||
|
| grep -q . \
|
||||||
|
|| (echo "ERROR: Suwayomi-Launcher.jar missing" && exit 1)
|
||||||
|
|
||||||
|
echo "Staging OK"
|
||||||
|
|
||||||
|
- name: Patch tauri.conf.json for CI
|
||||||
|
run: |
|
||||||
|
# Suppress the frontend rebuild (dist/ already built by frontend job).
|
||||||
|
sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json
|
||||||
|
|
||||||
|
# Remove externalBin entirely for Linux — we use bundled resources,
|
||||||
|
# no sidecar registration needed.
|
||||||
|
# Inject the suwayomi-bundle resource glob.
|
||||||
|
python3 - << 'PYEOF'
|
||||||
|
import json
|
||||||
|
with open("src-tauri/tauri.conf.json") as f:
|
||||||
|
conf = json.load(f)
|
||||||
|
conf["bundle"]["resources"] = ["binaries/suwayomi-bundle/**"]
|
||||||
|
conf["bundle"]["externalBin"] = []
|
||||||
|
with open("src-tauri/tauri.conf.json", "w") as f:
|
||||||
|
json.dump(conf, f, indent=2)
|
||||||
|
print("tauri.conf.json patched — resources injected, externalBin cleared")
|
||||||
|
PYEOF
|
||||||
|
|
||||||
|
- name: Build Tauri app (Linux x64)
|
||||||
|
uses: tauri-apps/tauri-action@v0
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
args: --target x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
- name: Upload AppImage
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: moku-linux-x64-appimage
|
||||||
|
path: src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/appimage/*.AppImage
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: Upload .deb
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: moku-linux-x64-deb
|
||||||
|
path: src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/deb/*.deb
|
||||||
|
retention-days: 7
|
||||||
@@ -83,49 +83,85 @@ jobs:
|
|||||||
|
|
||||||
unzip -q suwayomi-windows.zip -d suwayomi-raw
|
unzip -q suwayomi-windows.zip -d suwayomi-raw
|
||||||
|
|
||||||
TOP_DIRS=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type d)
|
- name: Extract Suwayomi bundle
|
||||||
TOP_FILES=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type f)
|
shell: bash
|
||||||
TOP_DIR_COUNT=$(echo "$TOP_DIRS" | grep -c . || true)
|
run: |
|
||||||
|
|
||||||
mkdir -p suwayomi-extracted
|
mkdir -p suwayomi-extracted
|
||||||
if [ "$TOP_DIR_COUNT" -eq 1 ] && [ -z "$TOP_FILES" ]; then
|
|
||||||
mv "$TOP_DIRS"/* suwayomi-extracted/
|
# Handle both zip layouts: single top-level dir, or files at root
|
||||||
|
TOP_DIRS=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type d | wc -l)
|
||||||
|
TOP_FILES=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type f | wc -l)
|
||||||
|
|
||||||
|
if [ "$TOP_DIRS" -eq 1 ] && [ "$TOP_FILES" -eq 0 ]; then
|
||||||
|
INNER=$(find suwayomi-raw -mindepth 1 -maxdepth 1 -type d | head -1)
|
||||||
|
cp -r "$INNER"/. suwayomi-extracted/
|
||||||
else
|
else
|
||||||
mv suwayomi-raw/* suwayomi-extracted/
|
cp -r suwayomi-raw/. suwayomi-extracted/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Extracted bundle contents (top-level):"
|
||||||
|
ls -la suwayomi-extracted/
|
||||||
|
|
||||||
- name: Stage Suwayomi bundle
|
- name: Stage Suwayomi bundle
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir -p src-tauri/binaries
|
mkdir -p src-tauri/binaries
|
||||||
|
|
||||||
JAVAW=$(find suwayomi-extracted -path "*/jre/bin/javaw.exe" | head -1)
|
JAVAW=$(find suwayomi-extracted -path "*/jre/bin/javaw.exe" | head -1)
|
||||||
|
JAR=$(find suwayomi-extracted -name "Suwayomi-Launcher.jar" | head -1)
|
||||||
|
|
||||||
if [ -z "$JAVAW" ]; then
|
if [ -z "$JAVAW" ]; then
|
||||||
echo "ERROR: could not find jre/bin/javaw.exe — bundle contents:"
|
echo "ERROR: jre/bin/javaw.exe not found. Bundle contents:"
|
||||||
find suwayomi-extracted -type f | head -40
|
find suwayomi-extracted -type f | head -50
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAR" ]; then
|
||||||
|
echo "ERROR: Suwayomi-Launcher.jar not found. Bundle contents:"
|
||||||
|
find suwayomi-extracted -type f | head -50
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Found javaw: $JAVAW"
|
echo "Found javaw: $JAVAW"
|
||||||
|
echo "Found jar: $JAR"
|
||||||
|
|
||||||
# Copy full bundle so jar + jre tree are available at runtime.
|
# Copy full bundle as resources — lib.rs invokes the bundled
|
||||||
# lib.rs looks for suwayomi-bundle/jre/bin/javaw.exe in the resource dir.
|
# java directly via resource_dir/suwayomi-bundle/jre/bin/javaw.exe.
|
||||||
cp -r suwayomi-extracted src-tauri/binaries/suwayomi-bundle
|
cp -r suwayomi-extracted src-tauri/binaries/suwayomi-bundle
|
||||||
|
|
||||||
|
- name: Validate staging
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "--- Validating bundle javaw ---"
|
||||||
|
find src-tauri/binaries/suwayomi-bundle -path "*/jre/bin/javaw.exe" \
|
||||||
|
| grep -q . \
|
||||||
|
|| (echo "ERROR: jre/bin/javaw.exe missing" && exit 1)
|
||||||
|
|
||||||
|
echo "--- Validating bundle jar ---"
|
||||||
|
find src-tauri/binaries/suwayomi-bundle -name "Suwayomi-Launcher.jar" \
|
||||||
|
| grep -q . \
|
||||||
|
|| (echo "ERROR: Suwayomi-Launcher.jar missing" && exit 1)
|
||||||
|
|
||||||
|
echo "Staging OK"
|
||||||
|
|
||||||
- name: Patch tauri.conf.json for CI
|
- name: Patch tauri.conf.json for CI
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
# Suppress the frontend rebuild (dist/ already built by frontend job).
|
||||||
sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json
|
sed -i 's/"beforeBuildCommand": "pnpm build"/"beforeBuildCommand": ""/' src-tauri/tauri.conf.json
|
||||||
# Inject the suwayomi-bundle resource so Tauri bundles it with the app.
|
|
||||||
# Done here (after staging) so the path exists when Tauri validates it.
|
# Remove externalBin entirely for Windows — we invoke the bundled
|
||||||
|
# javaw directly as a raw path, no sidecar registration needed.
|
||||||
|
# Inject the suwayomi-bundle resource glob.
|
||||||
python3 - << 'PYEOF'
|
python3 - << 'PYEOF'
|
||||||
import json
|
import json
|
||||||
with open("src-tauri/tauri.conf.json") as f:
|
with open("src-tauri/tauri.conf.json") as f:
|
||||||
conf = json.load(f)
|
conf = json.load(f)
|
||||||
conf["bundle"]["resources"] = ["binaries/suwayomi-bundle/**"]
|
conf["bundle"]["resources"] = ["binaries/suwayomi-bundle/**"]
|
||||||
|
conf["bundle"]["externalBin"] = []
|
||||||
with open("src-tauri/tauri.conf.json", "w") as f:
|
with open("src-tauri/tauri.conf.json", "w") as f:
|
||||||
json.dump(conf, f, indent=2)
|
json.dump(conf, f, indent=2)
|
||||||
|
print("tauri.conf.json patched — resources injected, externalBin cleared")
|
||||||
PYEOF
|
PYEOF
|
||||||
|
|
||||||
- name: Build Tauri app (Windows x64)
|
- name: Build Tauri app (Windows x64)
|
||||||
|
|||||||
+1
-1
@@ -38,4 +38,4 @@ src-tauri/gen/
|
|||||||
build-dir/
|
build-dir/
|
||||||
repo/
|
repo/
|
||||||
*.flatpak
|
*.flatpak
|
||||||
.flatpak-builder/
|
.flatpak-builder/\n# --- Staged sidecar binaries (platform-specific, never commit) ---\nsrc-tauri/binaries/
|
||||||
|
|||||||
@@ -10,13 +10,10 @@
|
|||||||
{
|
{
|
||||||
"identifier": "shell:allow-spawn",
|
"identifier": "shell:allow-spawn",
|
||||||
"allow": [
|
"allow": [
|
||||||
{ "name": "tachidesk-server" },
|
{ "name": "java", "args": true },
|
||||||
{ "name": "suwayomi-server" },
|
{ "name": "javaw", "args": true },
|
||||||
{ "name": "suwayomi-server-aarch64-apple-darwin" },
|
{ "name": "suwayomi-server", "args": true },
|
||||||
{ "name": "suwayomi-server-x86_64-apple-darwin" },
|
{ "name": "tachidesk-server", "args": true }
|
||||||
{ "name": "javaw", "args": true },
|
|
||||||
{ "name": "which" },
|
|
||||||
{ "name": "where" }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+94
-64
@@ -181,19 +181,37 @@ fn suwayomi_data_dir() -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ServerInvocation {
|
struct ServerInvocation {
|
||||||
bin: std::ffi::OsString,
|
// Absolute path to java/javaw (bundled JRE) or a PATH-resident binary name.
|
||||||
prefix_args: Vec<String>,
|
// All platforms use app.shell().command() — no externalBin/sidecar needed.
|
||||||
|
bin: String,
|
||||||
|
// Ordered args. rootdir_flag is inserted at position 0 by spawn_server
|
||||||
|
// so -D flags always precede -jar for the JVM.
|
||||||
|
args: Vec<String>,
|
||||||
|
// Set to the bundle dir so the jar can resolve its relative lib paths.
|
||||||
working_dir: Option<PathBuf>,
|
working_dir: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the platform-appropriate java binary inside a bundled JRE tree,
|
||||||
|
// or None if the expected path doesn't exist.
|
||||||
|
fn find_java_in_bundle(bundle_dir: &PathBuf) -> Option<PathBuf> {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let java = bundle_dir.join("jre").join("bin").join("javaw.exe");
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let java = bundle_dir.join("jre").join("bin").join("java");
|
||||||
|
|
||||||
|
if java.exists() { Some(java) } else { None }
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_server_binary(
|
fn resolve_server_binary(
|
||||||
binary: &str,
|
binary: &str,
|
||||||
app: &tauri::AppHandle,
|
app: &tauri::AppHandle,
|
||||||
) -> Result<ServerInvocation, SpawnError> {
|
) -> Result<ServerInvocation, SpawnError> {
|
||||||
|
// User-supplied explicit path — pass straight through.
|
||||||
if !binary.trim().is_empty() {
|
if !binary.trim().is_empty() {
|
||||||
return Ok(ServerInvocation {
|
return Ok(ServerInvocation {
|
||||||
bin: std::ffi::OsString::from(binary),
|
bin: binary.to_string(),
|
||||||
prefix_args: vec![],
|
args: vec![],
|
||||||
working_dir: None,
|
working_dir: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -201,71 +219,86 @@ fn resolve_server_binary(
|
|||||||
let resource_dir = app
|
let resource_dir = app
|
||||||
.path()
|
.path()
|
||||||
.resource_dir()
|
.resource_dir()
|
||||||
.map_err(|e| SpawnError::SpawnFailed(format!("Could not locate resource dir: {e}")))?;
|
.map_err(|e| SpawnError::SpawnFailed(format!("resource_dir error: {e}")))?;
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
// ── Windows & Linux: bundled JRE ─────────────────────────────────────────
|
||||||
|
// CI stages the Suwayomi linux-x64 / windows-x64 bundle as a resource at
|
||||||
|
// resource_dir/suwayomi-bundle/ (jar + JRE tree). We invoke the bundled
|
||||||
|
// java binary directly with -jar.
|
||||||
|
//
|
||||||
|
// Final arg order (rootdir_flag prepended by spawn_server):
|
||||||
|
// java -Dsuwayomi...rootDir=<path> -jar Suwayomi-Launcher.jar
|
||||||
|
//
|
||||||
|
// -D flags MUST precede -jar or the JVM silently ignores them.
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
{
|
{
|
||||||
let bundle_dir = resource_dir.join("suwayomi-bundle");
|
let bundle_dir = resource_dir.join("suwayomi-bundle");
|
||||||
let javaw = bundle_dir.join("jre").join("bin").join("javaw.exe");
|
|
||||||
let jar = bundle_dir.join("Suwayomi-Launcher.jar");
|
let jar = bundle_dir.join("Suwayomi-Launcher.jar");
|
||||||
|
|
||||||
if javaw.exists() && jar.exists() {
|
if let Some(java) = find_java_in_bundle(&bundle_dir) {
|
||||||
return Ok(ServerInvocation {
|
if jar.exists() {
|
||||||
bin: javaw.into_os_string(),
|
return Ok(ServerInvocation {
|
||||||
prefix_args: vec![
|
bin: java.to_string_lossy().into_owned(),
|
||||||
"-jar".to_string(),
|
args: vec![
|
||||||
jar.to_string_lossy().into_owned(),
|
"-jar".to_string(),
|
||||||
],
|
jar.to_string_lossy().into_owned(),
|
||||||
working_dir: Some(bundle_dir),
|
],
|
||||||
});
|
working_dir: Some(bundle_dir),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(SpawnError::NotConfigured(
|
|
||||||
"No bundled server found. Set the server path in Settings.".to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── macOS: bundled launcher script ───────────────────────────────────────
|
||||||
|
// The macOS workflow stages arch-specific .command launcher scripts as
|
||||||
|
// externalBin sidecars. They are self-contained (handle JVM invocation
|
||||||
|
// internally) so we exec the script directly with no extra args.
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let candidates = [
|
{
|
||||||
"suwayomi-server-aarch64-apple-darwin",
|
let candidates = [
|
||||||
"suwayomi-server-x86_64-apple-darwin",
|
"suwayomi-server-aarch64-apple-darwin",
|
||||||
];
|
"suwayomi-server-x86_64-apple-darwin",
|
||||||
|
"suwayomi-server",
|
||||||
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
|
];
|
||||||
let candidates = ["suwayomi-server"];
|
for name in &candidates {
|
||||||
|
let p = resource_dir.join(name);
|
||||||
#[cfg(not(target_os = "windows"))]
|
if p.exists() {
|
||||||
for name in &candidates {
|
return Ok(ServerInvocation {
|
||||||
let p = resource_dir.join(name);
|
bin: p.to_string_lossy().into_owned(),
|
||||||
if p.exists() {
|
args: vec![],
|
||||||
return Ok(ServerInvocation {
|
working_dir: None,
|
||||||
bin: p.into_os_string(),
|
});
|
||||||
prefix_args: vec![],
|
}
|
||||||
working_dir: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to PATH — covers Nix, distro packages, and any system install.
|
// ── PATH fallback (all platforms) ────────────────────────────────────────
|
||||||
// Windows always hits the early return above so this block is Linux/macOS only.
|
// Covers:
|
||||||
#[cfg(not(target_os = "windows"))]
|
// - nix develop (tachidesk-server in devShell.nativeBuildInputs)
|
||||||
for name in &["tachidesk-server", "suwayomi-server"] {
|
// - nix run .#moku (wrapProgram --prefix PATH injects tachidesk-server)
|
||||||
if std::process::Command::new("which")
|
// - Distro package installs
|
||||||
|
// - Manual system installs
|
||||||
|
//
|
||||||
|
// The Nix wrapper script accepts "$@" passthrough so the rootdir -D flag
|
||||||
|
// forwarded by spawn_server reaches the underlying JVM correctly.
|
||||||
|
for name in &["suwayomi-server", "tachidesk-server"] {
|
||||||
|
let found = std::process::Command::new("which")
|
||||||
.arg(name)
|
.arg(name)
|
||||||
.output()
|
.output()
|
||||||
.map(|o| o.status.success())
|
.map(|o| o.status.success())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false);
|
||||||
{
|
|
||||||
|
if found {
|
||||||
return Ok(ServerInvocation {
|
return Ok(ServerInvocation {
|
||||||
bin: std::ffi::OsString::from(name),
|
bin: name.to_string(),
|
||||||
prefix_args: vec![],
|
args: vec![],
|
||||||
working_dir: None,
|
working_dir: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(SpawnError::NotConfigured(
|
Err(SpawnError::NotConfigured(
|
||||||
"Server binary not found. Set the path in Settings.".to_string(),
|
"Server binary not found. Install Suwayomi-Server or set the path in Settings.".to_string(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,8 +306,7 @@ fn resolve_server_binary(
|
|||||||
fn spawn_server(binary: String, app: tauri::AppHandle) -> Result<(), SpawnError> {
|
fn spawn_server(binary: String, app: tauri::AppHandle) -> Result<(), SpawnError> {
|
||||||
{
|
{
|
||||||
let state = app.state::<ServerState>();
|
let state = app.state::<ServerState>();
|
||||||
let guard = state.0.lock().unwrap();
|
if state.0.lock().unwrap().is_some() {
|
||||||
if guard.is_some() {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,37 +314,35 @@ fn spawn_server(binary: String, app: tauri::AppHandle) -> Result<(), SpawnError>
|
|||||||
let data_dir = suwayomi_data_dir();
|
let data_dir = suwayomi_data_dir();
|
||||||
seed_server_conf(&data_dir);
|
seed_server_conf(&data_dir);
|
||||||
|
|
||||||
let invocation = resolve_server_binary(&binary, &app)?;
|
let mut invocation = resolve_server_binary(&binary, &app)?;
|
||||||
let bin_display = invocation.bin.clone();
|
let bin_display = invocation.bin.clone();
|
||||||
|
|
||||||
let rootdir_flag = format!(
|
let rootdir_flag = format!(
|
||||||
"-Dsuwayomi.tachidesk.config.server.rootDir={}",
|
"-Dsuwayomi.tachidesk.config.server.rootDir={}",
|
||||||
data_dir.to_string_lossy()
|
data_dir.to_string_lossy()
|
||||||
);
|
);
|
||||||
|
|
||||||
let args: Vec<String> = invocation.prefix_args
|
// Insert rootdir at position 0 so it always precedes -jar for the JVM.
|
||||||
.into_iter()
|
// For PATH-resident Nix wrapper scripts the flag is forwarded via "$@".
|
||||||
.chain(std::iter::once(rootdir_flag))
|
invocation.args.insert(0, rootdir_flag);
|
||||||
.collect();
|
|
||||||
|
let working_dir = invocation.working_dir
|
||||||
|
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
|
||||||
|
|
||||||
let cmd = app.shell()
|
let cmd = app.shell()
|
||||||
.command(&invocation.bin)
|
.command(&invocation.bin)
|
||||||
.env("JAVA_TOOL_OPTIONS", "-Djava.awt.headless=true")
|
.env("JAVA_TOOL_OPTIONS", "-Djava.awt.headless=true")
|
||||||
.args(&args)
|
.args(&invocation.args)
|
||||||
.current_dir(
|
.current_dir(&working_dir);
|
||||||
invocation.working_dir
|
|
||||||
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default())
|
|
||||||
);
|
|
||||||
|
|
||||||
match cmd.spawn() {
|
match cmd.spawn() {
|
||||||
Ok((_rx, child)) => {
|
Ok((_rx, child)) => {
|
||||||
println!("Spawned server: {:?}", bin_display);
|
println!("Spawned server: {}", bin_display);
|
||||||
let state = app.state::<ServerState>();
|
*app.state::<ServerState>().0.lock().unwrap() = Some(child);
|
||||||
*state.0.lock().unwrap() = Some(child);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Failed to spawn {:?}: {}", bin_display, e);
|
eprintln!("Failed to spawn {}: {}", bin_display, e);
|
||||||
Err(SpawnError::SpawnFailed(e.to_string()))
|
Err(SpawnError::SpawnFailed(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,9 @@
|
|||||||
"icons/icon.ico",
|
"icons/icon.ico",
|
||||||
"icons/icon.png"
|
"icons/icon.png"
|
||||||
],
|
],
|
||||||
|
"externalBin": [
|
||||||
|
"binaries/suwayomi-server"
|
||||||
|
],
|
||||||
"windows": {
|
"windows": {
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"installerIcon": "icons/icon.ico",
|
"installerIcon": "icons/icon.ico",
|
||||||
|
|||||||
@@ -9,5 +9,8 @@
|
|||||||
"devtools": true
|
"devtools": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"externalBin": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user