Compare commits

...
Author SHA1 Message Date
Ludy 44671d8ddb Merge branch 'main' into fix_win_desktop_task 2026-08-19 08:52:15 +02:00
Ludy 38051e6d59 Merge branch 'main' into fix_win_desktop_task 2026-08-08 12:57:12 +02:00
Ludy a56c02f292 Merge branch 'main' into fix_win_desktop_task 2026-07-22 09:08:42 +02:00
Ludy 4343f1cc48 Merge branch 'main' into fix_win_desktop_task 2026-07-19 21:42:35 +02:00
Ludy 9c9a1c8641 Merge branch 'main' into fix_win_desktop_task 2026-07-16 16:40:10 +02:00
Ludy 23029ac3ec Merge branch 'main' into fix_win_desktop_task 2026-07-16 15:51:43 +02:00
Ludy 9a142bd462 Merge branch 'main' into fix_win_desktop_task 2026-07-09 23:16:03 +02:00
Ludy f2b3871448 Merge branch 'main' into fix_win_desktop_task 2026-07-09 11:05:25 +02:00
Ludy 7800fb1b60 Merge branch 'main' into fix_win_desktop_task 2026-07-07 11:46:09 +02:00
Ludy 2815b2b535 Merge branch 'main' into fix_win_desktop_task 2026-07-06 15:36:49 +02:00
Ludy 999079e6ba Merge branch 'main' into fix_win_desktop_task 2026-07-06 14:02:22 +02:00
Ludy ff13d7360a Merge branch 'main' into fix_win_desktop_task 2026-07-06 13:52:37 +02:00
Ludy87 e91426e371 Remove configurable JRE path from verification
The bundled JRE verification script previously accepted a custom path via command-line argument, but this parameter was never used. Hardcoding the path simplifies the script, removes the validation checks, and eliminates unnecessary complexity.
2026-07-06 10:00:56 +02:00
Ludy87 ca739e14b0 Harden JRE verification and jlink modules
Tighten JRE/runtime handling and jlink safety.

- frontend/editor/scripts/verify-bundled-jre.mjs: import path.resolve, compute expected release path (src-tauri/runtime/jre/release) and refuse to read any unexpected release file path to avoid accidental/malicious file reads.
- scripts/build-jlink-runtime.ps1: add an allowed jlink module whitelist, sanitize/split the $Modules input, require at least one module, validate requested modules against the whitelist, and pass a safe ModulesArg to --add-modules to prevent injection or unsupported modules.
2026-07-06 09:38:15 +02:00
Ludy87 153dde8291 Add Windows support to desktop JAR copy task
Split the JAR copy command into platform-specific implementations. Unix systems (linux/darwin) use `cp`, while Windows uses PowerShell's Copy-Item with proper directory creation and glob handling. This fixes the desktop build on Windows by replacing the Unix-only `cp` command with a compatible PowerShell equivalent.
2026-07-06 09:28:07 +02:00
Ludy87 ca48361d26 Improve desktop build config and Windows support
Rename CACHE to JLINK_REUSE_CACHE for clarity about JAR reuse. Fix desktop:dev:login task to properly pass SECURITY_ENABLELOGIN as env var. Improve Windows compatibility in verify-bundled-jre.mjs by using cmd.exe as Task launcher. Rename desktop_enable_login_flag() to desktop_login_enabled_from_env() for clarity. Add documentation for desktop dev modes and override options. Add comments explaining compression fallback and file permissions handling.
2026-07-06 09:19:15 +02:00
Ludy87 f7c60e1fb4 Add desktop login support and improve jlink build
- Extract Windows jlink runtime building into dedicated PowerShell script (build-jlink-runtime.ps1) to improve maintainability and permission handling
- Add SECURITY_ENABLELOGIN environment variable support in Tauri backend to conditionally enable desktop login
- Enhance JRE verification script to automatically rebuild runtime if missing or outdated
- Add env section and new dev:login task to desktop.yml
- Add cache control (CACHE variable) to optionally remove build artifacts before rebuilding
- Improve error handling and user feedback in verify-bundled-jre.mjs with helper functions
2026-07-06 09:01:23 +02:00
5 changed files with 224 additions and 26 deletions
+43 -18
View File
@@ -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"
+24
View File
@@ -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
+70 -7
View File
@@ -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
];
+70
View File
@@ -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)
}