Skip to content

Nightly Build

Nightly Build #133

Workflow file for this run

name: Nightly Build
# Daily / on-demand builds, published as a rolling pre-release on GitHub.
#
# Difference vs release.yml:
# - release.yml → triggered by `v*` semver tags, becomes the "Latest" stable release
# - nightly.yml → continuous interim builds, marked prerelease, single rolling tag
#
# Trigger options:
# - Manual: Actions tab → "Nightly Build" → Run workflow
# - Push to main: every commit (disabled by default; uncomment the push block)
# - Daily cron at 04:00 UTC
on:
workflow_dispatch:
schedule:
- cron: '0 4 * * *'
# Uncomment to also build on every push to main:
# push:
# branches: [main]
permissions:
contents: write
concurrency:
group: nightly-build
cancel-in-progress: true
jobs:
prepare-release:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
release_id: ${{ steps.recreate.outputs.release_id }}
steps:
- uses: actions/checkout@v6
- name: Compute nightly version + tag
id: version
shell: bash
run: |
BASE_VERSION=$(node -p "require('./open-pdf-studio/package.json').version")
DATE=$(date -u +'%Y%m%d')
SHORT_SHA=$(git rev-parse --short HEAD)
VERSION="${BASE_VERSION}-nightly.${DATE}.${SHORT_SHA}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=nightly" >> "$GITHUB_OUTPUT"
echo "Building nightly ${VERSION}"
- name: Delete previous nightly release + tag (rolling)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release delete nightly --yes --cleanup-tag || true
- name: Create fresh nightly release (prerelease, draft=false so artifacts upload visibly)
id: recreate
uses: actions/github-script@v7
with:
script: |
const version = "${{ steps.version.outputs.version }}";
const { data } = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: "nightly",
target_commitish: context.sha,
name: `Nightly build ${version}`,
body: [
"🌙 **Nightly / interim build** — not a stable release.",
"",
"Built from the latest `main` branch. May contain bugs or incomplete features.",
"For stable, use the latest `v*` release.",
"",
`Source: \`${context.sha.substring(0,7)}\``,
`Build version: \`${version}\``
].join("\n"),
draft: false,
prerelease: true
});
core.setOutput("release_id", data.id);
return data.id;
build-tauri:
needs: prepare-release
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- platform: 'ubuntu-22.04'
args: ''
target: 'linux'
- platform: 'macos-26'
args: '--target universal-apple-darwin'
target: 'macos'
- platform: 'windows-latest'
args: ''
target: 'windows-system'
- platform: 'windows-latest'
args: ''
target: 'windows-user'
runs-on: ${{ matrix.platform }}
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: 'tauri2026'
# linuxdeploy/appimagetool need FUSE; extract-and-run avoids requiring a
# FUSE mount on the CI runner (harmless on Windows/macOS — only the
# AppImage tooling reads it).
APPIMAGE_EXTRACT_AND_RUN: 1
defaults:
run:
working-directory: open-pdf-studio
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 20
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install Rust targets (macOS universal)
if: matrix.target == 'macos'
run: rustup target add aarch64-apple-darwin x86_64-apple-darwin
- name: Install dependencies (Ubuntu only)
if: matrix.target == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install frontend dependencies
run: npm ci
# The PDFium worker pool runs as a Tauri sidecar (bundle.externalBin).
# Tauri requires binaries/pdfium-worker-<target-triple> to exist at build
# time on EVERY desktop platform, so build the workspace crate first and
# drop it in with the right triple suffix. Without these steps build.rs /
# tauri_build fails the validation — the cause of the failing nightlies.
- name: Build pdfium-worker sidecar (Windows)
if: contains(matrix.target, 'windows')
shell: pwsh
working-directory: .
run: |
cargo build --release -p pdfium-worker
New-Item -ItemType Directory -Force open-pdf-studio/src-tauri/binaries | Out-Null
Copy-Item target/release/pdfium-worker.exe open-pdf-studio/src-tauri/binaries/pdfium-worker-x86_64-pc-windows-msvc.exe -Force
- name: Build pdfium-worker sidecar (Linux)
if: matrix.target == 'linux'
working-directory: .
run: |
cargo build --release -p pdfium-worker
mkdir -p open-pdf-studio/src-tauri/binaries
cp target/release/pdfium-worker open-pdf-studio/src-tauri/binaries/pdfium-worker-x86_64-unknown-linux-gnu
- name: Build pdfium-worker sidecar (macOS universal)
if: matrix.target == 'macos'
working-directory: .
run: |
cargo build --release -p pdfium-worker --target aarch64-apple-darwin
cargo build --release -p pdfium-worker --target x86_64-apple-darwin
mkdir -p open-pdf-studio/src-tauri/binaries
# tauri-build validates the sidecar PER ARCH during the per-target
# cargo builds of a universal bundle, so all three names must exist.
cp target/aarch64-apple-darwin/release/pdfium-worker open-pdf-studio/src-tauri/binaries/pdfium-worker-aarch64-apple-darwin
cp target/x86_64-apple-darwin/release/pdfium-worker open-pdf-studio/src-tauri/binaries/pdfium-worker-x86_64-apple-darwin
lipo -create -output open-pdf-studio/src-tauri/binaries/pdfium-worker-universal-apple-darwin \
target/aarch64-apple-darwin/release/pdfium-worker \
target/x86_64-apple-darwin/release/pdfium-worker
- name: Set user install mode (Windows user installer)
if: matrix.target == 'windows-user'
shell: pwsh
run: |
$conf = Get-Content src-tauri/tauri.conf.json -Raw | ConvertFrom-Json
$conf.bundle.windows.nsis.installMode = "currentUser"
$conf | ConvertTo-Json -Depth 10 | Set-Content src-tauri/tauri.conf.json
# Linux uses in-proc PDFium (the worker sidecar is Windows-only), so the
# AppImage must bundle libpdfium.so — referenced by tauri.linux.conf.json.
# Fetch the same pdfium release as the committed win-x64/pdfium.dll
# (bblanchon chromium/7834) before bundling.
- name: Fetch libpdfium.so (Linux)
if: matrix.target == 'linux'
run: |
mkdir -p src-tauri/binaries/linux-x64
curl -L "https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F7834/pdfium-linux-x64.tgz" -o /tmp/pdfium-linux.tgz
tar xzf /tmp/pdfium-linux.tgz -C /tmp lib/libpdfium.so
cp /tmp/lib/libpdfium.so src-tauri/binaries/linux-x64/libpdfium.so
# macOS bundelt in-proc PDFium (naast de worker-sidecar): tauri.macos.conf.json
# verwijst naar binaries/macos-universal/libpdfium.dylib. Universal
# (arm64+x86_64) via bblanchon/pdfium-binaries (zelfde chromium-release als
# Linux/Windows), met SHA-256-verificatie.
- name: Prepare libpdfium.dylib (macOS)
if: matrix.target == 'macos'
run: npm run prepare:native-runtime
# Onderteken de dylib vóór het bundelen — zie release.yml voor de
# onderbouwing (notarisatie keurt een onondertekende resource-dylib af).
- name: Sign libpdfium.dylib (macOS)
if: matrix.target == 'macos'
shell: bash
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
run: |
set -euo pipefail
KEYCHAIN="$RUNNER_TEMP/dylib-sign.keychain-db"
KEYCHAIN_PW="$(uuidgen)"
printf '%s' "$APPLE_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/dylib-cert.p12"
security create-keychain -p "$KEYCHAIN_PW" "$KEYCHAIN"
security set-keychain-settings -lut 21600 "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_PW" "$KEYCHAIN"
security import "$RUNNER_TEMP/dylib-cert.p12" -P "$APPLE_CERTIFICATE_PASSWORD" \
-A -t cert -f pkcs12 -k "$KEYCHAIN"
security set-key-partition-list -S "apple-tool:,apple:,codesign:" \
-k "$KEYCHAIN_PW" "$KEYCHAIN" > /dev/null
security list-keychains -d user -s "$KEYCHAIN" login.keychain-db
codesign --force --options runtime --timestamp \
--sign "$APPLE_SIGNING_IDENTITY" \
src-tauri/binaries/macos-universal/libpdfium.dylib
codesign -dv --verbose=2 src-tauri/binaries/macos-universal/libpdfium.dylib
rm -f "$RUNNER_TEMP/dylib-cert.p12"
- name: Write App Store Connect API key (macOS)
if: matrix.target == 'macos'
shell: bash
run: |
printf '%s' "${{ secrets.APPLE_API_KEY_CONTENT }}" > "${{ runner.temp }}/apple-api-key.p8"
- name: Build Tauri app
if: matrix.target != 'windows-user' && matrix.target != 'macos'
uses: tauri-apps/tauri-action@action-v0.6.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
projectPath: open-pdf-studio
args: ${{ matrix.args }}
releaseId: ${{ needs.prepare-release.outputs.release_id }}
includeUpdaterJson: false
# Compile once. Bundling is a separate phase so a temporary failure in
# the external notarization service can be retried without rebuilding
# both macOS architectures.
- name: Build macOS app without bundles
if: matrix.target == 'macos'
run: npm run tauri -- build --target universal-apple-darwin --no-bundle
- name: Upload macOS release assets
if: matrix.target == 'macos'
uses: tauri-apps/tauri-action@action-v0.6.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
# Notarisatie via App Store Connect API-key: Apple-ID + app-specifiek
# wachtwoord gaf een permanente HTTP 500 op de notary-API (bekend
# probleem met die auth-methode); de API-key-route is de door Apple
# aanbevolen en betrouwbare CI-methode.
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
projectPath: open-pdf-studio
tauriScript: node scripts/macos-notarization-retry.mjs
args: --target universal-apple-darwin
releaseId: ${{ needs.prepare-release.outputs.release_id }}
includeUpdaterJson: false
- name: Verify macOS signature and notarization
if: matrix.target == 'macos'
run: |
APP_PATH="../target/universal-apple-darwin/release/bundle/macos/Open PDF Studio.app"
DMG_PATH="$(find ../target/universal-apple-darwin/release/bundle/dmg -name '*.dmg' -print -quit)"
test -n "$DMG_PATH"
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
spctl --assess --type execute --verbose=4 "$APP_PATH"
xcrun stapler validate "$APP_PATH"
xcrun stapler validate "$DMG_PATH"
bash scripts/macos-startup-smoke.sh "$APP_PATH"
- name: Build Tauri app (Windows user installer)
if: matrix.target == 'windows-user'
uses: tauri-apps/tauri-action@action-v0.6.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
projectPath: open-pdf-studio
args: ${{ matrix.args }}
releaseId: ${{ needs.prepare-release.outputs.release_id }}
includeUpdaterJson: false