Skip to content

feat: add print_to_pdf backend API #257

feat: add print_to_pdf backend API

feat: add print_to_pdf backend API #257

Workflow file for this run

name: CI
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- name: Install dependencies (Linux)
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libx11-dev libxcb1-dev libxkbcommon-dev libwayland-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
pipx install "clang-format==22.1.5"
- name: cargo fmt
run: cargo fmt --check
- name: cargo clippy
run: cargo clippy --workspace -- -D warnings
- name: clang-format
run: |
find capi cef/src webview/src -name '*.cc' -o -name '*.cpp' -o -name '*.c' -o -name '*.h' -o -name '*.mm' | \
xargs clang-format --dry-run --Werror
- name: deno fmt
run: deno fmt --check
- name: deno lint
run: deno lint
test:
name: cargo test (capi)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
prefix-key: test
# Only the capi crate has unit-testable logic (pure conversions /
# builders). The example runtimes are cdylibs and the winit/webview
# backends require display servers, so we don't run their tests in CI.
- run: cargo test -p laufey --lib
- run: cargo test -p laufey --doc
build-winit:
name: winit / ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# 22.04 keeps the glibc/libstdc++ baseline low (glibc 2.35,
# GLIBCXX_3.4.30) so binaries run on Debian 12 / Ubuntu 22.04+.
- { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 }
- { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm }
- { target: x86_64-pc-windows-msvc, os: windows-latest }
- { target: aarch64-pc-windows-msvc, os: windows-11-arm }
- { target: x86_64-apple-darwin, os: macos-15-intel }
- { target: aarch64-apple-darwin, os: macos-14 }
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
prefix-key: winit
# rust-cache keys on runner.os+arch only ("Linux-x64"), not the
# image version, so a cache built on the old ubuntu-latest (24.04)
# restores as a full match onto ubuntu-22.04 and poisons the target
# dir (stale fingerprints, missing dep rlibs -> E0463). Pin per image.
key: ${{ matrix.os }}
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libx11-dev libxcb1-dev libxkbcommon-dev libwayland-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install llvm
- name: Install dependencies (Windows)
if: runner.os == 'Windows'
run: choco install make -y
- run: make winit
- name: Package
shell: bash
run: |
mkdir staging
if [[ "${{ runner.os }}" == "Windows" ]]; then
cp target/release/laufey_winit.exe staging/
cd staging && 7z a ../laufey-winit-${{ matrix.target }}.zip *
else
cp target/release/laufey_winit staging/
tar czf laufey-winit-${{ matrix.target }}.tar.gz -C staging .
fi
- uses: actions/upload-artifact@v7
with:
name: laufey-winit-${{ matrix.target }}
path: |
laufey-winit-${{ matrix.target }}.tar.gz
laufey-winit-${{ matrix.target }}.zip
# End-to-end native-chrome / windowing tests. See docs/e2e-testing.md.
# Drives the *same* backend-agnostic native_e2e_runtime under each backend:
# Layer 0 (all): in-process readback + event-callback + clipboard + menu/
# tray click round-trips (via the test_click_menu_item hook).
# Layer 1 (Linux, cef): D-Bus StatusNotifier/dbusmenu observer.
#
# macOS exercises all three backends (both native-chrome codebases) end to
# end. Non-macOS legs are included only where they pass reliably; the rest are
# known backend limitations under headless CI, tracked as follow-ups:
# - winit / linux : the winit backend doesn't support Linux menus and panics
# building a muda menu with no GTK init.
# - webview / linux: WebKitGTK/Xlib isn't thread-safe under the worker-thread
# runtime (xcb_xlib_threads_sequence_lost).
# - cef|webview / windows: CEF's Windows dist extraction and the WebView2
# run are flaky in this job; need hands-on debugging.
native-e2e:
name: native-e2e / ${{ matrix.backend }} / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { os: macos-14, backend: winit }
- { os: macos-14, backend: webview }
- { os: macos-14, backend: cef }
- { os: windows-latest, backend: winit }
- { os: windows-latest, backend: webview }
- { os: ubuntu-22.04, backend: cef }
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
prefix-key: native-e2e
key: ${{ matrix.os }}-${{ matrix.backend }}
# CEF fetches a large prebuilt binary distribution; cache it.
- name: Cache CEF
if: matrix.backend == 'cef'
uses: actions/cache@v6
with:
path: vendor/cef
key: cef-${{ matrix.os }}-${{ runner.arch }}-${{ hashFiles('Makefile') }}
# Per-backend deps mirror the proven build-{winit,webview,cef} jobs, plus
# the runtime libs the *run* step needs (xvfb, dbus, libxkbcommon-x11 for
# winit). Note: the backend-common (cef/webview) tray needs no
# appindicator dev package — installing one makes cmake define
# LAUFEY_HAVE_APPINDICATOR while tray_linux.cc includes the non-ayatana
# header, breaking the build; without it the tray compiles as a stub (N/A).
- name: Install dependencies (Linux, winit)
if: runner.os == 'Linux' && matrix.backend == 'winit'
run: |
sudo apt-get update
sudo apt-get install -y make pkg-config xvfb dbus-x11 xclip \
libx11-dev libxcb1-dev libxkbcommon-dev libxkbcommon-x11-0 \
libwayland-dev libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
- name: Install dependencies (Linux, webview)
if: runner.os == 'Linux' && matrix.backend == 'webview'
run: |
sudo apt-get update
sudo apt-get install -y make cmake ninja-build pkg-config xvfb dbus-x11 \
libwebkit2gtk-4.1-dev libgtk-3-dev
- name: Install dependencies (Linux, cef)
if: runner.os == 'Linux' && matrix.backend == 'cef'
run: |
sudo apt-get update
sudo apt-get install -y make cmake ninja-build pkg-config xvfb dbus-x11 \
libx11-dev libxi-dev libglib2.0-dev libnss3-dev libnspr4-dev \
libatk1.0-dev libatk-bridge2.0-dev libcups2-dev libdrm-dev libgtk-3-dev \
libgbm-dev libasound2-dev libpango1.0-dev libcairo2-dev libxcomposite-dev \
libxdamage-dev libxrandr-dev libxkbcommon-dev libdbus-1-dev libxext-dev \
libxfixes-dev
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install llvm ninja
- name: Install dependencies (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
choco install make ninja -y
if [ "${{ matrix.backend }}" = "webview" ]; then
nuget install Microsoft.Web.WebView2 -OutputDirectory packages
fi
# Build the Rust runtime/driver BEFORE msvc-dev-cmd: cargo's own toolchain
# detection invokes MSVC link.exe by full path, but once msvc-dev-cmd has
# run, Git bash's /usr/bin/link shadows it and the build fails.
- name: Build runtime + driver
shell: bash
run: |
cargo build --release -p native_e2e_runtime
if [ "${{ runner.os }}" = "Linux" ]; then
cargo build --release -p native_e2e_driver
fi
- uses: ilammy/msvc-dev-cmd@v1
if: runner.os == 'Windows' && matrix.backend != 'winit'
- name: Build backend
shell: bash
run: |
if [ "${{ runner.os }}" = "Windows" ] && [ "${{ matrix.backend }}" = "webview" ]; then
WEBVIEW2_ROOT="$(pwd)/$(ls -d packages/Microsoft.Web.WebView2.* | head -1)" make webview
else
make ${{ matrix.backend }}
fi
- name: Layer 0 — readback / events / clipboard / menu+tray clicks
shell: bash
run: scripts/native-e2e-run.sh ${{ matrix.backend }}
# Layer 1 (Linux D-Bus StatusNotifier/dbusmenu observer) is built above
# (compile-checked on real Linux) but not run here: the only tray impl
# that registers an SNI is the winit backend's tray-icon crate, and
# winit/linux is excluded above pending its GTK-menu fix. Re-enable the
# driver run once a tray-capable Linux leg is in the matrix.
build-webview:
name: webview / ${{ matrix.target }}
runs-on: ${{ matrix.os }}
# Keep the macOS minimum in sync with LSMinimumSystemVersion in
# webview/macos/Info.plist.in (denoland/deno#35764). Ignored on non-macOS
# runners. The webview CMakeLists also pins this; setting it here guards
# against SDK-default minos stamping regardless of build path.
env:
MACOSX_DEPLOYMENT_TARGET: "10.15"
strategy:
fail-fast: false
matrix:
include:
# 22.04 keeps the glibc/libstdc++ baseline low (glibc 2.35,
# GLIBCXX_3.4.30) so binaries run on Debian 12 / Ubuntu 22.04+.
- { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 }
- { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm }
- { target: x86_64-pc-windows-msvc, os: windows-latest }
- { target: aarch64-pc-windows-msvc, os: windows-11-arm }
- { target: x86_64-apple-darwin, os: macos-15-intel }
- { target: aarch64-apple-darwin, os: macos-14 }
steps:
- uses: actions/checkout@v7
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build pkg-config libwebkit2gtk-4.1-dev libgtk-3-dev
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install llvm ninja
- name: Install dependencies (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
choco install make ninja -y
nuget install Microsoft.Web.WebView2 -OutputDirectory packages
- uses: ilammy/msvc-dev-cmd@v1
if: runner.os == 'Windows'
with:
# Match the MSVC toolchain to the runner arch so cl.exe targets ARM64
# on windows-11-arm; CMake then links the arm64 WebView2 loader.
arch: ${{ runner.arch == 'ARM64' && 'arm64' || 'x64' }}
- name: Build
shell: bash
run: |
if [[ "${{ runner.os }}" == "Windows" ]]; then
WEBVIEW2_ROOT="$(pwd)/$(ls -d packages/Microsoft.Web.WebView2.* | head -1)" make webview
else
make webview
fi
- name: Package
shell: bash
run: |
if [[ "${{ runner.os }}" == "macOS" ]]; then
tar czf laufey-webview-${{ matrix.target }}.tar.gz -C webview/build laufey_webview.app
elif [[ "${{ runner.os }}" == "Windows" ]]; then
cd webview/build && 7z a ../../laufey-webview-${{ matrix.target }}.zip laufey_webview.exe
else
tar czf laufey-webview-${{ matrix.target }}.tar.gz -C webview/build laufey_webview
fi
- uses: actions/upload-artifact@v7
with:
name: laufey-webview-${{ matrix.target }}
path: |
laufey-webview-${{ matrix.target }}.tar.gz
laufey-webview-${{ matrix.target }}.zip
e2e-cef-linux:
name: e2e / cef / x86_64-unknown-linux-gnu (Xvfb)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
prefix-key: e2e-cef-linux
- name: Install runtime + build deps
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build pkg-config \
xvfb xdotool dbus-x11 \
libx11-dev libxi-dev libglib2.0-dev libnss3-dev libnspr4-dev \
libatk1.0-dev libatk-bridge2.0-dev libcups2-dev libdrm-dev \
libgtk-3-dev libgbm-dev libasound2-dev libpango1.0-dev \
libcairo2-dev libxcomposite-dev libxdamage-dev libxrandr-dev \
libxkbcommon-dev libdbus-1-dev libxext-dev libxfixes-dev
- name: Cache CEF vendor
uses: actions/cache@v6
with:
path: vendor/cef
key: cef-ubuntu-latest-${{ runner.arch }}-${{ hashFiles('Makefile') }}
- name: Build CEF backend
run: make cef
- name: Build e2e harness
run: cargo build --release -p cef_e2e_runtime
- name: Launch under Xvfb and assert
# The harness exits 0 on PASS, 1 on FAIL. `xvfb-run` provides
# the headless display; `dbus-launch` gives CEF the session
# bus it expects (Chromium probes it during startup and stalls
# for several seconds if absent).
#
# 90-second outer timeout to fail loud rather than letting CI
# block for the full 6-hour ceiling if a binding pipeline
# regression makes the harness hang forever.
run: |
export LAUFEY_BIN="$PWD/cef/build/Release/laufey"
if [ ! -x "$LAUFEY_BIN" ]; then
echo "::error::CEF backend binary not at $LAUFEY_BIN — build step changed layout?"
ls cef/build/Release || true
exit 1
fi
log_file="$RUNNER_TEMP/laufey-cef-e2e.log"
LAUFEY_CEF_LOG_SEVERITY=error timeout 90 \
xvfb-run --auto-servernum --server-args="-screen 0 1280x800x24" \
dbus-launch --exit-with-session \
"$LAUFEY_BIN" --runtime "$PWD/target/release/libcef_e2e_runtime.so" \
2>&1 | tee "$log_file"
if grep -Eq 'google_apis/gcm/engine/registration_request|DEPRECATED_ENDPOINT' "$log_file"; then
echo "::error::CEF attempted legacy GCM registration"
exit 1
fi
build-cef:
name: cef / ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# 22.04 keeps the glibc/libstdc++ baseline low (glibc 2.35,
# GLIBCXX_3.4.30) so binaries run on Debian 12 / Ubuntu 22.04+.
- { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 }
- { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm }
- { target: x86_64-apple-darwin, os: macos-15-intel }
- { target: aarch64-apple-darwin, os: macos-14 }
- { target: x86_64-pc-windows-msvc, os: windows-latest }
# aarch64-pc-windows-msvc (windows-11-arm) is intentionally omitted:
# CEF-on-Windows-ARM64 needs the windowsarm64 dist + arch-matched MSVC
# setup and is tracked as a separate follow-up (issue #42).
steps:
- uses: actions/checkout@v7
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build pkg-config \
libx11-dev libxi-dev libglib2.0-dev libnss3-dev libnspr4-dev \
libatk1.0-dev libatk-bridge2.0-dev libcups2-dev libdrm-dev \
libgtk-3-dev libgbm-dev libasound2-dev libpango1.0-dev \
libcairo2-dev libxcomposite-dev libxdamage-dev libxrandr-dev \
libxkbcommon-dev libdbus-1-dev libxext-dev libxfixes-dev
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install llvm ninja
- name: Install dependencies (Windows)
if: runner.os == 'Windows'
run: choco install make ninja -y
- uses: ilammy/msvc-dev-cmd@v1
if: runner.os == 'Windows'
- name: Cache CEF
uses: actions/cache@v6
with:
path: vendor/cef
key: cef-${{ matrix.os }}-${{ runner.arch }}-${{ hashFiles('Makefile') }}
- run: make cef
- name: Package
shell: bash
run: |
if [[ "${{ runner.os }}" == "macOS" ]]; then
tar czf laufey-cef-${{ matrix.target }}.tar.gz -C cef/build/Release laufey.app
elif [[ "${{ runner.os }}" == "Windows" ]]; then
cd cef/build/Release && 7z a ../../../laufey-cef-${{ matrix.target }}.zip *
else
strip --strip-unneeded cef/build/Release/libcef.so
tar czf laufey-cef-${{ matrix.target }}.tar.gz -C cef/build/Release .
fi
- uses: actions/upload-artifact@v7
with:
name: laufey-cef-${{ matrix.target }}
path: |
laufey-cef-${{ matrix.target }}.tar.gz
laufey-cef-${{ matrix.target }}.zip
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [build-winit, build-webview, build-cef]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- name: Verify tag matches capi crate version
run: |
tag="${GITHUB_REF#refs/tags/v}"
crate=$(awk -F'"' '/^version = / {print $2; exit}' capi/Cargo.toml)
if [ "$tag" != "$crate" ]; then
echo "::error::Tag v$tag does not match capi crate version $crate"
exit 1
fi
- uses: actions/download-artifact@v8
with:
path: artifacts
merge-multiple: true
- name: Generate SHA256SUMS
working-directory: artifacts
run: |
shopt -s nullglob
files=(*.tar.gz *.zip)
sha256sum "${files[@]}" > SHA256SUMS
cat SHA256SUMS
- uses: dtolnay/rust-toolchain@stable
- name: Publish laufey crate to crates.io
working-directory: capi
# Non-fatal: a missing/under-scoped CARGO_REGISTRY_TOKEN, or a version
# already on crates.io, must not block the GitHub release below.
continue-on-error: true
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish
- name: Create release
uses: softprops/action-gh-release@v3
with:
files: artifacts/*
generate_release_notes: true