mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-01 20:10:35 +01:00
Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.12.1 to 2.14.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/step-security/harden-runner/releases">step-security/harden-runner's releases</a>.</em></p> <blockquote> <h2>v2.14.0</h2> <h2>What's Changed</h2> <ul> <li>Selective installation: Harden-Runner now skips installation on GitHub-hosted runners when the repository has a custom property skip_harden_runner, allowing organizations to opt out specific repos.</li> <li>Avoid double install: The action no longer installs Harden-Runner if it’s already present on a GitHub-hosted runner, which could happen when a composite action also installs it.</li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/step-security/harden-runner/compare/v2.13.3...v2.14.0">https://github.com/step-security/harden-runner/compare/v2.13.3...v2.14.0</a></p> <h2>v2.13.3</h2> <h2>What's Changed</h2> <ul> <li>Fixed an issue where process events were not uploaded in certain edge cases.</li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/step-security/harden-runner/compare/v2.13.2...v2.13.3">https://github.com/step-security/harden-runner/compare/v2.13.2...v2.13.3</a></p> <h2>v2.13.2</h2> <h2>What's Changed</h2> <ul> <li>Fixed an issue where there was a limit of 512 allowed endpoints when using block egress policy. This restriction has been removed, allowing for an unlimited number of endpoints to be configured.</li> <li>Harden Runner now automatically detects if the agent is already pre-installed on a custom VM image used by a GitHub-hosted runner. When detected, the action will skip reinstallation and use the existing agent.</li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/step-security/harden-runner/compare/v2.13.1...v2.13.2">https://github.com/step-security/harden-runner/compare/v2.13.1...v2.13.2</a></p> <h2>v2.13.1</h2> <h2>What's Changed</h2> <ul> <li> <p>Graceful handling of HTTP errors: Improved error handling when fetching Harden Runner policies from the StepSecurity Policy Store API, ensuring more reliable execution even in case of temporary network/API issues.</p> </li> <li> <p>Security updates for npm dependencies: Updated vulnerable npm package dependencies to the latest secure versions.</p> </li> <li> <p>Faster enterprise agent downloads: The enterprise agent is now downloaded from GitHub Releases instead of packages.stepsecurity.io, improving download speed and reliability.</p> </li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/step-security/harden-runner/compare/v2.13.0...v2.13.1">https://github.com/step-security/harden-runner/compare/v2.13.0...v2.13.1</a></p> <h2>v2.13.0</h2> <h2>What's Changed</h2> <ul> <li>Improved job markdown summary</li> <li>Https monitoring for all domains (included with the enterprise tier)</li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/step-security/harden-runner/compare/v2...v2.13.0">https://github.com/step-security/harden-runner/compare/v2...v2.13.0</a></p> <h2>v2.12.2</h2> <h2>What's Changed</h2> <p>Added HTTPS Monitoring for additional destinations - *.githubusercontent.com Bug fixes:</p> <ul> <li>Implicitly allow local multicast, local unicast and broadcast IP addresses in block mode</li> <li>Increased policy map size for block mode</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="20cf305ff2"><code>20cf305</code></a> Merge pull request <a href="https://redirect.github.com/step-security/harden-runner/issues/622">#622</a> from step-security/feature/custom-property-skip</li> <li><a href="c51e8eeb6c"><code>c51e8ee</code></a> feat: skip agent install and post step on subsequent runs for GitHub-hosted r...</li> <li><a href="e152b90204"><code>e152b90</code></a> feat: skip harden-runner based on repository custom property</li> <li><a href="ee1faec052"><code>ee1faec</code></a> feat: replace skip-harden-runner with skip-on-custom-property input</li> <li><a href="1dc7c17646"><code>1dc7c17</code></a> feat: add skip-harden-runner input to conditionally skip execution</li> <li><a href="df199fb7be"><code>df199fb</code></a> Merge pull request <a href="https://redirect.github.com/step-security/harden-runner/issues/620">#620</a> from step-security/rc-29</li> <li><a href="03d096a772"><code>03d096a</code></a> update agent</li> <li><a href="40901073af"><code>4090107</code></a> fix: update agent</li> <li><a href="95d9a5deda"><code>95d9a5d</code></a> Merge pull request <a href="https://redirect.github.com/step-security/harden-runner/issues/606">#606</a> from step-security/rc-28</li> <li><a href="87e429d3fb"><code>87e429d</code></a> Update limitations.md</li> <li>Additional commits viewable in <a href="https://github.com/step-security/harden-runner/compare/v2.12.1...20cf305ff2072d973412fa9b1e3a4f227bda3c76">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
646 lines
28 KiB
YAML
646 lines
28 KiB
YAML
name: Build Tauri Applications
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
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, V2-tauri-windows]
|
|
paths:
|
|
- "frontend/src-tauri/**"
|
|
- "frontend/src/desktop/**"
|
|
- "frontend/tsconfig.desktop.json"
|
|
- ".github/workflows/tauri-build.yml"
|
|
push:
|
|
branches: [main]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
determine-matrix:
|
|
if: ${{ vars.CI_PROFILE != 'lite' }}
|
|
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-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 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-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:
|
|
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@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
|
|
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: 22
|
|
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..."
|
|
# 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 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 (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 ci
|
|
|
|
# DigiCert KeyLocker Setup (Cloud HSM)
|
|
- name: Setup DigiCert KeyLocker
|
|
id: digicert-setup
|
|
if: ${{ matrix.platform == 'windows-latest' && env.SM_API_KEY != '' && github.ref == 'refs/heads/main' }}
|
|
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.ref == 'refs/heads/main' }}
|
|
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.ref == 'refs/heads/main' }}
|
|
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'
|
|
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'
|
|
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: Check DMG creation dependencies (macOS only)
|
|
if: matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel'
|
|
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.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 main
|
|
SIGN: ${{ github.ref == 'refs/heads/main' && (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.ref == 'refs/heads/main' }}
|
|
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 ""
|
|
|
|
# List available certificates and check if they have certificates attached
|
|
Write-Host "Checking for available certificates..."
|
|
$certList = & smctl keypair ls 2>&1
|
|
Write-Host "Keypair list output:"
|
|
Write-Host $certList
|
|
Write-Host ""
|
|
|
|
# Parse the output to check certificate status
|
|
$lines = $certList -split "`n"
|
|
$foundKeypair = $false
|
|
$hasCertificate = $false
|
|
|
|
foreach ($line in $lines) {
|
|
if ($line -match "${{ secrets.SM_KEYPAIR_ALIAS }}") {
|
|
$foundKeypair = $true
|
|
Write-Host "[SUCCESS] Found keypair in list"
|
|
|
|
# Check if this line has certificate info (not just empty spaces after alias)
|
|
$parts = $line -split "\s+"
|
|
if ($parts.Count -gt 2 -and $parts[1] -ne "" -and $parts[1] -ne "CERTIFICATE") {
|
|
$hasCertificate = $true
|
|
Write-Host "[SUCCESS] Certificate is associated with keypair"
|
|
}
|
|
}
|
|
}
|
|
|
|
if (-not $foundKeypair) {
|
|
Write-Host "[ERROR] Keypair not found: ${{ secrets.SM_KEYPAIR_ALIAS }}"
|
|
Write-Host "Available keypairs are listed above"
|
|
Write-Host ""
|
|
Write-Host "Please verify:"
|
|
Write-Host " 1. Keypair alias is correct in GitHub secret"
|
|
Write-Host " 2. API key has access to this keypair"
|
|
exit 1
|
|
}
|
|
|
|
if (-not $hasCertificate) {
|
|
Write-Host "[ERROR] No certificate associated with keypair"
|
|
Write-Host "This usually means:"
|
|
Write-Host " 1. Certificate not yet synced to KeyLocker (run sync manually)"
|
|
Write-Host " 2. Certificate is pending approval"
|
|
Write-Host " 3. Certificate needs to be attached to the keypair"
|
|
Write-Host ""
|
|
Write-Host "Try running in DigiCert ONE portal:"
|
|
Write-Host " smctl keypair sync"
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "[SUCCESS] Certificate check passed"
|
|
Write-Host ""
|
|
|
|
# Find only the files we need to sign (not build scripts)
|
|
$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 (set by DigiCert action)
|
|
$pkcs11Config = $env:PKCS11_CONFIG
|
|
if (-not $pkcs11Config) {
|
|
Write-Host "[ERROR] PKCS11_CONFIG environment variable not set"
|
|
Write-Host "DigiCert KeyLocker action may not have run correctly"
|
|
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..."
|
|
# Use smctl to sign with 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"
|
|
|
|
# Check if output contains "FAILED" even with exit code 0
|
|
if ($output -match "FAILED" -or $output -match "error" -or $output -match "Error") {
|
|
Write-Host ""
|
|
Write-Host "[ERROR] Signing failed for $($file.Name)"
|
|
Write-Host "[ERROR] smctl returned success but output indicates failure"
|
|
Write-Host ""
|
|
Write-Host "Possible issues:"
|
|
Write-Host " 1. Certificate not fully synced to KeyLocker (wait a few minutes)"
|
|
Write-Host " 2. Incorrect keypair alias"
|
|
Write-Host " 3. API key lacks signing permissions"
|
|
Write-Host ""
|
|
Write-Host "Please verify in DigiCert ONE portal:"
|
|
Write-Host " - Certificate status is 'Issued' (not Pending)"
|
|
Write-Host " - Keypair status is 'Online'"
|
|
Write-Host " - 'Can sign' is set to 'Yes'"
|
|
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: Verify notarization (macOS only)
|
|
if: matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel'
|
|
run: |
|
|
echo "🔍 Verifying notarization status..."
|
|
cd ./frontend/src-tauri/target
|
|
DMG_FILE=$(find . -name "*.dmg" | head -1)
|
|
if [ -n "$DMG_FILE" ]; then
|
|
echo "Found DMG: $DMG_FILE"
|
|
echo "Checking notarization ticket..."
|
|
spctl -a -vvv -t install "$DMG_FILE" || echo "⚠️ Notarization check failed or not yet complete"
|
|
stapler validate "$DMG_FILE" || echo "⚠️ No notarization ticket attached"
|
|
else
|
|
echo "⚠️ No DMG file found to verify"
|
|
fi
|
|
|
|
- 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" \;
|
|
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: Verify Windows Code Signature
|
|
if: matrix.platform == 'windows-latest' && github.ref == 'refs/heads/main'
|
|
shell: pwsh
|
|
run: |
|
|
Write-Host "Verifying Windows code signatures..."
|
|
|
|
$exePath = "./dist/Stirling-PDF-${{ matrix.name }}.exe"
|
|
$msiPath = "./dist/Stirling-PDF-${{ matrix.name }}.msi"
|
|
|
|
$allSigned = $true
|
|
$usingKeyLocker = "${{ env.SM_API_KEY }}" -ne ""
|
|
$usingPfx = "${{ env.WINDOWS_CERTIFICATE }}" -ne ""
|
|
|
|
# Check EXE signature
|
|
if (Test-Path $exePath) {
|
|
$exeSig = Get-AuthenticodeSignature -FilePath $exePath
|
|
Write-Host "EXE Signature Status: $($exeSig.Status)"
|
|
Write-Host "EXE Signer: $($exeSig.SignerCertificate.Subject)"
|
|
Write-Host "EXE Timestamp: $($exeSig.TimeStamperCertificate.NotAfter)"
|
|
|
|
if ($exeSig.Status -ne "Valid") {
|
|
Write-Host "[WARNING] EXE is not properly signed (Status: $($exeSig.Status))"
|
|
if ($usingKeyLocker -or $usingPfx) {
|
|
Write-Host "[ERROR] Certificate was provided but signing failed"
|
|
$allSigned = $false
|
|
} else {
|
|
Write-Host "[INFO] Building unsigned binary (no certificate provided)"
|
|
}
|
|
} else {
|
|
Write-Host "[SUCCESS] EXE is properly signed"
|
|
}
|
|
}
|
|
|
|
# Check MSI signature
|
|
if (Test-Path $msiPath) {
|
|
$msiSig = Get-AuthenticodeSignature -FilePath $msiPath
|
|
Write-Host "MSI Signature Status: $($msiSig.Status)"
|
|
Write-Host "MSI Signer: $($msiSig.SignerCertificate.Subject)"
|
|
Write-Host "MSI Timestamp: $($msiSig.TimeStamperCertificate.NotAfter)"
|
|
|
|
if ($msiSig.Status -ne "Valid") {
|
|
Write-Host "[WARNING] MSI is not properly signed (Status: $($msiSig.Status))"
|
|
if ($usingKeyLocker -or $usingPfx) {
|
|
Write-Host "[ERROR] Certificate was provided but signing failed"
|
|
$allSigned = $false
|
|
} else {
|
|
Write-Host "[INFO] Building unsigned binary (no certificate provided)"
|
|
}
|
|
} else {
|
|
Write-Host "[SUCCESS] MSI is properly signed"
|
|
}
|
|
}
|
|
|
|
if (($usingKeyLocker -or $usingPfx) -and -not $allSigned) {
|
|
Write-Host "[ERROR] Code signing verification failed"
|
|
exit 1
|
|
} else {
|
|
Write-Host "[SUCCESS] Code signature verification completed"
|
|
}
|
|
|
|
- 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-15" ] || [ "${{ matrix.platform }}" = "macos-15-intel" ]; then
|
|
echo "Checking for macOS artifacts..."
|
|
find . -name "*.dmg" | head -5
|
|
if [ $(find . -name "*.dmg" | 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:
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
if: always()
|
|
steps:
|
|
- name: Report build results
|
|
run: |
|
|
if [ "${{ needs.build.result }}" = "success" ]; then
|
|
echo "✅ All Tauri builds completed successfully!"
|
|
echo "Artifacts are ready for distribution."
|
|
elif [ "${{ needs.build.result }}" = "skipped" ]; then
|
|
echo "⏭️ Tauri builds skipped (CI lite mode enabled)"
|
|
else
|
|
echo "❌ Some Tauri builds failed."
|
|
echo "Please check the logs and fix any issues."
|
|
exit 1
|
|
fi
|