diff --git a/.github/workflows/README-tauri.md b/.github/workflows/README-tauri.md index 34668ff22..0ab3823f0 100644 --- a/.github/workflows/README-tauri.md +++ b/.github/workflows/README-tauri.md @@ -19,11 +19,13 @@ This directory contains GitHub Actions workflows for building Tauri desktop appl - **Linux**: x86_64 (deb and AppImage) **Features**: -- Builds Java backend first +- Builds Java backend with custom JRE using JLink +- Creates self-contained applications (no Java installation required) - Installs all dependencies - Creates signed artifacts (if signing keys are configured) - Validates all artifacts are created successfully - Can create GitHub releases (when not in test mode) +- Optimized runtime with only required Java modules ### 2. `tauri-test.yml` - Test Workflow @@ -35,9 +37,10 @@ This directory contains GitHub Actions workflows for building Tauri desktop appl **Features**: - Allows testing specific platforms or all platforms -- Validates build artifacts are created -- Checks artifact sizes +- Validates build artifacts are created with custom JRE +- Checks artifact sizes and runtime optimization - Reports results without creating releases +- Caches JLink runtime for faster subsequent builds ## Usage @@ -135,6 +138,25 @@ Both workflows include comprehensive validation: 2. **Artifact Inspection**: Download artifacts to verify contents 3. **Local Testing**: Test builds locally before running workflows +## JLink Integration Benefits + +The workflows now use JLink to create custom Java runtimes: + +### **Self-Contained Applications** +- **No Java Required**: Users don't need Java installed +- **Consistent Runtime**: Same Java version across all deployments +- **Smaller Size**: Only includes needed Java modules (~30-50MB vs full JRE) + +### **Security & Performance** +- **Minimal Attack Surface**: Only required modules included +- **Faster Startup**: Optimized runtime with stripped debug info +- **Better Compression**: JLink level 2 compression reduces size + +### **Module Analysis** +- **Automatic Detection**: Uses `jdeps` to analyze JAR dependencies +- **Fallback Safety**: Predefined module list if analysis fails +- **Platform Optimized**: Different modules per platform if needed + ## Integration with Existing Workflows These workflows are designed to complement the existing build system: @@ -142,6 +164,7 @@ These workflows are designed to complement the existing build system: - Uses same JDK and Gradle setup as `build.yml` - Follows same security practices as `multiOSReleases.yml` - Compatible with existing release processes +- Integrates JLink logic from `build-tauri-jlink.sh/bat` scripts ## Next Steps diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index cb78958e6..0e85147d0 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -91,17 +91,90 @@ jobs: with: workspaces: './frontend/src-tauri -> target' + - name: Cache JLink runtime + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + with: + path: ./frontend/src-tauri/runtime/jre + key: jlink-runtime-${{ runner.os }}-jdk21-${{ hashFiles('stirling-pdf/build.gradle') }} + restore-keys: | + jlink-runtime-${{ runner.os }}-jdk21- + - name: Set up JDK 21 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: java-version: "21" distribution: "temurin" - - name: Build Java backend + - name: Build Java backend with JLink working-directory: ./ + shell: bash run: | chmod +x ./gradlew - ./gradlew clean build -x test + echo "🔧 Building Stirling-PDF JAR..." + ./gradlew clean bootJar --no-daemon + + # Find the built JAR + STIRLING_JAR=$(ls stirling-pdf/build/libs/Stirling-PDF-*.jar | head -n 1) + echo "✅ Built JAR: $STIRLING_JAR" + + # Create Tauri directories + mkdir -p ./frontend/src-tauri/libs + mkdir -p ./frontend/src-tauri/runtime + + # Copy JAR to Tauri libs + cp "$STIRLING_JAR" ./frontend/src-tauri/libs/ + echo "✅ JAR copied to Tauri libs" + + # Analyze JAR dependencies for jlink modules + echo "🔍 Analyzing JAR dependencies..." + if command -v jdeps &> /dev/null; then + DETECTED_MODULES=$(jdeps --print-module-deps --ignore-missing-deps "$STIRLING_JAR" 2>/dev/null || echo "") + if [ -n "$DETECTED_MODULES" ]; then + echo "📋 jdeps detected modules: $DETECTED_MODULES" + MODULES="$DETECTED_MODULES,java.compiler,java.instrument,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.transaction.xa,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" + else + echo "⚠️ jdeps analysis failed, using predefined modules" + MODULES="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" + fi + else + echo "⚠️ jdeps not available, using predefined modules" + MODULES="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" + fi + + # Create custom JRE with jlink (if not cached) + if [ ! -d "./frontend/src-tauri/runtime/jre" ]; then + echo "🔧 Creating custom JRE with jlink..." + echo "📋 Using modules: $MODULES" + + # Create the custom JRE + jlink \ + --add-modules "$MODULES" \ + --strip-debug \ + --compress=2 \ + --no-header-files \ + --no-man-pages \ + --output ./frontend/src-tauri/runtime/jre + + if [ ! -d "./frontend/src-tauri/runtime/jre" ]; then + echo "❌ Failed to create JLink runtime" + exit 1 + fi + else + echo "✅ Using cached JLink runtime" + fi + + # Test the bundled runtime + if [ -f "./frontend/src-tauri/runtime/jre/bin/java" ]; then + RUNTIME_VERSION=$(./frontend/src-tauri/runtime/jre/bin/java --version 2>&1 | head -n 1) + echo "✅ Custom JRE created successfully: $RUNTIME_VERSION" + else + echo "❌ Custom JRE executable not found" + exit 1 + fi + + # Calculate runtime size + RUNTIME_SIZE=$(du -sh ./frontend/src-tauri/runtime/jre | cut -f1) + echo "📊 Custom JRE size: $RUNTIME_SIZE" env: DISABLE_ADDITIONAL_FEATURES: true @@ -109,11 +182,6 @@ jobs: working-directory: ./frontend run: npm ci - - name: Copy backend JAR to Tauri resources - run: | - mkdir -p ./frontend/src-tauri/libs - cp ./stirling-pdf/build/libs/stirling-pdf-*.jar ./frontend/src-tauri/libs - - name: Build Tauri app uses: tauri-apps/tauri-action@6fdd37473788d5a2b4dd80e7ccc0b3c7fe8a5bcd # v0.5.7 env: diff --git a/.github/workflows/tauri-test.yml b/.github/workflows/tauri-test.yml index 5f80fc0a9..ffbeb1cf7 100644 --- a/.github/workflows/tauri-test.yml +++ b/.github/workflows/tauri-test.yml @@ -56,12 +56,12 @@ jobs: echo 'matrix={"include":[{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT ;; *) - echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-latest","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"ubuntu-20.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT + echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-latest","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT ;; esac else # For PR/push events, test all platforms - echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-latest","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"ubuntu-20.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT + echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-latest","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT fi test-build: @@ -103,17 +103,90 @@ jobs: with: workspaces: './frontend/src-tauri -> target' + - name: Cache JLink runtime + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + with: + path: ./frontend/src-tauri/runtime/jre + key: jlink-runtime-${{ runner.os }}-jdk21-${{ hashFiles('stirling-pdf/build.gradle') }} + restore-keys: | + jlink-runtime-${{ runner.os }}-jdk21- + - name: Set up JDK 21 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: java-version: "21" distribution: "temurin" - - name: Build Java backend + - name: Build Java backend with JLink working-directory: ./ + shell: bash run: | chmod +x ./gradlew - ./gradlew clean build -x test + echo "🔧 Building Stirling-PDF JAR..." + ./gradlew clean bootJar --no-daemon + + # Find the built JAR + STIRLING_JAR=$(ls stirling-pdf/build/libs/Stirling-PDF-*.jar | head -n 1) + echo "✅ Built JAR: $STIRLING_JAR" + + # Create Tauri directories + mkdir -p ./frontend/src-tauri/libs + mkdir -p ./frontend/src-tauri/runtime + + # Copy JAR to Tauri libs + cp "$STIRLING_JAR" ./frontend/src-tauri/libs/ + echo "✅ JAR copied to Tauri libs" + + # Analyze JAR dependencies for jlink modules + echo "🔍 Analyzing JAR dependencies..." + if command -v jdeps &> /dev/null; then + DETECTED_MODULES=$(jdeps --print-module-deps --ignore-missing-deps "$STIRLING_JAR" 2>/dev/null || echo "") + if [ -n "$DETECTED_MODULES" ]; then + echo "📋 jdeps detected modules: $DETECTED_MODULES" + MODULES="$DETECTED_MODULES,java.compiler,java.instrument,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.transaction.xa,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported" + else + echo "⚠️ jdeps analysis failed, using predefined modules" + MODULES="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" + fi + else + echo "⚠️ jdeps not available, using predefined modules" + MODULES="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" + fi + + # Create custom JRE with jlink (if not cached) + if [ ! -d "./frontend/src-tauri/runtime/jre" ]; then + echo "🔧 Creating custom JRE with jlink..." + echo "📋 Using modules: $MODULES" + + # Create the custom JRE + jlink \ + --add-modules "$MODULES" \ + --strip-debug \ + --compress=2 \ + --no-header-files \ + --no-man-pages \ + --output ./frontend/src-tauri/runtime/jre + + if [ ! -d "./frontend/src-tauri/runtime/jre" ]; then + echo "❌ Failed to create JLink runtime" + exit 1 + fi + else + echo "✅ Using cached JLink runtime" + fi + + # Test the bundled runtime + if [ -f "./frontend/src-tauri/runtime/jre/bin/java" ]; then + RUNTIME_VERSION=$(./frontend/src-tauri/runtime/jre/bin/java --version 2>&1 | head -n 1) + echo "✅ Custom JRE created successfully: $RUNTIME_VERSION" + else + echo "❌ Custom JRE executable not found" + exit 1 + fi + + # Calculate runtime size + RUNTIME_SIZE=$(du -sh ./frontend/src-tauri/runtime/jre | cut -f1) + echo "📊 Custom JRE size: $RUNTIME_SIZE" env: DISABLE_ADDITIONAL_FEATURES: true @@ -121,11 +194,6 @@ jobs: working-directory: ./frontend run: npm ci - - name: Copy backend JAR to Tauri resources - run: | - mkdir -p ./frontend/src-tauri/libs - cp ./stirling-pdf/build/libs/stirling-pdf-*.jar ./frontend/src-tauri/libs - - name: Build Tauri app (test mode) uses: tauri-apps/tauri-action@v0.5 env: