mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 22:29:24 +00:00
398 lines
17 KiB
YAML
398 lines
17 KiB
YAML
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..."
|
||
./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 (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: Sign JAR and nested native libraries
|
||
if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-13'
|
||
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 }}
|
||
run: |
|
||
echo "🔐 Signing JAR and all nested native libraries..."
|
||
cd ./frontend/src-tauri/libs
|
||
|
||
# Get the main JAR
|
||
MAIN_JAR=$(ls stirling-pdf*.jar | head -n 1)
|
||
echo "📦 Processing main JAR: $MAIN_JAR"
|
||
|
||
# Create a backup
|
||
cp "$MAIN_JAR" "${MAIN_JAR}.backup"
|
||
|
||
# Create temporary directory for signing operations
|
||
SIGNING_DIR=$(mktemp -d)
|
||
echo "🔧 Using temporary directory: $SIGNING_DIR"
|
||
|
||
# List JAR contents to find .dylib files
|
||
echo "🔍 Scanning for .dylib files in JAR..."
|
||
jar -tf "$MAIN_JAR" | grep '\.dylib$' > "$SIGNING_DIR/dylib_list.txt" || true
|
||
|
||
if [ -s "$SIGNING_DIR/dylib_list.txt" ]; then
|
||
echo "📦 Found .dylib files to sign:"
|
||
cat "$SIGNING_DIR/dylib_list.txt"
|
||
|
||
# Extract and sign each .dylib file
|
||
while IFS= read -r dylib_path; do
|
||
echo "🔐 Processing: $dylib_path"
|
||
|
||
# Extract the .dylib file
|
||
jar -xf "$MAIN_JAR" "$dylib_path"
|
||
|
||
# Sign the extracted .dylib file
|
||
codesign --force --verify --verbose --timestamp \
|
||
--options runtime \
|
||
--sign "$CERT_ID" \
|
||
"$dylib_path"
|
||
|
||
# Update the JAR with the signed .dylib file
|
||
jar -uf "$MAIN_JAR" "$dylib_path"
|
||
|
||
echo "✅ Signed and updated: $dylib_path"
|
||
|
||
# Clean up the extracted file
|
||
rm -rf "$(dirname "$dylib_path")"
|
||
|
||
done < "$SIGNING_DIR/dylib_list.txt"
|
||
|
||
echo "✅ All .dylib files signed and updated in JAR"
|
||
else
|
||
echo "ℹ️ No .dylib files found in JAR"
|
||
fi
|
||
|
||
# Clean up
|
||
rm -rf "$SIGNING_DIR"
|
||
|
||
# Validate the JAR integrity
|
||
echo "🔍 Validating JAR integrity..."
|
||
if jar -tf "$MAIN_JAR" | grep -q "META-INF/MANIFEST.MF"; then
|
||
echo "✅ JAR manifest preserved"
|
||
else
|
||
echo "❌ JAR manifest missing!"
|
||
exit 1
|
||
fi
|
||
|
||
if java -jar "$MAIN_JAR" --version >/dev/null 2>&1; then
|
||
echo "✅ JAR executable test passed"
|
||
else
|
||
echo "⚠️ JAR executable test failed (may be expected if missing dependencies)"
|
||
fi
|
||
|
||
echo "✅ JAR signing completed 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.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 }}
|
||
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: tauri-${{ 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 |