mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44671d8ddb | ||
|
|
38051e6d59 | ||
|
|
a56c02f292 | ||
|
|
4343f1cc48 | ||
|
|
9c9a1c8641 | ||
|
|
23029ac3ec | ||
|
|
9a142bd462 | ||
|
|
f2b3871448 | ||
|
|
7800fb1b60 | ||
|
|
2815b2b535 | ||
|
|
999079e6ba | ||
|
|
ff13d7360a | ||
|
|
e91426e371 | ||
|
|
ca739e14b0 | ||
|
|
153dde8291 | ||
|
|
ca48361d26 | ||
|
|
f7c60e1fb4 |
+43
-18
@@ -27,6 +27,14 @@ vars:
|
||||
esac
|
||||
fi
|
||||
|
||||
env:
|
||||
# Desktop builds default to the smallest useful backend surface unless a task
|
||||
# explicitly opts into extra features or login.
|
||||
DISABLE_ADDITIONAL_FEATURES: '{{.DISABLE_ADDITIONAL_FEATURES | default "true"}}'
|
||||
SECURITY_ENABLELOGIN: '{{.SECURITY_ENABLELOGIN | default "false"}}'
|
||||
# Reuse the previously built backend JAR unless a clean rebuild is requested.
|
||||
JLINK_REUSE_CACHE: '{{.JLINK_REUSE_CACHE | default "true"}}'
|
||||
|
||||
tasks:
|
||||
prepare:
|
||||
desc: "Prepare desktop build dependencies"
|
||||
@@ -51,6 +59,19 @@ tasks:
|
||||
cmds:
|
||||
- npx tauri dev --no-watch
|
||||
|
||||
dev:login:
|
||||
desc: "Start Tauri desktop dev mode with login enabled and cache reuse"
|
||||
deps:
|
||||
- task: prepare
|
||||
vars:
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
ignore_error: true
|
||||
dir: editor
|
||||
env:
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
cmds:
|
||||
- npx tauri dev --no-watch
|
||||
|
||||
build:
|
||||
desc: "Build Tauri desktop app (production)"
|
||||
deps: [prepare]
|
||||
@@ -107,7 +128,7 @@ tasks:
|
||||
|
||||
jlink:
|
||||
desc: "Build backend JAR and create JLink runtime for Tauri"
|
||||
deps: [jlink:jar, jlink:runtime]
|
||||
deps: [jlink:cacheremove, jlink:jar, jlink:runtime]
|
||||
# Runs after the runtime is in place. Lives here (not in jlink:runtime's
|
||||
# cmds) so it still fires when jlink:runtime short-circuits on its `status:`
|
||||
# check and reuses an existing runtime/jre - that reuse path is exactly how
|
||||
@@ -116,27 +137,40 @@ tasks:
|
||||
- task: jlink:verify
|
||||
|
||||
jlink:verify:
|
||||
desc: "Fail the build if the bundled JRE is older than the app JAR requires"
|
||||
desc: "Verify the bundled JRE exists and matches the app JAR's required Java version"
|
||||
dir: editor
|
||||
env:
|
||||
REQUIRED_JAVA: "{{.REQUIRED_JAVA}}"
|
||||
cmds:
|
||||
- node scripts/verify-bundled-jre.mjs src-tauri/runtime/jre/release
|
||||
- node scripts/verify-bundled-jre.mjs
|
||||
|
||||
jlink:cacheremove:
|
||||
desc: "Remove cached backend JARs before a clean desktop rebuild"
|
||||
dir: ..
|
||||
if: '{{eq .JLINK_REUSE_CACHE "false"}}'
|
||||
cmds:
|
||||
- cmd: rm -rf app/core/build/libs
|
||||
- 'echo "rm -rf app/core/build/libs"'
|
||||
- task: jlink:clean
|
||||
|
||||
jlink:jar:
|
||||
desc: "Build backend JAR for Tauri bundling (host-OS natives only by default)"
|
||||
run: once
|
||||
dir: ..
|
||||
env:
|
||||
DISABLE_ADDITIONAL_FEATURES: "true"
|
||||
DISABLE_ADDITIONAL_FEATURES: '{{.DISABLE_ADDITIONAL_FEATURES}}'
|
||||
SECURITY_ENABLELOGIN: '{{.SECURITY_ENABLELOGIN}}'
|
||||
cmds:
|
||||
- echo "Building bootJar with JPDFium natives for {{.JPDFIUM_PLATFORMS}}"
|
||||
- cmd: cmd /c gradlew.bat bootJar --no-daemon -PjpdfiumPlatforms={{.JPDFIUM_PLATFORMS}}
|
||||
- 'echo "Building bootJar with JPDFium natives for {{.JPDFIUM_PLATFORMS}}"'
|
||||
- cmd: 'cmd /c "gradlew.bat bootJar --no-daemon -PjpdfiumPlatforms={{.JPDFIUM_PLATFORMS}}"'
|
||||
platforms: [windows]
|
||||
- cmd: ./gradlew bootJar --no-daemon -PjpdfiumPlatforms={{.JPDFIUM_PLATFORMS}}
|
||||
- cmd: './gradlew bootJar --no-daemon -PjpdfiumPlatforms={{.JPDFIUM_PLATFORMS}}'
|
||||
platforms: [linux, darwin]
|
||||
- mkdir -p frontend/editor/src-tauri/libs
|
||||
- cp app/core/build/libs/stirling-pdf-*.jar frontend/editor/src-tauri/libs/
|
||||
- cmd: cp app/core/build/libs/stirling-pdf-*.jar frontend/editor/src-tauri/libs/
|
||||
platforms: [linux, darwin]
|
||||
- cmd: powershell -NoProfile -ExecutionPolicy Bypass -Command "New-Item -ItemType Directory -Force -Path 'frontend/editor/src-tauri/libs' | Out-Null; Copy-Item -Force (Get-ChildItem 'app/core/build/libs/stirling-pdf-*.jar' | Select-Object -First 1) 'frontend/editor/src-tauri/libs'"
|
||||
platforms: [windows]
|
||||
status:
|
||||
- test -f frontend/editor/src-tauri/libs/stirling-pdf-*.jar
|
||||
|
||||
@@ -154,16 +188,7 @@ tasks:
|
||||
# jdk.crypto.mscapi (the Windows certificate store / SunMSCAPI provider, used by
|
||||
# hardware-backed cert signing) is a Windows-only module - it only exists in a Windows
|
||||
# JDK's jmods, so it is added on Windows only or jlink fails to resolve it elsewhere.
|
||||
- cmd: |
|
||||
JLINK="${JAVA_HOME:+$JAVA_HOME/bin/}jlink"
|
||||
JLINK_COMPRESS="$("$JLINK" --help 2>&1 | grep -q 'zip-\[0-9\]' && echo zip-6 || echo 2)"
|
||||
"$JLINK" \
|
||||
--add-modules {{.JLINK_MODULES}},jdk.crypto.mscapi \
|
||||
--strip-debug \
|
||||
--compress="$JLINK_COMPRESS" \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--output runtime/jre
|
||||
- cmd: powershell -NoProfile -ExecutionPolicy Bypass -File ../../../scripts/build-jlink-runtime.ps1 -Modules "{{.JLINK_MODULES}}"
|
||||
platforms: [windows]
|
||||
- cmd: |
|
||||
JLINK="${JAVA_HOME:+$JAVA_HOME/bin/}jlink"
|
||||
|
||||
@@ -50,6 +50,26 @@ task desktop:dev
|
||||
|
||||
This ensures the JLink runtime and backend JAR exist (skipping if already built), then starts Tauri in dev mode.
|
||||
|
||||
For local login testing, use the login-enabled variant:
|
||||
|
||||
```bash
|
||||
task desktop:dev:login
|
||||
```
|
||||
|
||||
That task reuses the same desktop launcher, but it keeps the backend cache and enables the login flow through the desktop taskfile defaults.
|
||||
|
||||
You can override the desktop task defaults inline:
|
||||
|
||||
- `JLINK_REUSE_CACHE=false` - force a clean backend JAR/JRE rebuild before launch
|
||||
- `DISABLE_ADDITIONAL_FEATURES=false` - keep the full backend feature set for the bundled desktop run
|
||||
- `SECURITY_ENABLELOGIN=true` - enable the normal login flow in desktop mode
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
task desktop:dev:login JLINK_REUSE_CACHE=false DISABLE_ADDITIONAL_FEATURES=false SECURITY_ENABLELOGIN=true
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
@@ -78,6 +98,10 @@ task desktop:jlink:runtime # Create JLink custom JRE only
|
||||
task desktop:jlink:clean # Remove JLink artifacts
|
||||
```
|
||||
|
||||
`task desktop:jlink` verifies the bundled runtime after it is built. If the
|
||||
runtime release file is missing or too old, the verification script will
|
||||
rebuild `desktop:jlink:runtime` automatically and re-check the version.
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
|
||||
@@ -9,24 +9,87 @@
|
||||
//
|
||||
// Required major comes from REQUIRED_JAVA (wired from .taskfiles/desktop.yml,
|
||||
// which mirrors build.gradle `modernJavaVersion`). Keep them in sync.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { resolve } from "node:path";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const required = Number(process.env.REQUIRED_JAVA ?? "25");
|
||||
const releasePath = process.argv[2] ?? "runtime/jre/release";
|
||||
const releasePath = resolve(process.cwd(), "src-tauri/runtime/jre/release");
|
||||
|
||||
function rebuildRuntime(reason) {
|
||||
console.warn(
|
||||
`${reason} Rebuilding runtime with 'task desktop:jlink:runtime'.`,
|
||||
);
|
||||
|
||||
const isWindows = process.platform === "win32";
|
||||
// Node does not reliably spawn the Task .cmd shim directly on Windows in
|
||||
// this repo environment, so use cmd.exe as the stable trampoline.
|
||||
const command = isWindows ? "cmd.exe" : "task";
|
||||
const args = isWindows
|
||||
? ["/d", "/s", "/c", "task desktop:jlink:runtime"]
|
||||
: ["desktop:jlink:runtime"];
|
||||
const result = spawnSync(command, args, {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
console.error(
|
||||
`FATAL: failed to launch 'task desktop:jlink:runtime': ${result.error.message}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error(
|
||||
`FATAL: 'task desktop:jlink:runtime' exited with status ${result.status}.`,
|
||||
);
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
function readReleaseFile() {
|
||||
return readFileSync(releasePath, "utf8");
|
||||
}
|
||||
|
||||
function readReleaseOrFail() {
|
||||
try {
|
||||
return readReleaseFile();
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`FATAL: cannot read bundled JRE release file at "${releasePath}" after rebuilding: ${err.message}.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function parseMajor(raw) {
|
||||
// jlink writes release metadata as plain text, so we can validate the
|
||||
// embedded runtime without starting Java.
|
||||
const match = raw.match(/JAVA_VERSION="?(\d+)/);
|
||||
return match ? Number(match[1]) : 0;
|
||||
}
|
||||
|
||||
let raw;
|
||||
try {
|
||||
raw = readFileSync(releasePath, "utf8");
|
||||
raw = readReleaseFile();
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`FATAL: cannot read bundled JRE release file at "${releasePath}": ${err.message}. ` +
|
||||
`Is the runtime built? Run 'task desktop:jlink'.`,
|
||||
`WARN: cannot read bundled JRE release file at "${releasePath}": ${err.message}.`,
|
||||
);
|
||||
process.exit(1);
|
||||
rebuildRuntime("Bundled runtime is missing.");
|
||||
raw = readReleaseOrFail();
|
||||
}
|
||||
|
||||
const match = raw.match(/JAVA_VERSION="?(\d+)/);
|
||||
const major = match ? Number(match[1]) : 0;
|
||||
let major = parseMajor(raw);
|
||||
if (!major || major < required) {
|
||||
// Rebuild stale or too-old runtimes automatically so desktop launches can
|
||||
// recover from a cached JRE instead of failing at app start.
|
||||
rebuildRuntime(
|
||||
`Bundled runtime/jre is Java ${major || "unknown"} but the app JAR requires Java ${required}.`,
|
||||
);
|
||||
raw = readReleaseOrFail();
|
||||
major = parseMajor(raw);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Bundled JRE major: ${major || "unknown"} (required >= ${required})`,
|
||||
|
||||
@@ -54,6 +54,18 @@ fn check_backend_status() -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allow desktop launches to opt into the normal login flow when either the
|
||||
/// new desktop-specific flag or the existing SECURITY_ENABLELOGIN flag is set.
|
||||
fn desktop_login_enabled_from_env() -> bool {
|
||||
matches!(
|
||||
std::env::var("STIRLING_PDF_DESKTOP_ENABLE_LOGIN")
|
||||
.or_else(|_| std::env::var("SECURITY_ENABLELOGIN"))
|
||||
.ok()
|
||||
.as_deref(),
|
||||
Some("true" | "TRUE" | "True" | "1" | "yes" | "YES" | "on" | "ON")
|
||||
)
|
||||
}
|
||||
|
||||
// Find the bundled JRE and return the java executable path
|
||||
fn find_bundled_jre(resource_dir: &PathBuf) -> Result<PathBuf, String> {
|
||||
let jre_dir = resource_dir.join("runtime").join("jre");
|
||||
@@ -213,7 +225,11 @@ fn run_stirling_pdf_jar(app: &tauri::AppHandle, java_path: &PathBuf, jar_path: &
|
||||
// No reverse proxy in front of the local sidecar, so don't trust forwarded headers.
|
||||
// Stops a LAN caller spoofing X-Forwarded-For to defeat the desktop-only signing gate.
|
||||
"-Dserver.forward-headers-strategy=none",
|
||||
"-Dsecurity.enableLogin=false", // Disable login for desktop mode
|
||||
if desktop_login_enabled_from_env() {
|
||||
"-Dsecurity.enableLogin=true"
|
||||
} else {
|
||||
"-Dsecurity.enableLogin=false"
|
||||
},
|
||||
"-Dsecurity.csrfDisabled=true", // Disable CSRF for desktop mode
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Modules
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$AllowedModules = @(
|
||||
"java.base",
|
||||
"java.compiler",
|
||||
"java.desktop",
|
||||
"java.instrument",
|
||||
"java.logging",
|
||||
"java.management",
|
||||
"java.naming",
|
||||
"java.net.http",
|
||||
"java.prefs",
|
||||
"java.rmi",
|
||||
"java.scripting",
|
||||
"java.security.jgss",
|
||||
"java.security.sasl",
|
||||
"java.sql",
|
||||
"java.transaction.xa",
|
||||
"java.xml",
|
||||
"java.xml.crypto",
|
||||
"jdk.crypto.ec",
|
||||
"jdk.crypto.cryptoki",
|
||||
"jdk.unsupported",
|
||||
"jdk.dynalink"
|
||||
)
|
||||
|
||||
$RequestedModules = @($Modules -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
if ($RequestedModules.Count -eq 0) {
|
||||
throw "At least one jlink module is required."
|
||||
}
|
||||
|
||||
$InvalidModules = $RequestedModules | Where-Object {
|
||||
($_ -notmatch '^[A-Za-z0-9_.]+$') -or ($_ -notin $AllowedModules)
|
||||
}
|
||||
if ($InvalidModules) {
|
||||
throw "Unsupported jlink module(s): $($InvalidModules -join ', ')"
|
||||
}
|
||||
|
||||
$ModulesArg = $RequestedModules -join ","
|
||||
|
||||
$Jlink = if ($env:JAVA_HOME) {
|
||||
Join-Path $env:JAVA_HOME "bin/jlink.exe"
|
||||
} else {
|
||||
"jlink.exe"
|
||||
}
|
||||
|
||||
$HelpText = & $Jlink --help 2>&1 | Out-String
|
||||
# Older JDKs do not support the newer zip compressor selector, so fall back to
|
||||
# the numeric compression level they do understand.
|
||||
$Compress = if ($HelpText -match "zip-\[0-9\]") { "zip-6" } else { "2" }
|
||||
|
||||
& $Jlink `
|
||||
--add-modules "$ModulesArg,jdk.crypto.mscapi" `
|
||||
--strip-debug `
|
||||
--compress="$Compress" `
|
||||
--no-header-files `
|
||||
--no-man-pages `
|
||||
--output runtime/jre
|
||||
|
||||
# Tauri's resource staging preserves source permissions, so the bundled runtime
|
||||
# must be writable here or the next incremental build can fail on read-only JRE
|
||||
# files copied into the target directory.
|
||||
Get-ChildItem -Recurse runtime/jre -Force -File | ForEach-Object {
|
||||
$_.Attributes = $_.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
|
||||
}
|
||||
Reference in New Issue
Block a user