diff --git a/.github/workflows/multiOSReleases.yml b/.github/workflows/multiOSReleases.yml index 1e4a18f91d..720c6629af 100644 --- a/.github/workflows/multiOSReleases.yml +++ b/.github/workflows/multiOSReleases.yml @@ -1,4 +1,4 @@ -name: Test Installers Build +name: Multi-OS Tauri Releases on: workflow_dispatch: @@ -7,6 +7,20 @@ on: description: "Run in test mode (skip release step)" required: false default: "false" + type: choice + options: + - "true" + - "false" + platform: + description: "Platform to build (windows, macos, linux, or all)" + required: true + default: "all" + type: choice + options: + - all + - windows + - macos + - linux release: types: [created] @@ -14,41 +28,50 @@ permissions: contents: read jobs: - read_versions: + determine-matrix: runs-on: ubuntu-latest outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} version: ${{ steps.versionNumber.outputs.versionNumber }} - versionMac: ${{ steps.versionNumberMac.outputs.versionNumberMac }} steps: - name: Harden Runner uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Set up JDK - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 - with: - distribution: 'temurin' - java-version: '21' - - # ✅ Get version from Gradle - name: Get version number id: versionNumber run: | - VERSION=$(./gradlew printVersion --quiet | tail -1) + VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}') echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT - # ✅ Get Mac-specific version from Gradle - - name: Get version number mac - id: versionNumberMac + - name: Determine build matrix + id: set-matrix run: | - VERSION_MAC=$(./gradlew printMacVersion --quiet | tail -1) - echo "versionNumberMac=$VERSION_MAC" >> $GITHUB_OUTPUT + 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-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-15-intel","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-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT + ;; + esac + else + # For release events, build all platforms + echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT + fi - build-portable: - needs: read_versions + build-jars: + needs: determine-matrix runs-on: ubuntu-latest strategy: matrix: @@ -64,10 +87,10 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up JDK 21 - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 with: java-version: "21" distribution: "temurin" @@ -76,36 +99,405 @@ jobs: with: gradle-version: 8.14 - - name: Generate jar (Disable Security=${{ matrix.disable_security }}) - run: ./gradlew clean createExe + - name: Build JAR + run: ./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube env: DISABLE_ADDITIONAL_FEATURES: ${{ matrix.disable_security }} STIRLING_PDF_DESKTOP_UI: false - - name: Rename binaries + - name: Rename JAR run: | - mkdir ./binaries - mv ./build/launch4j/Stirling-PDF.exe ./binaries/win-Stirling-PDF-portable-Server${{ matrix.file_suffix }}.exe - mv ./app/core/build/libs/stirling-pdf-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar + mkdir -p ./jar-dist + cp app/core/build/libs/stirling-pdf-${{ needs.determine-matrix.outputs.version }}.jar ./jar-dist/Stirling-PDF${{ matrix.file_suffix }}.jar + + - name: Upload JAR artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: jar${{ matrix.file_suffix }} + path: ./jar-dist/*.jar + retention-days: 1 + + build: + needs: determine-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.determine-matrix.outputs.matrix) }} + runs-on: ${{ matrix.platform }} + env: + SM_API_KEY: ${{ secrets.SM_API_KEY }} + WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} + 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-15' || matrix.platform == 'macos-15-intel') && '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..." + ./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube + + # Find the built JAR + STIRLING_JAR=$(ls app/core/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 + 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 + + # DigiCert KeyLocker Setup (Cloud HSM) + - name: Setup DigiCert KeyLocker + id: digicert-setup + if: ${{ matrix.platform == 'windows-latest' && env.SM_API_KEY != '' && github.event_name == 'release' }} + uses: digicert/ssm-code-signing@v1.1.0 + env: + SM_API_KEY: ${{ secrets.SM_API_KEY }} + SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }} + SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} + SM_KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS }} + SM_HOST: ${{ secrets.SM_HOST }} + + - name: Setup DigiCert KeyLocker Certificate + if: ${{ matrix.platform == 'windows-latest' && env.SM_API_KEY != '' && github.event_name == 'release' }} + shell: pwsh + run: | + Write-Host "Setting up DigiCert KeyLocker environment..." + + # Decode client certificate + $certBytes = [Convert]::FromBase64String("${{ secrets.SM_CLIENT_CERT_FILE_B64 }}") + $certPath = "D:\Certificate_pkcs12.p12" + [IO.File]::WriteAllBytes($certPath, $certBytes) + + # Set environment variables + echo "SM_CLIENT_CERT_FILE=D:\Certificate_pkcs12.p12" >> $env:GITHUB_ENV + echo "SM_HOST=${{ secrets.SM_HOST }}" >> $env:GITHUB_ENV + echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> $env:GITHUB_ENV + echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> $env:GITHUB_ENV + echo "SM_KEYPAIR_ALIAS=${{ secrets.SM_KEYPAIR_ALIAS }}" >> $env:GITHUB_ENV + + # Get PKCS11 config path from DigiCert action + $pkcs11Config = $env:PKCS11_CONFIG + if ($pkcs11Config) { + Write-Host "Found PKCS11_CONFIG: $pkcs11Config" + echo "PKCS11_CONFIG=$pkcs11Config" >> $env:GITHUB_ENV + } else { + Write-Host "PKCS11_CONFIG not set by DigiCert action, using default path" + $defaultPath = "C:\Users\RUNNER~1\AppData\Local\Temp\smtools-windows-x64\pkcs11properties.cfg" + if (Test-Path $defaultPath) { + Write-Host "Found config at default path: $defaultPath" + echo "PKCS11_CONFIG=$defaultPath" >> $env:GITHUB_ENV + } else { + Write-Host "Warning: Could not find PKCS11 config file" + } + } + + # Traditional PFX Certificate Import (fallback if KeyLocker not configured) + - name: Import Windows Code Signing Certificate + if: ${{ matrix.platform == 'windows-latest' && env.SM_API_KEY == '' && github.event_name == 'release' }} + env: + WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} + WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} + shell: powershell + run: | + if ($env:WINDOWS_CERTIFICATE) { + Write-Host "Importing Windows Code Signing Certificate..." + + # Decode base64 certificate and save to file + $certBytes = [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE) + $certPath = Join-Path $env:RUNNER_TEMP "certificate.pfx" + [IO.File]::WriteAllBytes($certPath, $certBytes) + + # Import certificate to CurrentUser\My store + $cert = Import-PfxCertificate -FilePath $certPath -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force) + + # Extract and set thumbprint as environment variable + $thumbprint = $cert.Thumbprint + Write-Host "Certificate imported with thumbprint: $thumbprint" + echo "WINDOWS_CERTIFICATE_THUMBPRINT=$thumbprint" >> $env:GITHUB_ENV + + # Clean up certificate file + Remove-Item $certPath + + Write-Host "Windows certificate import completed." + } else { + Write-Host "⚠️ WINDOWS_CERTIFICATE secret not set - building unsigned binary" + } + + - name: Import Apple Developer Certificate + if: (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && github.event_name == 'release' + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + echo "Importing Apple Developer Certificate..." + echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12 + # Create temporary keychain + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + # Import certificate + security import certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + # Clean up + rm certificate.p12 + + - name: Verify Certificate + if: (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && github.event_name == 'release' + run: | + echo "Verifying Apple Developer Certificate..." + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + CERT_INFO=$(security find-identity -v -p codesigning $KEYCHAIN_PATH | grep "Developer ID Application") + echo "Certificate Info: $CERT_INFO" + CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}') + echo "Certificate ID: $CERT_ID" + echo "APPLE_SIGNING_IDENTITY=$CERT_ID" >> $GITHUB_ENV + echo "Certificate imported successfully." + + - 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.APPLE_SIGNING_IDENTITY }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.APPIMAGETOOL_SIGN_PASSPHRASE }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY: ${{ secrets.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY }} + VITE_SAAS_SERVER_URL: ${{ secrets.VITE_SAAS_SERVER_URL }} + # Only enable Windows signing in Tauri when on release + SIGN: ${{ github.event_name == 'release' && (env.SM_API_KEY == '' && env.WINDOWS_CERTIFICATE != '') && '1' || '0' }} + CI: true + with: + projectPath: ./frontend + tauriScript: npx tauri + args: ${{ matrix.args }} + + # Sign with DigiCert KeyLocker (post-build) + - name: Sign Windows binaries with DigiCert KeyLocker + if: ${{ matrix.platform == 'windows-latest' && env.SM_API_KEY != '' && github.event_name == 'release' }} + shell: pwsh + run: | + Write-Host "=== DigiCert KeyLocker Signing ===" + + # Test smctl connectivity first + Write-Host "Testing smctl connection..." + $healthCheck = & smctl healthcheck 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "[SUCCESS] Connected to DigiCert KeyLocker" + } else { + Write-Host "[ERROR] Failed to connect to DigiCert KeyLocker" + Write-Host $healthCheck + exit 1 + } + Write-Host "" + + # Sync certificates to Windows certificate store + Write-Host "Syncing certificates to Windows certificate store..." + $syncOutput = & smctl windows certsync 2>&1 + Write-Host "Cert sync result: $syncOutput" + Write-Host "" + + # Find only the files we need to sign + $filesToSign = @() + + # Main application executable + $mainExe = Get-ChildItem -Path "./frontend/src-tauri/target/x86_64-pc-windows-msvc/release" -Filter "stirling-pdf.exe" -File -ErrorAction SilentlyContinue + if ($mainExe) { $filesToSign += $mainExe } + + # MSI installer + $msiFiles = Get-ChildItem -Path "./frontend/src-tauri/target" -Filter "*.msi" -Recurse -File + $filesToSign += $msiFiles + + if ($filesToSign.Count -eq 0) { + Write-Host "[ERROR] No files found to sign" + exit 1 + } + + Write-Host "Found $($filesToSign.Count) files to sign:" + foreach ($f in $filesToSign) { Write-Host " - $($f.Name)" } + Write-Host "" + + $signedCount = 0 + foreach ($file in $filesToSign) { + Write-Host "Signing: $($file.Name)" + + # Get PKCS11 config file path + $pkcs11Config = $env:PKCS11_CONFIG + if (-not $pkcs11Config) { + Write-Host "[ERROR] PKCS11_CONFIG environment variable not set" + exit 1 + } + + Write-Host "Using PKCS11 config: $pkcs11Config" + + # Try signing with certificate fingerprint first (if available) + $fingerprint = "${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" + if ($fingerprint -and $fingerprint -ne "") { + Write-Host "Attempting to sign with certificate fingerprint..." + $output = & smctl sign --fingerprint "$fingerprint" --input "$($file.FullName)" --config-file "$pkcs11Config" --verbose 2>&1 + $exitCode = $LASTEXITCODE + } else { + Write-Host "No fingerprint provided, using keypair alias..." + $output = & smctl sign --keypair-alias "${{ secrets.SM_KEYPAIR_ALIAS }}" --input "$($file.FullName)" --config-file "$pkcs11Config" --verbose 2>&1 + $exitCode = $LASTEXITCODE + } + + Write-Host "Exit code: $exitCode" + Write-Host "Output: $output" + + if ($output -match "FAILED" -or $output -match "error" -or $output -match "Error") { + Write-Host "[ERROR] Signing failed for $($file.Name)" + exit 1 + } + + if ($exitCode -ne 0) { + Write-Host "[ERROR] Failed to sign $($file.Name)" + Write-Host "Full error output:" + Write-Host $output + exit 1 + } + + $signedCount++ + Write-Host "[SUCCESS] Signed: $($file.Name)" + Write-Host "" + } + + Write-Host "=== Summary ===" + Write-Host "[SUCCESS] Signed $signedCount/$($filesToSign.Count) files successfully" + + - 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-15" ] || [ "${{ matrix.platform }}" = "macos-15-intel" ]; 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 build artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: + name: Stirling-PDF-${{ matrix.name }} + path: ./dist/* retention-days: 1 - if-no-files-found: error - name: stirling${{ matrix.file_suffix }}-binaries - path: | - ./binaries/* - sign_verify-portable: - needs: [build-portable, read_versions] + sign_verify_jars: + needs: build-jars runs-on: ubuntu-latest + if: success() strategy: matrix: disable_security: [true, false] include: - disable_security: false - file_suffix: "with-login-" + file_suffix: "-with-login" - disable_security: true file_suffix: "" steps: @@ -114,188 +506,136 @@ jobs: with: egress-policy: audit - - name: Download build artifacts + - name: Download JAR artifacts uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: - name: stirling-${{ matrix.file_suffix }}binaries - - - name: Display structure of downloaded files - run: ls -R - - - name: Upload signed artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - retention-days: 1 - if-no-files-found: error - name: stirling-${{ matrix.file_suffix }}signed - path: | - ./* - !cosign.* - - build-installers: - needs: read_versions - strategy: - matrix: - include: - - os: windows-latest - platform: win- - - os: macos-latest - platform: mac- - # - os: ubuntu-latest - # platform: linux- - runs-on: ${{ matrix.os }} - permissions: - contents: write - steps: - - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - egress-policy: audit - - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Set up JDK 21 - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 - with: - java-version: "21" - distribution: "temurin" - - - uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 - with: - gradle-version: 8.14 - - # Install Windows dependencies - - name: Install WiX Toolset - if: matrix.os == 'windows-latest' - run: | - curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe - .\wix.exe /install /quiet - - # Build installer - - name: Build Installer - run: ./gradlew build jpackage -x test --info - env: - DISABLE_ADDITIONAL_FEATURES: true - STIRLING_PDF_DESKTOP_UI: true - BROWSER_OPEN: true - - - name: Set up JDK (x86_64) - if: matrix.os == 'macos-latest' - run: | - curl -L -o jdk.tar.gz https://cdn.azul.com/zulu/bin/zulu17.56.15-ca-jdk17.0.14-macosx_x64.tar.gz - mkdir -p zulu17 - tar -xzf jdk.tar.gz -C zulu17 --strip-components=1 - echo "JAVA_HOME=$PWD/zulu17" >> $GITHUB_ENV - echo "$PWD/zulu17/bin" >> $GITHUB_PATH - - - name: Verify JDK architecture - if: matrix.os == 'macos-latest' - run: file $JAVA_HOME/bin/java - - - name: Build project and run jpackage (x86_64) - if: matrix.os == 'macos-latest' - run: arch -x86_64 ./gradlew jpackageMacX64 - - # Rename and collect artifacts based on OS - - name: Prepare artifacts - id: prepare - shell: bash - run: | - ls -lah ./build/jpackage/ - mkdir ./binaries - if [ "${{ matrix.os }}" = "windows-latest" ]; then - mv "./build/jpackage/Stirling PDF-${{ needs.read_versions.outputs.version }}.exe" "./binaries/Stirling-PDF-win-installer.exe" - elif [ "${{ matrix.os }}" = "macos-latest" ]; then - mv "./build/jpackage/Stirling PDF-${{ needs.read_versions.outputs.versionMac }}.dmg" "./binaries/Stirling-PDF-mac-installer.dmg" - mv "./build/jpackage/x86_64/Stirling PDF (x86_64)-${{ needs.read_versions.outputs.versionMac }}.dmg" "./binaries/Stirling-PDF-mac-x86_64-installer.dmg" - else - mv "./build/jpackage/stirling-pdf_${{ needs.read_versions.outputs.version }}-1_amd64.deb" "./binaries/Stirling-PDF-linux-installer.deb" - fi - - - name: Display structure of downloaded files - run: ls -R ./binaries - - - name: Upload build artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - retention-days: 1 - if-no-files-found: error - name: ${{ matrix.platform }}binaries - path: | - ./binaries/* - - sign_verify: - needs: [read_versions, build-installers] - strategy: - matrix: - include: - - os: windows-latest - platform: win- - - os: macos-latest - platform: mac- - # - os: ubuntu-latest - # platform: linux- - runs-on: ubuntu-latest - steps: - - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - egress-policy: audit - - - name: Download build artifacts - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 - with: - name: ${{ matrix.platform }}binaries + name: jar${{ matrix.file_suffix }} - name: Display structure of downloaded files run: ls -R - name: Install Cosign - if: matrix.os == 'windows-latest' uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 - name: Generate key pair - if: matrix.os == 'windows-latest' run: cosign generate-key-pair - - name: Sign and generate attestations - if: matrix.os == 'windows-latest' + - name: Sign and generate attestations for JAR + shell: bash run: | - cosign sign-blob \ - --key ./cosign.key \ - --yes \ - --output-signature ./Stirling-PDF-win-installer.exe.sig \ - ./Stirling-PDF-win-installer.exe + for file in *.jar; do + if [ -f "$file" ]; then + echo "Signing: $file" - cosign attest-blob \ - --predicate - \ - --key ./cosign.key \ - --yes \ - --output-attestation ./Stirling-PDF-win-installer.exe.intoto.jsonl \ - ./Stirling-PDF-win-installer.exe + # Sign the artifact + cosign sign-blob \ + --key ./cosign.key \ + --yes \ + --output-signature "${file}.sig" \ + "$file" - cosign verify-blob \ - --key ./cosign.pub \ - --signature ./Stirling-PDF-win-installer.exe.sig \ - ./Stirling-PDF-win-installer.exe + # Generate attestation + cosign attest-blob \ + --predicate - \ + --key ./cosign.key \ + --yes \ + --output-attestation "${file}.intoto.jsonl" \ + "$file" + + # Verify the signature + cosign verify-blob \ + --key ./cosign.pub \ + --signature "${file}.sig" \ + "$file" + + echo "✅ Signed and verified: $file" + fi + done + + - name: Upload signed JAR artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: jar${{ matrix.file_suffix }}-signed + path: | + * + !cosign.key + !cosign.pub + retention-days: 1 + + sign_verify: + needs: build + runs-on: ubuntu-latest + if: success() + strategy: + fail-fast: false + matrix: + name: [windows-x86_64, macos-aarch64, macos-x86_64, linux-x86_64] + steps: + - name: Harden Runner + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 + with: + egress-policy: audit + + - name: Download build artifacts + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: Stirling-PDF-${{ matrix.name }} - name: Display structure of downloaded files run: ls -R + - name: Install Cosign + uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 + + - name: Generate key pair + run: cosign generate-key-pair + + - name: Sign and generate attestations + shell: bash + run: | + # Sign all artifacts for this platform + for file in *; do + if [ -f "$file" ] && [[ ! "$file" =~ \.(sig|intoto\.jsonl)$ ]]; then + echo "Signing: $file" + + # Sign the artifact + cosign sign-blob \ + --key ./cosign.key \ + --yes \ + --output-signature "${file}.sig" \ + "$file" + + # Generate attestation + cosign attest-blob \ + --predicate - \ + --key ./cosign.key \ + --yes \ + --output-attestation "${file}.intoto.jsonl" \ + "$file" + + # Verify the signature + cosign verify-blob \ + --key ./cosign.pub \ + --signature "${file}.sig" \ + "$file" + + echo "✅ Signed and verified: $file" + fi + done + - name: Upload signed artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - retention-days: 1 - if-no-files-found: error - name: ${{ matrix.platform }}signed + name: Stirling-PDF-${{ matrix.name }}-signed path: | - ./Stirling-PDF-${{ matrix.platform }}installer.* - ./Stirling-PDF-${{ matrix.platform }}x86_64-installer.* - !cosign.* + * + !cosign.key + !cosign.pub + retention-days: 1 create-release: if: github.event_name != 'workflow_dispatch' || github.event.inputs.test_mode != 'true' - needs: [read_versions, sign_verify, sign_verify-portable] + needs: [determine-matrix, build, sign_verify, sign_verify_jars] runs-on: ubuntu-latest permissions: contents: write @@ -305,14 +645,27 @@ jobs: with: egress-policy: audit - - name: Download signed artifacts + - name: Download all signed Tauri artifacts uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + pattern: Stirling-PDF-*-signed + path: ./artifacts + + - name: Download all signed JAR artifacts + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + pattern: jar*-signed + path: ./artifacts/jars + - name: Display structure of downloaded files - run: ls -R - - name: Upload binaries, attestations and signatures to Release and create GitHub Release + run: ls -R ./artifacts + + - name: Upload binaries, attestations and signatures to Release uses: softprops/action-gh-release@62c96d0c4e8a889135c1f3a25910db8dbe0e85f7 # v2.3.4 with: - tag_name: v${{ needs.read_versions.outputs.version }} + tag_name: v${{ needs.determine-matrix.outputs.version }} generate_release_notes: true files: | - ./*signed/* + ./artifacts/**/* + draft: false + prerelease: false