diff --git a/.github/workflows/README-tauri.md b/.github/workflows/README-tauri.md index 83b5db829..be6346045 100644 --- a/.github/workflows/README-tauri.md +++ b/.github/workflows/README-tauri.md @@ -2,14 +2,14 @@ This directory contains GitHub Actions workflows for building Tauri desktop applications for Stirling-PDF. -## Workflows +## Workflow -### 1. `tauri-build.yml` - Production Build Workflow +### `tauri-build.yml` - Unified Build Workflow -**Purpose**: Build Tauri applications for all platforms (Windows, macOS, Linux) and optionally create releases. +**Purpose**: Build Tauri applications for all platforms (Windows, macOS, Linux) with comprehensive testing and validation. **Triggers**: -- Manual dispatch with options for test mode and platform selection +- Manual dispatch with platform selection (windows, macos, linux, or all) - Pull requests affecting Tauri-related files - Pushes to main branch affecting Tauri-related files @@ -19,63 +19,36 @@ This directory contains GitHub Actions workflows for building Tauri desktop appl - **Linux**: x86_64 (deb and AppImage) **Features**: -- 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 - -**Purpose**: Test Tauri builds without creating releases - perfect for validating changes. - -**Triggers**: -- Manual dispatch with platform selection -- Pull requests affecting Tauri-related files - -**Features**: -- Allows testing specific platforms or all platforms -- 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 +- **Dynamic Platform Selection**: Choose specific platforms or build all +- **Smart JRE Bundling**: Uses JLink to create optimized custom JRE +- **Apple Code Signing**: Full macOS notarization and signing support +- **Comprehensive Validation**: Artifact verification and size checks +- **Self-Contained**: No Java installation required for end users +- **Cross-Platform**: Builds on actual target platforms for compatibility +- **Detailed Logging**: Complete build process visibility ## Usage -### Testing Before Merge +### Manual Testing 1. **Test All Platforms**: ```bash # Go to Actions tab in GitHub - # Run "Test Tauri Build" workflow + # Run "Build Tauri Applications" workflow # Select "all" for platform ``` 2. **Test Specific Platform**: ```bash # Go to Actions tab in GitHub - # Run "Test Tauri Build" workflow + # Run "Build Tauri Applications" workflow # Select specific platform (windows/macos/linux) ``` -### Production Builds - -1. **Test Mode** (recommended for PRs): - ```bash - # Go to Actions tab in GitHub - # Run "Build Tauri Applications" workflow - # Set test_mode: true - ``` - -2. **Release Mode**: - ```bash - # Go to Actions tab in GitHub - # Run "Build Tauri Applications" workflow - # Set test_mode: false - # This will create a GitHub release - ``` +3. **Automatic Testing**: + - Builds are automatically triggered on PRs and pushes + - All platforms are tested by default + - Artifacts are uploaded for download and testing ## Configuration diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index 687639ce9..5e85cf63d 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -3,15 +3,16 @@ name: Build Tauri Applications on: workflow_dispatch: inputs: - test_mode: - description: "Run in test mode (skip release step)" - required: false - default: "true" - type: boolean - target_platforms: - description: "Target platforms (comma-separated: windows,macos,linux)" - required: false - default: "windows,macos,linux" + platform: + description: "Platform to build (windows, macos, linux, or all)" + required: true + default: "all" + type: choice + options: + - all + - windows + - macos + - linux pull_request: branches: [main] paths: @@ -21,7 +22,7 @@ on: - 'frontend/package-lock.json' - '.github/workflows/tauri-build.yml' push: - branches: [main] + branches: [main, "infra/tauri-actions"] paths: - 'frontend/src-tauri/**' - 'frontend/src/**' @@ -32,31 +33,40 @@ on: permissions: contents: read -env: - TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - jobs: - build-tauri: - permissions: - contents: write + determine-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Determine build matrix + id: set-matrix + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + case "${{ github.event.inputs.platform }}" in + "windows") + echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"}]}' >> $GITHUB_OUTPUT + ;; + "macos") + echo 'matrix={"include":[{"platform":"macos-latest","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-13","args":"--target x86_64-apple-darwin","name":"macos-x86_64"}]}' >> $GITHUB_OUTPUT + ;; + "linux") + 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":"macos-13","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT + ;; + esac + else + # For PR/push events, build 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":"macos-13","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT + fi + + build: + needs: determine-matrix strategy: fail-fast: false - matrix: - include: - - platform: 'macos-latest' - args: '--target aarch64-apple-darwin' - name: 'macos-aarch64' - - platform: 'macos-latest' - args: '--target x86_64-apple-darwin' - name: 'macos-x86_64' - - platform: 'ubuntu-22.04' - args: '' - name: 'linux-x86_64' - - platform: 'windows-latest' - args: '--target x86_64-pc-windows-msvc' - name: 'windows-x86_64' - + matrix: ${{ fromJson(needs.determine-matrix.outputs.matrix) }} runs-on: ${{ matrix.platform }} steps: - name: Harden Runner @@ -74,30 +84,19 @@ jobs: sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libjavascriptcoregtk-4.0-dev libsoup2.4-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev - name: Setup Node.js - uses: actions/setup-node@0ad00a8b5b3388e41dc48b8dd2912fcdecfb8ca6 # v4.3.1 + uses: actions/setup-node@v4 with: node-version: 20 cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: Setup Rust - uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336d9b35a # stable + uses: dtolnay/rust-toolchain@stable with: toolchain: stable - targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} + targets: ${{ (matrix.platform == 'macos-latest' || matrix.platform == 'macos-13') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} - - name: Rust cache - uses: swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5 - with: - workspaces: './frontend/src-tauri -> target' - - name: Cache JLink runtime - uses: actions/cache@v4 - 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 @@ -111,8 +110,9 @@ jobs: run: | chmod +x ./gradlew echo "🔧 Building Stirling-PDF JAR..." - ./gradlew clean bootJar --no-daemon - + # STIRLING_PDF_DESKTOP_UI=false ./gradlew clean bootJar --no-daemon + ./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube + # Find the built JAR STIRLING_JAR=$(ls stirling-pdf/build/libs/stirling-pdf-*.jar | head -n 1) echo "✅ Built JAR: $STIRLING_JAR" @@ -131,7 +131,7 @@ jobs: 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" + 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.sql,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" @@ -141,26 +141,25 @@ jobs: 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) + # Create custom JRE with jlink (always rebuild) + echo "🔧 Creating custom JRE with jlink..." + echo "📋 Using modules: $MODULES" + + # Remove any existing JRE + rm -rf ./frontend/src-tauri/runtime/jre + + # 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 "🔧 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" + echo "❌ Failed to create JLink runtime" + exit 1 fi # Test the bundled runtime @@ -182,18 +181,65 @@ jobs: working-directory: ./frontend run: npm install - - name: Install Tauri CLI - working-directory: ./frontend - run: npm install -g @tauri-apps/cli@v2 + - name: Import Apple Developer Certificate + if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-13' + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + echo "Importing Apple Developer Certificate..." + echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12 + security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain + security set-keychain-settings -t 3600 -u build.keychain + security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain + security find-identity -v -p codesigning build.keychain + - name: Verify Certificate + if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-13' + run: | + echo "Verifying Apple Developer Certificate..." + CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application") + echo "Certificate Info: $CERT_INFO" + CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}') + echo "Certificate ID: $CERT_ID" + echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV + echo "Certificate imported." - - name: Build Tauri app + - name: Check DMG creation dependencies (macOS only) + if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-13' + run: | + echo "🔍 Checking DMG creation dependencies on ${{ matrix.platform }}..." + echo "hdiutil version: $(hdiutil --version || echo 'NOT FOUND')" + echo "create-dmg availability: $(which create-dmg || echo 'NOT FOUND')" + echo "Available disk space: $(df -h /tmp | tail -1)" + echo "macOS version: $(sw_vers -productVersion)" + echo "Available tools:" + ls -la /usr/bin/hd* || echo "No hd* tools found" + + - name: Build Tauri app uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.APPIMAGETOOL_SIGN_PASSPHRASE }} + SIGN: 1 + CI: true with: projectPath: ./frontend + tauriScript: npx tauri args: ${{ matrix.args }} - + - name: Rename artifacts shell: bash run: | @@ -204,7 +250,7 @@ jobs: if [ "${{ matrix.platform }}" = "windows-latest" ]; then find . -name "*.exe" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.exe" \; find . -name "*.msi" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.msi" \; - elif [ "${{ matrix.platform }}" = "macos-latest" ]; then + elif [ "${{ matrix.platform }}" = "macos-latest" ] || [ "${{ matrix.platform }}" = "macos-13" ]; then find . -name "*.dmg" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.dmg" \; find . -name "*.app" -exec cp -r {} "../../../dist/Stirling-PDF-${{ matrix.name }}.app" \; else @@ -215,109 +261,69 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: tauri-${{ matrix.name }} + name: Stirling-PDF-${{ matrix.name }} path: ./dist/* retention-days: 7 - - test-build: - needs: build-tauri - runs-on: ubuntu-latest - steps: - - name: Harden Runner - uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 - with: - egress-policy: audit - - - name: Download all artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - - - name: Display structure of downloaded files - run: ls -la */ - - - name: Verify artifacts exist + + - name: Verify build artifacts + shell: bash run: | - expected_files=( - "tauri-windows-x86_64/Stirling-PDF-windows-x86_64.exe" - "tauri-macos-aarch64/Stirling-PDF-macos-aarch64.dmg" - "tauri-macos-x86_64/Stirling-PDF-macos-x86_64.dmg" - "tauri-linux-x86_64/Stirling-PDF-linux-x86_64.deb" - ) + cd ./frontend/src-tauri/target - missing_files=() - for file in "${expected_files[@]}"; do - if [ ! -f "$file" ]; then - missing_files+=("$file") + # Check for expected artifacts based on platform + if [ "${{ matrix.platform }}" = "windows-latest" ]; then + echo "Checking for Windows artifacts..." + find . -name "*.exe" -o -name "*.msi" | head -5 + if [ $(find . -name "*.exe" | wc -l) -eq 0 ]; then + echo "❌ No Windows executable found" + exit 1 + fi + elif [ "${{ matrix.platform }}" = "macos-latest" ] || [ "${{ matrix.platform }}" = "macos-13" ]; then + echo "Checking for macOS artifacts..." + find . -name "*.dmg" -o -name "*.app" | head -5 + if [ $(find . -name "*.dmg" -o -name "*.app" | wc -l) -eq 0 ]; then + echo "❌ No macOS artifacts found" + exit 1 + fi + else + echo "Checking for Linux artifacts..." + find . -name "*.deb" -o -name "*.AppImage" | head -5 + if [ $(find . -name "*.deb" -o -name "*.AppImage" | wc -l) -eq 0 ]; then + echo "❌ No Linux artifacts found" + exit 1 fi - done - - if [ ${#missing_files[@]} -gt 0 ]; then - echo "ERROR: Missing expected artifacts:" - printf '%s\n' "${missing_files[@]}" - exit 1 fi - echo "✅ All expected artifacts are present" + echo "✅ Build artifacts found for ${{ matrix.name }}" - - name: Check artifact sizes + - name: Test artifact sizes + shell: bash run: | - echo "Artifact sizes:" - find . -name "*.exe" -o -name "*.dmg" -o -name "*.deb" -o -name "*.AppImage" | while read file; do - size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "unknown") - echo "$file: $size bytes" + cd ./frontend/src-tauri/target + echo "Artifact sizes for ${{ matrix.name }}:" + find . -name "*.exe" -o -name "*.dmg" -o -name "*.deb" -o -name "*.AppImage" -o -name "*.msi" | while read file; do + if [ -f "$file" ]; then + size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "unknown") + echo "$file: $size bytes" + # Check if file is suspiciously small (less than 1MB) + if [ "$size" != "unknown" ] && [ "$size" -lt 1048576 ]; then + echo "⚠️ Warning: $file is smaller than 1MB" + fi + fi done - create-release: - if: github.event_name != 'workflow_dispatch' || github.event.inputs.test_mode != 'true' - needs: [build-tauri, test-build] + report: + needs: build runs-on: ubuntu-latest - permissions: - contents: write + if: always() steps: - - name: Harden Runner - uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 - with: - egress-policy: audit - - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Download all artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - - - name: Get version from package.json - id: version + - name: Report build results run: | - VERSION=$(grep '"version"' frontend/package.json | cut -d'"' -f4) - echo "VERSION=$VERSION" >> $GITHUB_OUTPUT - - - name: Create Release - uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2 - with: - tag_name: v${{ steps.version.outputs.VERSION }}-tauri - name: Stirling-PDF Tauri v${{ steps.version.outputs.VERSION }} - body: | - # Stirling-PDF Tauri Desktop Applications - - This release contains desktop applications built with Tauri for: - - Windows x86_64 - - macOS Apple Silicon (ARM64) - - macOS Intel (x86_64) - - Linux x86_64 - - ## Installation - - ### Windows - - Download `Stirling-PDF-windows-x86_64.exe` for a portable executable - - Download `Stirling-PDF-windows-x86_64.msi` for an installer - - ### macOS - - Download `Stirling-PDF-macos-aarch64.dmg` for Apple Silicon Macs - - Download `Stirling-PDF-macos-x86_64.dmg` for Intel Macs - - ### Linux - - Download `Stirling-PDF-linux-x86_64.deb` for Debian/Ubuntu - - Download `Stirling-PDF-linux-x86_64.AppImage` for universal Linux - draft: false - prerelease: true - files: | - tauri-*/* \ No newline at end of file + if [ "${{ needs.build.result }}" = "success" ]; then + echo "✅ All Tauri builds completed successfully!" + echo "Artifacts are ready for distribution." + else + echo "❌ Some Tauri builds failed." + echo "Please check the logs and fix any issues." + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/tauri-test.yml b/.github/workflows/tauri-test.yml deleted file mode 100644 index f5da2864d..000000000 --- a/.github/workflows/tauri-test.yml +++ /dev/null @@ -1,331 +0,0 @@ -name: Test Tauri Build - -on: - workflow_dispatch: - inputs: - platform: - description: "Platform to test (windows, macos, linux, or all)" - required: true - default: "all" - type: choice - options: - - all - - windows - - macos - - linux - pull_request: - branches: [main] - paths: - - 'frontend/src-tauri/**' - - 'frontend/src/**' - - 'frontend/package.json' - - 'frontend/package-lock.json' - - '.github/workflows/tauri-test.yml' - - '.github/workflows/tauri-build.yml' - push: - branches: [main, "infra/tauri-actions"] - paths: - - 'frontend/src-tauri/**' - - 'frontend/src/**' - - 'frontend/package.json' - - 'frontend/package-lock.json' - - '.github/workflows/tauri-test.yml' - - '.github/workflows/tauri-build.yml' - -permissions: - contents: read - -jobs: - determine-matrix: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - name: Determine build matrix - id: set-matrix - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - case "${{ github.event.inputs.platform }}" in - "windows") - echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"}]}' >> $GITHUB_OUTPUT - ;; - "macos") - echo 'matrix={"include":[{"platform":"macos-latest","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-13","args":"--target x86_64-apple-darwin","name":"macos-x86_64"}]}' >> $GITHUB_OUTPUT - ;; - "linux") - 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":"macos-13","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"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":"macos-13","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT - fi - - test-build: - needs: determine-matrix - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.determine-matrix.outputs.matrix) }} - runs-on: ${{ matrix.platform }} - steps: - - name: Harden Runner - uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 - with: - egress-policy: audit - - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Install dependencies (ubuntu only) - if: matrix.platform == 'ubuntu-22.04' - run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libjavascriptcoregtk-4.0-dev libsoup2.4-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: 'npm' - cache-dependency-path: frontend/package-lock.json - - - name: Setup Rust - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - targets: ${{ (matrix.platform == 'macos-latest' || matrix.platform == 'macos-13') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} - - - - - name: Set up JDK 21 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - java-version: "21" - distribution: "temurin" - - - name: Build Java backend with JLink - working-directory: ./ - shell: bash - run: | - chmod +x ./gradlew - echo "🔧 Building Stirling-PDF JAR..." - # STIRLING_PDF_DESKTOP_UI=false ./gradlew clean bootJar --no-daemon - ./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube - - # 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.sql,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 (always rebuild) - echo "🔧 Creating custom JRE with jlink..." - echo "📋 Using modules: $MODULES" - - # Remove any existing JRE - rm -rf ./frontend/src-tauri/runtime/jre - - # 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 - - # 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 - - - name: Install frontend dependencies - working-directory: ./frontend - run: npm install - - - name: Import Apple Developer Certificate - if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-13' - env: - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} - run: | - echo "Importing Apple Developer Certificate..." - echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12 - security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain - security default-keychain -s build.keychain - security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain - security set-keychain-settings -t 3600 -u build.keychain - security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain - security find-identity -v -p codesigning build.keychain - - name: Verify Certificate - if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-13' - run: | - echo "Verifying Apple Developer Certificate..." - CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application") - echo "Certificate Info: $CERT_INFO" - CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}') - echo "Certificate ID: $CERT_ID" - echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV - echo "Certificate imported." - - - name: Check DMG creation dependencies (macOS only) - if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-13' - run: | - echo "🔍 Checking DMG creation dependencies on ${{ matrix.platform }}..." - echo "hdiutil version: $(hdiutil --version || echo 'NOT FOUND')" - echo "create-dmg availability: $(which create-dmg || echo 'NOT FOUND')" - echo "Available disk space: $(df -h /tmp | tail -1)" - echo "macOS version: $(sw_vers -productVersion)" - echo "Available tools:" - ls -la /usr/bin/hd* || echo "No hd* tools found" - - - name: Build Tauri app - uses: tauri-apps/tauri-action@v0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }} - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - APPLE_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.APPIMAGETOOL_SIGN_PASSPHRASE }} - SIGN: 1 - CI: true - with: - projectPath: ./frontend - tauriScript: npx tauri - args: ${{ matrix.args }} - - - name: Rename artifacts - shell: bash - run: | - mkdir -p ./dist - cd ./frontend/src-tauri/target - - # Find and rename artifacts based on platform - if [ "${{ matrix.platform }}" = "windows-latest" ]; then - find . -name "*.exe" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.exe" \; - find . -name "*.msi" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.msi" \; - elif [ "${{ matrix.platform }}" = "macos-latest" ] || [ "${{ matrix.platform }}" = "macos-13" ]; then - find . -name "*.dmg" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.dmg" \; - find . -name "*.app" -exec cp -r {} "../../../dist/Stirling-PDF-${{ matrix.name }}.app" \; - else - find . -name "*.deb" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.deb" \; - find . -name "*.AppImage" -exec cp {} "..../../dist/Stirling-PDF-${{ matrix.name }}.AppImage" \; - fi - - - name: Upload artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: Stirling-PDF-${{ matrix.name }} - path: ./dist/* - retention-days: 7 - - - name: Verify build artifacts - shell: bash - run: | - cd ./frontend/src-tauri/target - - # Check for expected artifacts based on platform - if [ "${{ matrix.platform }}" = "windows-latest" ]; then - echo "Checking for Windows artifacts..." - find . -name "*.exe" -o -name "*.msi" | head -5 - if [ $(find . -name "*.exe" | wc -l) -eq 0 ]; then - echo "❌ No Windows executable found" - exit 1 - fi - elif [ "${{ matrix.platform }}" = "macos-latest" ] || [ "${{ matrix.platform }}" = "macos-13" ]; then - echo "Checking for macOS artifacts..." - find . -name "*.dmg" -o -name "*.app" | head -5 - if [ $(find . -name "*.dmg" -o -name "*.app" | wc -l) -eq 0 ]; then - echo "❌ No macOS artifacts found" - exit 1 - fi - else - echo "Checking for Linux artifacts..." - find . -name "*.deb" -o -name "*.AppImage" | head -5 - if [ $(find . -name "*.deb" -o -name "*.AppImage" | wc -l) -eq 0 ]; then - echo "❌ No Linux artifacts found" - exit 1 - fi - fi - - echo "✅ Build artifacts found for ${{ matrix.name }}" - - - name: Test artifact sizes - shell: bash - run: | - cd ./frontend/src-tauri/target - echo "Artifact sizes for ${{ matrix.name }}:" - find . -name "*.exe" -o -name "*.dmg" -o -name "*.deb" -o -name "*.AppImage" -o -name "*.msi" | while read file; do - if [ -f "$file" ]; then - size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "unknown") - echo "$file: $size bytes" - # Check if file is suspiciously small (less than 1MB) - if [ "$size" != "unknown" ] && [ "$size" -lt 1048576 ]; then - echo "⚠️ Warning: $file is smaller than 1MB" - fi - fi - done - - report-results: - needs: test-build - runs-on: ubuntu-latest - if: always() - steps: - - name: Report test results - run: | - if [ "${{ needs.test-build.result }}" = "success" ]; then - echo "✅ All Tauri build tests passed successfully!" - echo "The build action is ready to be merged." - else - echo "❌ Some Tauri build tests failed." - echo "Please check the logs and fix any issues before merging." - exit 1 - fi \ No newline at end of file