mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
# Description of Changes This change adds cleanup steps to GitHub Actions workflows that create temporary files or sensitive build resources. Changes include: - Removing temporary backend helper files, logs, and PID files. - Cleaning up database migration temporary directories after failure logs are uploaded. - Removing locally generated deployment files and Storybook archives. - Deleting temporary signing certificates, MSI extraction directories, and Apple signing keychains. - Ensuring cleanup runs even when earlier workflow steps fail. The cleanup reduces temporary data retention on runners and ensures sensitive signing material is removed after builds. No functional application code was changed. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details.
814 lines
38 KiB
YAML
814 lines
38 KiB
YAML
name: Build Tauri Applications
|
|
|
|
# Multi-OS Tauri desktop bundle build matrix (Windows / macOS universal /
|
|
# Linux). Called from build.yml on PRs that touch desktop sources (gated
|
|
# via the `tauri` filter in .github/config/.files.yaml). Also runnable
|
|
# on demand via workflow_dispatch with a per-platform selector.
|
|
#
|
|
# Note: editing this file is itself enough to make the `tauri` path filter
|
|
# match, which is how non-desktop PRs (e.g. backend-only fixes) opt into a
|
|
# desktop smoke build.
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
platform:
|
|
description: "Platform to build (windows, windows-arm64, macos, linux, windows-macos, or all)."
|
|
required: false
|
|
type: string
|
|
default: "all"
|
|
sign:
|
|
description: "Sign and notarize the bundles."
|
|
required: false
|
|
type: boolean
|
|
default: true
|
|
minimal:
|
|
description: "Fast smoke build: Linux deb only, skip rpm and the flaky AppImage pass. Used by PR builds."
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
use_shared_cache:
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
workflow_dispatch:
|
|
inputs:
|
|
platform:
|
|
description: "Platform to build (windows, windows-arm64, macos, linux, windows-macos, or all)"
|
|
required: true
|
|
default: "all"
|
|
type: choice
|
|
options:
|
|
- all
|
|
- windows
|
|
- windows-arm64
|
|
- macos
|
|
- linux
|
|
- windows-macos
|
|
sign:
|
|
description: "Sign and notarize the bundles."
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
minimal:
|
|
description: "Fast smoke build: Linux deb only, skip rpm and the flaky AppImage pass."
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
|
|
jobs:
|
|
determine-matrix:
|
|
# Only probes APPLE_CERTIFICATE for presence, so it stays on the unrestricted
|
|
# signing environment - release-signing would block every PR run.
|
|
environment:
|
|
name: ci-signing
|
|
deployment: false
|
|
if: ${{ vars.CI_PROFILE != 'lite' }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
steps:
|
|
- name: Harden the runner (Audit all outbound calls)
|
|
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
|
|
with:
|
|
egress-policy: audit
|
|
|
|
- name: Determine build matrix
|
|
id: set-matrix
|
|
env:
|
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
|
PLATFORM: ${{ inputs.platform }}
|
|
run: |
|
|
WINDOWS='{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64","jpdfium_platforms":"windows-x64"}'
|
|
# ARM64: NSIS only (WiX MSI has no arm64 support in Tauri) and no JPDFium
|
|
# natives yet - flip jpdfium_platforms to windows-arm64 once JPDFium ships it.
|
|
WINDOWS_ARM64='{"platform":"windows-11-arm","args":"--target aarch64-pc-windows-msvc --bundles nsis","name":"windows-arm64","jpdfium_platforms":"none"}'
|
|
MACOS='{"platform":"macos-15","args":"--target universal-apple-darwin","name":"macos-universal","jpdfium_platforms":"darwin-arm64,darwin-x64"}'
|
|
LINUX='{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64","jpdfium_platforms":"linux-x64"}'
|
|
|
|
case "$PLATFORM" in
|
|
windows) ENTRIES=("$WINDOWS" "$WINDOWS_ARM64") ;;
|
|
windows-arm64) ENTRIES=("$WINDOWS_ARM64") ;;
|
|
macos) ENTRIES=("$MACOS") ;;
|
|
linux) ENTRIES=("$LINUX") ;;
|
|
windows-macos) ENTRIES=("$WINDOWS" "$MACOS") ;;
|
|
*) ENTRIES=("$WINDOWS" "$WINDOWS_ARM64" "$MACOS" "$LINUX") ;;
|
|
esac
|
|
|
|
# Drop macOS entries when Apple certificate secret is unavailable
|
|
if [ -z "$APPLE_CERTIFICATE" ]; then
|
|
echo "⚠️ APPLE_CERTIFICATE secret not available - skipping macOS builds"
|
|
FILTERED=()
|
|
for entry in "${ENTRIES[@]}"; do
|
|
[[ "$entry" != *'"macos'* ]] && FILTERED+=("$entry")
|
|
done
|
|
ENTRIES=("${FILTERED[@]}")
|
|
fi
|
|
|
|
JOINED=$(IFS=','; echo "${ENTRIES[*]}")
|
|
echo "matrix={\"include\":[$JOINED]}" >> $GITHUB_OUTPUT
|
|
|
|
build:
|
|
# Windows/GPG signing only runs on main (see the per-step gates below), so only
|
|
# that path needs the reviewer-gated release-signing environment. Everything else
|
|
# (PRs, merge queue, nightly) signs macOS only and uses ci-signing, which has no
|
|
# approval or branch restriction.
|
|
environment:
|
|
name: ${{ (inputs.sign && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))) && 'release-signing' || 'ci-signing' }}
|
|
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 }}
|
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
|
RELEASE_GPG_PRIVATE_KEY: ${{ secrets.RELEASE_GPG_PRIVATE_KEY }}
|
|
# Per-platform sign gate. macOS signs on any run with the cert available,
|
|
# PRs included: Gatekeeper blocks an unsigned .dmg, so an unsigned macOS
|
|
# PR build is not testable. Windows and Linux stay main-only, matching the
|
|
# gates on their own signing steps below.
|
|
SIGN_BUNDLE: ${{ inputs.sign && (matrix.platform == 'macos-15' && secrets.APPLE_CERTIFICATE != '' || github.ref == 'refs/heads/main') }}
|
|
steps:
|
|
- name: Harden Runner
|
|
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
|
|
with:
|
|
egress-policy: audit
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.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@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: 22
|
|
cache: "npm"
|
|
cache-dependency-path: frontend/package-lock.json
|
|
|
|
- name: Setup Rust
|
|
uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
|
|
with:
|
|
toolchain: stable
|
|
targets: ${{ matrix.platform == 'macos-15' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
|
|
|
# Cache the Cargo registry and compiled dependency crates so the build
|
|
# only recompiles the app crate. Written on main; PRs and the merge queue
|
|
# restore from it.
|
|
- name: Cache Rust build
|
|
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
|
with:
|
|
workspaces: frontend/editor/src-tauri
|
|
# Stable key shared across workflows so the nightly warmer.
|
|
# rust-cache still appends OS + rustc + Cargo.lock.
|
|
shared-key: tauri-${{ matrix.name }}
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
# Save the dependency cache even if a later step fails
|
|
cache-on-failure: true
|
|
|
|
- name: Restore cache Gradle User Home
|
|
if: inputs.use_shared_cache
|
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: gradle-v1-${{ runner.os }}-${{ runner.arch }}-jdk-25-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/libs.versions.toml', 'buildSrc/**', 'settings.gradle', 'build.gradle', 'app/**/build.gradle', 'gradle/**/*.gradle') }}
|
|
|
|
- name: Restore cache Gradle
|
|
if: inputs.use_shared_cache == false
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: gradle-tauri-build-v1-${{ runner.os }}-${{ runner.arch }}-jdk-25-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties', 'gradle/libs.versions.toml', 'buildSrc/**', 'settings.gradle', 'build.gradle', 'app/**/build.gradle', 'gradle/**/*.gradle') }}
|
|
|
|
- name: Set up x86_64 JDK 25 (macOS universal JRE)
|
|
if: matrix.platform == 'macos-15'
|
|
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
java-version: "25"
|
|
distribution: "temurin"
|
|
architecture: "x64"
|
|
|
|
- name: Capture x86_64 JAVA_HOME
|
|
if: matrix.platform == 'macos-15'
|
|
run: echo "X64_JAVA_HOME=$JAVA_HOME" >> "$GITHUB_ENV"
|
|
|
|
# Temurin has no windows-aarch64 JDK 25 yet; Microsoft OpenJDK does.
|
|
- name: Set up JDK 25
|
|
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
java-version: "25"
|
|
distribution: ${{ matrix.platform == 'windows-11-arm' && 'microsoft' || 'temurin' }}
|
|
|
|
- name: Setup Task
|
|
uses: go-task/setup-task@01a4adf9db2d14c1de7a560f09170b6e0df736aa # v2.1.0
|
|
|
|
- name: Build universal macOS JRE
|
|
if: matrix.platform == 'macos-15'
|
|
env:
|
|
AARCH64_JAVA_HOME: ${{ env.JAVA_HOME }}
|
|
JPDFIUM_PLATFORMS: ${{ matrix.jpdfium_platforms }}
|
|
run: task desktop:jlink:universal-mac
|
|
|
|
- name: Prepare desktop build
|
|
env:
|
|
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
|
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
|
MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }}
|
|
DISABLE_ADDITIONAL_FEATURES: true
|
|
JPDFIUM_PLATFORMS: ${{ matrix.jpdfium_platforms }}
|
|
run: task desktop:prepare
|
|
|
|
- name: Run Tauri/Cargo tests
|
|
run: task desktop:test
|
|
|
|
# DigiCert KeyLocker Setup (Cloud HSM)
|
|
- name: Setup DigiCert KeyLocker
|
|
id: digicert-setup
|
|
if: ${{ inputs.sign && startsWith(matrix.platform, 'windows') && env.SM_API_KEY != '' && github.ref == 'refs/heads/main' }}
|
|
uses: digicert/ssm-code-signing@1d820463733701cf1484c7eb5d7d24a15ca2c454 # v1.2.1
|
|
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: ${{ inputs.sign && startsWith(matrix.platform, 'windows') && env.SM_API_KEY != '' && github.ref == 'refs/heads/main' }}
|
|
shell: pwsh
|
|
env:
|
|
SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }}
|
|
SM_HOST: ${{ secrets.SM_HOST }}
|
|
SM_API_KEY: ${{ secrets.SM_API_KEY }}
|
|
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
|
|
SM_KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS }}
|
|
run: |
|
|
Write-Host "Setting up DigiCert KeyLocker environment..."
|
|
|
|
# Decode client certificate
|
|
$certBytes = [Convert]::FromBase64String("$env: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=$env:SM_HOST" >> $env:GITHUB_ENV
|
|
echo "SM_API_KEY=$env:SM_API_KEY" >> $env:GITHUB_ENV
|
|
echo "SM_CLIENT_CERT_PASSWORD=$env:SM_CLIENT_CERT_PASSWORD" >> $env:GITHUB_ENV
|
|
echo "SM_KEYPAIR_ALIAS=$env: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"
|
|
}
|
|
}
|
|
|
|
- name: Import Apple Developer Certificate
|
|
if: env.SIGN_BUNDLE == 'true' && matrix.platform == 'macos-15'
|
|
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: env.SIGN_BUNDLE == 'true' && matrix.platform == 'macos-15'
|
|
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'
|
|
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: Preflight smctl
|
|
if: ${{ inputs.sign && startsWith(matrix.platform, 'windows') && env.SM_API_KEY != '' && github.ref == 'refs/heads/main' }}
|
|
shell: pwsh
|
|
env:
|
|
KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS }}
|
|
run: |
|
|
& smctl healthcheck
|
|
if ($LASTEXITCODE -ne 0) { Write-Host "[ERROR] smctl healthcheck failed"; exit 1 }
|
|
& smctl keypair ls
|
|
if ($LASTEXITCODE -ne 0) { Write-Host "[ERROR] smctl keypair ls failed"; exit 1 }
|
|
& smctl windows certsync --keypair-alias "$env:KEYPAIR_ALIAS"
|
|
if ($LASTEXITCODE -ne 0) { Write-Host "[WARN] smctl windows certsync returned non-zero - continuing" }
|
|
|
|
- name: Configure Windows code signing
|
|
if: ${{ inputs.sign && startsWith(matrix.platform, 'windows') && env.SM_API_KEY != '' && github.ref == 'refs/heads/main' }}
|
|
shell: bash
|
|
env:
|
|
KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS }}
|
|
run: |
|
|
cat > ./frontend/editor/src-tauri/tauri.windows.conf.json <<EOF
|
|
{
|
|
"bundle": {
|
|
"windows": {
|
|
"signCommand": {
|
|
"cmd": "smctl",
|
|
"args": ["sign", "--keypair-alias", "${KEYPAIR_ALIAS}", "--input", "%1", "--verbose"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
- name: Import release GPG signing key (Linux)
|
|
if: inputs.sign && matrix.platform == 'ubuntu-22.04' && env.RELEASE_GPG_PRIVATE_KEY != '' && github.ref == 'refs/heads/main'
|
|
run: |
|
|
echo "$RELEASE_GPG_PRIVATE_KEY" | gpg --batch --import
|
|
gpg --list-secret-keys --keyid-format=long
|
|
|
|
- name: Make libjvm discoverable for linuxdeploy (Linux AppImage)
|
|
if: matrix.platform == 'ubuntu-22.04'
|
|
run: |
|
|
JAVA_LIBJVM="$JAVA_HOME/lib/server/libjvm.so"
|
|
if [ -f "$JAVA_LIBJVM" ]; then
|
|
sudo ln -sf "$JAVA_LIBJVM" /usr/lib/libjvm.so
|
|
echo "Linked libjvm from $JAVA_LIBJVM -> /usr/lib/libjvm.so"
|
|
else
|
|
echo "libjvm not found at $JAVA_LIBJVM"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Build Tauri app (signed)
|
|
if: env.SIGN_BUNDLE == 'true'
|
|
uses: tauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060f # v1.0.0
|
|
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 }}
|
|
# AppImage signing — three env vars work together:
|
|
# SIGN=1 tells linuxdeploy-plugin-appimage to forward --sign to appimagetool
|
|
# APPIMAGETOOL_SIGN_PASSPHRASE appimagetool uses this to unlock the GPG key non-interactively
|
|
# SIGN_KEY appimagetool picks the key matching this fingerprint
|
|
# Without SIGN=1, the other two are ignored and the AppImage is built unsigned even if a key is present.
|
|
# Mirror the Windows/macOS gate: only sign when secret is present AND ref is main (skips PRs from forks/Dependabot).
|
|
SIGN: ${{ (env.RELEASE_GPG_PRIVATE_KEY != '' && github.ref == 'refs/heads/main') && '1' || '0' }}
|
|
APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.RELEASE_GPG_PASSPHRASE }}
|
|
SIGN_KEY: ${{ vars.RELEASE_GPG_FINGERPRINT }}
|
|
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 || 'sb_publishable_UHz2SVRF5mvdrPHWkRteyA_yNlZTkYb' }}
|
|
VITE_SAAS_SERVER_URL: ${{ secrets.VITE_SAAS_SERVER_URL || 'https://app.stirlingpdf.com' }}
|
|
VITE_SAAS_BACKEND_API_URL: ${{ secrets.VITE_SAAS_BACKEND_API_URL || 'https://api.stirlingpdf.com' }}
|
|
SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}
|
|
CI: true
|
|
with:
|
|
projectPath: ./frontend/editor
|
|
tauriScript: npx tauri
|
|
# Linux: build deb+rpm only here (deb-only on minimal smoke builds).
|
|
# AppImage runs in its own continue-on-error step below so its
|
|
# persistent linuxdeploy failure (#6127 onwards) does not tank uploads.
|
|
args: ${{ matrix.platform == 'ubuntu-22.04' && (inputs.minimal && '--bundles deb' || '--bundles deb,rpm') || matrix.args }}
|
|
|
|
- name: Build Tauri app (unsigned)
|
|
if: env.SIGN_BUNDLE != 'true'
|
|
uses: tauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060f # v1.0.0
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
SIGN: "0"
|
|
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 || 'sb_publishable_UHz2SVRF5mvdrPHWkRteyA_yNlZTkYb' }}
|
|
VITE_SAAS_SERVER_URL: ${{ secrets.VITE_SAAS_SERVER_URL || 'https://app.stirlingpdf.com' }}
|
|
VITE_SAAS_BACKEND_API_URL: ${{ secrets.VITE_SAAS_BACKEND_API_URL || 'https://api.stirlingpdf.com' }}
|
|
CI: true
|
|
with:
|
|
projectPath: ./frontend/editor
|
|
tauriScript: npx tauri
|
|
# Linux: build deb+rpm only here (deb-only on minimal smoke builds).
|
|
# AppImage runs in its own continue-on-error step below so its
|
|
# persistent linuxdeploy failure (#6127 onwards) does not tank uploads.
|
|
args: >-
|
|
${{ matrix.platform == 'ubuntu-22.04' && (inputs.minimal && '--bundles deb' || '--bundles deb,rpm') || matrix.args }}
|
|
--config '{"bundle":{"createUpdaterArtifacts":false}}'
|
|
|
|
# AppImage is decoupled so its linuxdeploy run gets a fresh process
|
|
# (rpm scratch state torn down) and its failure can't tank deb/rpm.
|
|
# Skipped on minimal smoke builds (flaky + slow, deb is enough to verify).
|
|
- name: Build Tauri app (Linux AppImage)
|
|
if: matrix.platform == 'ubuntu-22.04' && !inputs.minimal
|
|
continue-on-error: true
|
|
uses: tauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060f # v1.0.0
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
SIGN: ${{ (inputs.sign && env.RELEASE_GPG_PRIVATE_KEY != '' && github.ref == 'refs/heads/main') && '1' || '0' }}
|
|
APPIMAGETOOL_SIGN_PASSPHRASE: ${{ secrets.RELEASE_GPG_PASSPHRASE }}
|
|
SIGN_KEY: ${{ vars.RELEASE_GPG_FINGERPRINT }}
|
|
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 || 'sb_publishable_UHz2SVRF5mvdrPHWkRteyA_yNlZTkYb' }} # gitleaks:allow
|
|
VITE_SAAS_SERVER_URL: ${{ secrets.VITE_SAAS_SERVER_URL || 'https://app.stirlingpdf.com' }}
|
|
VITE_SAAS_BACKEND_API_URL: ${{ secrets.VITE_SAAS_BACKEND_API_URL || 'https://api.stirlingpdf.com' }}
|
|
CI: true
|
|
with:
|
|
projectPath: ./frontend/editor
|
|
tauriScript: npx tauri
|
|
args: --bundles appimage
|
|
|
|
# Bundled libwayland conflicts with the host's on some distros (Fedora
|
|
# Wayland: EGL_BAD_PARAMETER, blank window - #6878). The AppImage
|
|
# ecosystem excludelist agrees these libs must come from the system.
|
|
- name: Strip bundled Wayland libs from AppImage
|
|
if: matrix.platform == 'ubuntu-22.04' && !inputs.minimal
|
|
continue-on-error: true
|
|
run: |
|
|
set -euo pipefail
|
|
AI=$(find "$PWD/frontend/editor/src-tauri/target" -name "*.AppImage" | head -1)
|
|
if [ -z "$AI" ]; then echo "No AppImage found - skipping"; exit 0; fi
|
|
chmod +x "$AI"
|
|
WORK=$(mktemp -d)
|
|
(cd "$WORK" && "$AI" --appimage-extract >/dev/null)
|
|
if ! ls "$WORK/squashfs-root/usr/lib/"libwayland-* >/dev/null 2>&1; then
|
|
echo "No bundled libwayland - nothing to strip"
|
|
rm -rf "$WORK"
|
|
exit 0
|
|
fi
|
|
rm -f "$WORK/squashfs-root/usr/lib/"libwayland-*
|
|
curl -fsSL -o "$WORK/appimagetool" \
|
|
https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
|
|
# Pinned checksum: never execute an unverified downloaded binary. On
|
|
# mismatch (upstream rebuilt continuous) the step aborts and the
|
|
# original AppImage ships unchanged - update the pin deliberately.
|
|
echo "a6d71e2b6cd66f8e8d16c37ad164658985e0cf5fcaa950c90a482890cb9d13e0 $WORK/appimagetool" | sha256sum -c -
|
|
chmod +x "$WORK/appimagetool"
|
|
"$WORK/appimagetool" --appimage-extract-and-run "$WORK/squashfs-root" "$AI.new"
|
|
mv "$AI.new" "$AI"
|
|
rm -rf "$WORK"
|
|
echo "Stripped bundled libwayland from $(basename "$AI")"
|
|
|
|
- name: Clear release GPG key from runner keyring (Linux)
|
|
if: always() && inputs.sign && matrix.platform == 'ubuntu-22.04' && env.RELEASE_GPG_PRIVATE_KEY != '' && github.ref == 'refs/heads/main'
|
|
env:
|
|
RELEASE_GPG_FINGERPRINT: ${{ vars.RELEASE_GPG_FINGERPRINT }}
|
|
run: |
|
|
if [ -n "$RELEASE_GPG_FINGERPRINT" ]; then
|
|
gpg --batch --yes --delete-secret-keys "$RELEASE_GPG_FINGERPRINT" || true
|
|
gpg --batch --yes --delete-keys "$RELEASE_GPG_FINGERPRINT" || true
|
|
fi
|
|
|
|
- name: Verify notarization (macOS only)
|
|
if: env.SIGN_BUNDLE == 'true' && matrix.platform == 'macos-15'
|
|
run: |
|
|
echo "🔍 Verifying notarization status..."
|
|
cd ./frontend/editor/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: |
|
|
# Absolute dist path so the cd below can't break the copy targets.
|
|
DIST="$GITHUB_WORKSPACE/dist"
|
|
mkdir -p "$DIST"
|
|
cd ./frontend/editor/src-tauri/target
|
|
|
|
# Find and rename artifacts based on platform
|
|
if [ "${{ matrix.platform }}" = "windows-latest" ]; then
|
|
# Only ship the MSI installer. The loose exe and WiX toolset exes
|
|
# are not the user-facing installer - the MSI contains the signed inner exe.
|
|
find . -name "*.msi" -exec cp {} "$DIST/Stirling-PDF-${{ matrix.name }}.msi" \;
|
|
elif [ "${{ matrix.platform }}" = "windows-11-arm" ]; then
|
|
# arm64 ships the NSIS installer (WiX MSI has no arm64 support in Tauri).
|
|
find . -name "*-setup.exe" -exec cp {} "$DIST/Stirling-PDF-${{ matrix.name }}-setup.exe" \;
|
|
elif [ "${{ matrix.platform }}" = "macos-15" ]; 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 "*.rpm" -exec cp {} "$DIST/Stirling-PDF-${{ matrix.name }}.rpm" \;
|
|
find . -name "*.AppImage" -exec cp {} "$DIST/Stirling-PDF-${{ matrix.name }}.AppImage" \;
|
|
fi
|
|
|
|
# Verify the MSI AND the inner exe extracted from it are signed.
|
|
# The inner exe is what gets installed on users' machines and what AV scans.
|
|
- name: Verify Windows Code Signature
|
|
if: inputs.sign && startsWith(matrix.platform, 'windows') && env.SM_API_KEY != '' && github.ref == 'refs/heads/main'
|
|
shell: pwsh
|
|
run: |
|
|
# arm64 ships an NSIS installer, not an MSI. Tauri's signCommand signs the
|
|
# inner exe before packing and the setup exe after, so verifying the setup
|
|
# exe is the arm64 equivalent of the MSI + inner-exe check below.
|
|
if ("${{ matrix.platform }}" -eq "windows-11-arm") {
|
|
$exePath = "./dist/Stirling-PDF-${{ matrix.name }}-setup.exe"
|
|
if (-not (Test-Path $exePath)) {
|
|
Write-Host "[ERROR] NSIS installer not found at $exePath"
|
|
exit 1
|
|
}
|
|
$sig = Get-AuthenticodeSignature -FilePath $exePath
|
|
Write-Host "NSIS installer: Status=$($sig.Status), Signer=$($sig.SignerCertificate.Subject)"
|
|
if ($sig.Status -ne "Valid") {
|
|
Write-Host "[ERROR] NSIS installer is not signed"
|
|
exit 1
|
|
}
|
|
Write-Host "[SUCCESS] NSIS installer is properly signed"
|
|
exit 0
|
|
}
|
|
|
|
$allSigned = $true
|
|
$msiPath = "./dist/Stirling-PDF-${{ matrix.name }}.msi"
|
|
|
|
# Check MSI (outer wrapper)
|
|
if (Test-Path $msiPath) {
|
|
$sig = Get-AuthenticodeSignature -FilePath $msiPath
|
|
Write-Host "MSI: Status=$($sig.Status), Signer=$($sig.SignerCertificate.Subject)"
|
|
if ($sig.Status -ne "Valid") {
|
|
Write-Host "[ERROR] MSI is not signed"
|
|
$allSigned = $false
|
|
}
|
|
|
|
# Extract MSI and verify inner exe
|
|
$extractDir = Join-Path $env:RUNNER_TEMP "msi-verify"
|
|
if (Test-Path $extractDir) { Remove-Item $extractDir -Recurse -Force }
|
|
$proc = Start-Process msiexec.exe -ArgumentList '/a', $msiPath, '/qn', "TARGETDIR=$extractDir" -Wait -PassThru -NoNewWindow
|
|
if ($proc.ExitCode -eq 0) {
|
|
$innerExe = Get-ChildItem -Path $extractDir -Filter "stirling-pdf.exe" -Recurse -File | Select-Object -First 1
|
|
if ($innerExe) {
|
|
$sig = Get-AuthenticodeSignature -FilePath $innerExe.FullName
|
|
Write-Host "Inner EXE: Status=$($sig.Status), Signer=$($sig.SignerCertificate.Subject)"
|
|
if ($sig.Status -ne "Valid") {
|
|
Write-Host "[ERROR] Inner exe is NOT signed - AV will flag this at runtime"
|
|
$allSigned = $false
|
|
}
|
|
} else {
|
|
Write-Host "[ERROR] Could not find stirling-pdf.exe inside MSI"
|
|
$allSigned = $false
|
|
}
|
|
} else {
|
|
Write-Host "[ERROR] MSI extraction failed (exit code: $($proc.ExitCode))"
|
|
$allSigned = $false
|
|
}
|
|
} else {
|
|
Write-Host "[ERROR] MSI not found at $msiPath"
|
|
$allSigned = $false
|
|
}
|
|
|
|
if (-not $allSigned) {
|
|
Write-Host "[ERROR] Signature verification failed"
|
|
exit 1
|
|
}
|
|
Write-Host "[SUCCESS] MSI and inner exe are properly signed"
|
|
|
|
- name: Dump smctl logs on failure
|
|
if: ${{ failure() && startsWith(matrix.platform, 'windows') && env.SM_API_KEY != '' }}
|
|
shell: pwsh
|
|
run: |
|
|
$logDir = "$env:USERPROFILE\.signingmanager\logs"
|
|
if (Test-Path $logDir) {
|
|
Get-ChildItem $logDir | ForEach-Object {
|
|
Write-Host "=== $($_.FullName) ==="
|
|
Get-Content $_.FullName -Tail 200
|
|
Write-Host ""
|
|
}
|
|
} else {
|
|
Write-Host "smctl log directory not found at $logDir"
|
|
}
|
|
|
|
- name: Upload artifacts
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: Stirling-PDF-${{ matrix.name }}
|
|
path: ./dist/*
|
|
retention-days: 7
|
|
|
|
- name: Verify build artifacts
|
|
shell: bash
|
|
run: |
|
|
cd ./frontend/editor/src-tauri/target
|
|
|
|
# Check for expected artifacts based on platform
|
|
if [ "${{ matrix.platform }}" = "windows-latest" ] || [ "${{ matrix.platform }}" = "windows-11-arm" ]; 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" ]; 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 "*.rpm" -o -name "*.AppImage" | head -5
|
|
if [ $(find . -name "*.deb" -o -name "*.rpm" -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/editor/src-tauri/target
|
|
echo "Artifact sizes for ${{ matrix.name }}:"
|
|
find . -name "*.exe" -o -name "*.dmg" -o -name "*.deb" -o -name "*.rpm" -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
|
|
|
|
- name: Cleanup temporary files
|
|
if: always()
|
|
shell: bash
|
|
run: |
|
|
rm -f certificate.p12
|
|
rm -rf "$RUNNER_TEMP/msi-verify"
|
|
if [ "${{ matrix.platform }}" = "macos-15" ]; then
|
|
security delete-keychain "$RUNNER_TEMP/app-signing.keychain-db" 2>/dev/null || true
|
|
fi
|
|
continue-on-error: true
|
|
|
|
pr-comment:
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
# Fork and Dependabot pull_request runs receive a read-only GITHUB_TOKEN,
|
|
# so the API cannot create or update PR comments there. The artifacts are
|
|
# still uploaded and remain available from the Actions run page.
|
|
if: >-
|
|
github.event_name == 'pull_request' &&
|
|
needs.build.result == 'success' &&
|
|
!github.event.pull_request.head.repo.fork &&
|
|
github.actor != 'dependabot[bot]'
|
|
permissions:
|
|
pull-requests: write
|
|
steps:
|
|
- name: Harden the runner
|
|
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
|
|
with:
|
|
egress-policy: audit
|
|
|
|
- name: Post/Update PR Comment with Download Links
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
with:
|
|
script: |
|
|
const owner = context.repo.owner;
|
|
const repo = context.repo.repo;
|
|
const prNumber = context.issue.number;
|
|
const runId = context.runId;
|
|
|
|
// Fetch artifacts for this workflow run
|
|
const { data: artifactsList } = await github.rest.actions.listWorkflowRunArtifacts({
|
|
owner,
|
|
repo,
|
|
run_id: runId
|
|
});
|
|
|
|
// Map of expected artifact names to display info
|
|
const artifactMap = {
|
|
'Stirling-PDF-windows-x86_64': { icon: '🪟', platform: 'Windows x64', files: '.exe, .msi' },
|
|
'Stirling-PDF-windows-arm64': { icon: '🪟', platform: 'Windows ARM64', files: '-setup.exe (NSIS)' },
|
|
'Stirling-PDF-macos-universal': { icon: '🍎', platform: 'macOS Universal', files: '.dmg' },
|
|
'Stirling-PDF-linux-x86_64': { icon: '🐧', platform: 'Linux x64', files: '.deb, .rpm, .AppImage' }
|
|
};
|
|
|
|
let commentBody = `## 📦 Tauri Desktop Builds Ready!\n\n`;
|
|
commentBody += `The desktop applications have been built and are ready for testing.\n\n`;
|
|
commentBody += `### Download Artifacts:\n\n`;
|
|
|
|
// Add links for each found artifact
|
|
let foundArtifacts = 0;
|
|
for (const artifact of artifactsList.artifacts) {
|
|
const info = artifactMap[artifact.name];
|
|
if (info) {
|
|
foundArtifacts++;
|
|
// GitHub doesn't provide direct download URLs via API, but we can link to the artifact on the Actions page
|
|
const artifactUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}/artifacts/${artifact.id}`;
|
|
commentBody += `${info.icon} **${info.platform}**: [Download ${artifact.name}](${artifactUrl}) `;
|
|
commentBody += `(${info.files}) - ${(artifact.size_in_bytes / 1024 / 1024).toFixed(1)} MB\n`;
|
|
}
|
|
}
|
|
|
|
if (foundArtifacts === 0) {
|
|
commentBody += `⚠️ **Warning**: No artifacts found in workflow run.\n`;
|
|
commentBody += `[View workflow run](https://github.com/${owner}/${repo}/actions/runs/${runId})\n`;
|
|
}
|
|
|
|
commentBody += `\n---\n`;
|
|
commentBody += `_Built from commit ${context.sha.substring(0, 7)}_\n`;
|
|
commentBody += `_Artifacts expire in 7 days_`;
|
|
|
|
// Find existing comment
|
|
const { data: comments } = await github.rest.issues.listComments({
|
|
owner,
|
|
repo,
|
|
issue_number: prNumber
|
|
});
|
|
|
|
const botComment = comments.find(comment =>
|
|
comment.user.type === 'Bot' &&
|
|
comment.body.includes('📦 Tauri Desktop Builds Ready!')
|
|
);
|
|
|
|
if (botComment) {
|
|
// Update existing comment
|
|
await github.rest.issues.updateComment({
|
|
owner,
|
|
repo,
|
|
comment_id: botComment.id,
|
|
body: commentBody
|
|
});
|
|
console.log('Updated existing comment');
|
|
} else {
|
|
// Create new comment
|
|
await github.rest.issues.createComment({
|
|
owner,
|
|
repo,
|
|
issue_number: prNumber,
|
|
body: commentBody
|
|
});
|
|
console.log('Created new comment');
|
|
}
|
|
|
|
report:
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
if: always()
|
|
steps:
|
|
- name: Harden the runner (Audit all outbound calls)
|
|
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
|
|
with:
|
|
egress-policy: audit
|
|
|
|
- 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
|