Files
oak-editor/.github/workflows/ci.yml
T
Mike-Solar 5f92db9dc7 ci/cd: install fonts for the font-backend test; CD publishes drafts
- the Arch (and Debian/Fedora) containers carried no system fonts, so
  the effectchain font-family test failed with "the font backend found
  no families": install dejavu (ttf-dejavu / dejavu-sans-fonts /
  fonts-dejavu-core) on every Linux entry;
- the release job now creates a DRAFT release: a human reviews the assets
  and publishes deliberately, and a published version is never silently
  replaced (the repo also protects v* tags from being moved).
2026-09-24 19:44:21 +08:00

627 lines
28 KiB
YAML

name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
# One matrix, seven platforms: the three Linux-family packaging distros
# (Debian 12, Fedora 43, Arch), openKylin on x64 and arm64, macOS and
# Windows. The environments match .github/workflows/cd.yml exactly (same
# container images, same dependency lists, same runner sizes), so a
# "passes CI, fails CD" dependency drift is caught here first.
jobs:
build-test:
name: Build & test (${{ matrix.name }})
runs-on: ${{ matrix.runner }}
# Container entries carry the container as JSON ({"image":...,
# "options":...}); the empty string means "run on the host"
# (actions/runner#265 allows an empty container value).
container: ${{ matrix.container != '' && fromJSON(matrix.container) || '' }}
# The Test step's watchdog caps a hung suite at 30 min; leave a cold
# FFmpeg build + full compile room beyond that.
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
- name: Debian
platform: linux
distro: debian
arch: x64
runner: warp-ubuntu-latest-x64-8x
triplet: x64-linux
container: '{"image":"debian:12","options":"--shm-size=8g"}'
- name: Fedora
platform: linux
distro: fedora
arch: x64
runner: warp-ubuntu-latest-x64-8x
triplet: x64-linux
container: '{"image":"fedora:43","options":"--shm-size=8g"}'
- name: Arch
platform: linux
distro: arch
arch: x64
runner: warp-ubuntu-latest-x64-8x
triplet: x64-linux
container: '{"image":"archlinux:latest","options":"--shm-size=8g"}'
- name: openKylin x64
platform: linux
distro: openkylin
arch: x64
runner: warp-ubuntu-latest-x64-8x
triplet: x64-linux
container: '{"image":"openkylin/openkylin:latest","options":"--shm-size=8g"}'
- name: openKylin arm64
platform: linux
distro: openkylin
arch: arm64
runner: warp-ubuntu-latest-arm64-16x
triplet: arm64-linux
container: '{"image":"openkylin/openkylin:latest","options":"--shm-size=8g"}'
- name: macOS
platform: macos
distro: macos
arch: arm64
runner: warp-macos-26-arm64-12x
triplet: arm64-osx
container: ''
- name: Windows
platform: windows
distro: windows
arch: x64
runner: warp-windows-2025-vs2026-x64-32x
triplet: x64-windows
container: ''
steps:
# The container images are bare (Fedora/Arch even lack git);
# checkout needs git/curl, and WarpCache needs wget inside
# a container (its README requires it). First step of the job, so
# the package lists are still fresh.
- name: Bootstrap container (git, curl, wget)
if: matrix.container != ''
shell: bash
run: |
case "${{ matrix.distro }}" in
fedora) dnf install -y --setopt=install_weak_deps=False --setopt=max_parallel_downloads=16 git curl wget which ;;
arch) pacman -Sy --noconfirm git curl wget which ;;
debian|openkylin) apt-get update && apt-get install -y git curl ca-certificates wget ;;
esac
- name: Checkout
uses: actions/checkout@v7
with:
# gpui/ is a git submodule; its crates are workspace members of
# their own repo and build as path dependencies of oakapp.
submodules: true
# Taste the disk before the toolchains land: Defender scans every
# file the cargo/FFmpeg builds touch (tens of thousands of small
# writes), which dominates a cold Windows build. The runner is an
# ephemeral VM, so the scanner is turned off for the job
# (exclusions are kept as a fallback for images where real-time
# protection cannot be disabled).
- name: Disable Windows Defender scanning
if: matrix.platform == 'windows'
shell: pwsh
run: |
try {
Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction Stop
Set-MpPreference -DisableScriptScanning $true -ErrorAction SilentlyContinue
Set-MpPreference -DisableArchiveScanning $true -ErrorAction SilentlyContinue
Write-Host "Windows Defender real-time scanning disabled for this job"
} catch {
Write-Host "Windows Defender could not be disabled (non-fatal, falling back to exclusions): $_"
}
foreach ($path in @(
$env:GITHUB_WORKSPACE,
"$env:USERPROFILE\.cargo",
"$env:USERPROFILE\.rustup"
)) {
Add-MpPreference -ExclusionPath $path -ErrorAction SilentlyContinue
}
try {
Get-MpPreference |
Select-Object DisableRealtimeMonitoring, DisableScriptScanning, ExclusionPath |
Format-List
} catch {
Write-Host "Defender status unavailable: $_"
}
# The containers run as root but Actions sets HOME=/github/home;
# rustup refuses the euid mismatch ("$HOME differs from
# euid-obtained home directory") and would install a toolchain the
# later steps cannot find under the Actions home. Pin the job to
# root's home so rustup/cargo and the toolchain agree.
- name: Pin HOME for rustup
if: matrix.container != ''
shell: bash
run: |
{
echo "HOME=/root"
echo "CARGO_HOME=/root/.cargo"
echo "RUSTUP_HOME=/root/.rustup"
} >> "$GITHUB_ENV"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
# The Windows build is MSVC-ABI (the runner carries VS 2026):
# The prebuilt FFmpeg and the vendored OCIO build both want it.
toolchain: ${{ matrix.platform == 'windows' && 'stable-x86_64-pc-windows-msvc' || 'stable' }}
# ------------------------------------------------------------------
# System dependencies — one list per distro, byte-for-byte the same
# lists CD uses (see .github/workflows/cd.yml): a package a CD build
# needs cannot be missing here.
# ------------------------------------------------------------------
- name: Install system dependencies (Debian)
if: matrix.distro == 'debian'
shell: bash
run: |
apt-get update
apt-get install -y \
build-essential clang libclang-dev cmake pkg-config nasm \
git curl zip unzip tar python3 dpkg-dev \
libpipewire-0.3-dev libspa-0.2-dev libjack-jackd2-dev \
libasound2-dev libpulse-dev libsndfile1-dev \
libgl1-mesa-dev libgl1-mesa-dri mesa-vulkan-drivers \
libvulkan-dev libxkbcommon-dev libxkbcommon-x11-dev xvfb libdrm-dev \
icc-profiles-free gdb file librsvg2-bin patchelf fonts-dejavu-core \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (Fedora)
if: matrix.distro == 'fedora'
shell: bash
run: |
# Fedora 41 is EOL (its mirrors moved to the slow archive), so
# the matrix uses the current release; weak dependencies (docs,
# fonts, optional tooling) are skipped and downloads run wide.
dnf install -y --setopt=install_weak_deps=False \
--setopt=max_parallel_downloads=16 \
gcc gcc-c++ clang clang-devel cmake pkgconf-pkg-config nasm \
git curl zip unzip tar python3 patch xz which \
pipewire-devel jack-audio-connection-kit-devel \
alsa-lib-devel pulseaudio-libs-devel libsndfile-devel \
mesa-libGL-devel mesa-vulkan-drivers \
vulkan-headers vulkan-loader-devel \
libxkbcommon-devel libxkbcommon-x11-devel \
rpm-build librsvg2-tools libdrm-devel \
perl-IPC-Cmd perl-FindBin perl-File-Basename perl-File-Compare \
perl-File-Copy perl-File-Path perl-File-Temp perl-Time-Piece \
xorg-x11-server-Xvfb xorg-x11-xauth gdb file dejavu-sans-fonts \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (Arch)
if: matrix.distro == 'arch'
shell: bash
run: |
pacman -S --needed --noconfirm \
base-devel clang cmake pkgconf nasm \
git curl zip unzip tar python patch xz which \
pipewire jack2 alsa-lib libpulse libsndfile \
mesa vulkan-headers vulkan-icd-loader \
libxkbcommon libxkbcommon-x11 librsvg libdrm \
xorg-server-xvfb xorg-xauth gdb file ttf-dejavu \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (openKylin)
if: matrix.distro == 'openkylin'
shell: bash
run: |
apt-get update
apt-get install -y \
build-essential clang libclang-dev cmake pkg-config nasm \
git curl zip unzip tar python3 patch xz-utils dpkg-dev \
libpipewire-0.3-dev libspa-0.2-dev libjack-jackd2-dev \
libasound2-dev libpulse-dev libsndfile1-dev \
libgl1-mesa-dev libgl1-mesa-dri mesa-vulkan-drivers \
libvulkan-dev libxkbcommon-dev libxkbcommon-x11-dev xvfb libdrm-dev \
gdb file patchelf fonts-dejavu-core \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (macOS)
if: matrix.platform == 'macos'
run: |
# Homebrew's pkgconf installs a `pkg-config` symlink, which is
# the name crates/oak-ffmpeg-link/build.rs invokes; nasm is what the
# project FFmpeg build (tooling/ffmpeg/build-ffmpeg.sh) requires.
# librsvg is CD's icon renderer.
brew install cmake pkg-config nasm librsvg autoconf automake libtool autoconf-archive
# Windows uses BtbN's prebuilt FFmpeg (GPL, shared): the archive
# ships include/, MSVC import libs (.lib), pkg-config files and the
# runtime DLLs, built from FFmpeg's release/8.1 branch by BtbN's
# public GitHub Actions (BtbN is an FFmpeg developer and ffmpeg.org
# links these builds). Downloaded from his release page and
# verified against the checksums.sha256 published in the same
# release; nothing is mirrored here, so provenance stays upstream.
# The alternative — a source build — costs ~40 minutes per run on
# this platform.
- name: Install prebuilt FFmpeg (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
$base = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest"
$asset = "ffmpeg-n8.1-latest-win64-gpl-shared-8.1.zip"
$dir = Join-Path $env:RUNNER_TEMP "ffmpeg-prebuilt"
New-Item -ItemType Directory -Force $dir | Out-Null
Invoke-WebRequest -Uri "$base/checksums.sha256" -OutFile "$dir\checksums.sha256"
$line = Select-String -Path "$dir\checksums.sha256" -Pattern ([regex]::Escape($asset) + "\s*$") |
Select-Object -First 1
if (-not $line) { throw "no checksum for $asset in the release's checksums.sha256" }
$expected = $line.Line.Split()[0].ToLowerInvariant()
Invoke-WebRequest -Uri "$base/$asset" -OutFile "$dir\$asset"
$actual = (Get-FileHash "$dir\$asset" -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actual -ne $expected) { throw "FFmpeg checksum mismatch: expected $expected, got $actual" }
$root = "$env:GITHUB_WORKSPACE\.cache"
Remove-Item "$root\ffmpeg", "$root\ffmpeg-extract" -Recurse -Force -ErrorAction SilentlyContinue
Expand-Archive -Path "$dir\$asset" -DestinationPath "$root\ffmpeg-extract" -Force
$inner = Get-ChildItem "$root\ffmpeg-extract" -Directory | Select-Object -First 1
Move-Item $inner.FullName "$root\ffmpeg"
# ------------------------------------------------------------------
# FFmpeg + caches
# ------------------------------------------------------------------
# The Linux/macOS jobs build the project FFmpeg from source with the
# distro's codec packages — the same path local builds use
# (tooling/install-deps.sh + tooling/ffmpeg/build-ffmpeg.sh; the
# latter also drives the vendored static OCIO). Windows uses the
# prebuilt archive downloaded above.
- name: Install FFmpeg dependencies
if: matrix.platform != 'windows'
shell: bash
run: tooling/install-deps.sh
# The built FFmpeg is cached as a whole (keyed on the script, distro
# and arch): a cache hit skips the ~10 minute build. The
# `.build-complete` marker distinguishes a finished build from a
# partial cache save; without it the tree is rebuilt.
- name: Restore FFmpeg (WarpCache)
if: matrix.platform == 'linux'
id: ffmpeg-cache
uses: WarpBuilds/cache/restore@v2
# A job container does not inherit the runner environment;
# WarpCache authenticates with this token (README: "Running
# inside a container").
env:
WARPBUILD_RUNNER_VERIFICATION_TOKEN: ${{ env.WARPBUILD_RUNNER_VERIFICATION_TOKEN }}
with:
path: .cache/ffmpeg
key: ffmpeg-${{ matrix.distro }}-${{ matrix.arch }}-${{ hashFiles('tooling/ffmpeg/build-ffmpeg.sh') }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
ffmpeg-${{ matrix.distro }}-${{ matrix.arch }}-${{ hashFiles('tooling/ffmpeg/build-ffmpeg.sh') }}-
ffmpeg-${{ matrix.distro }}-${{ matrix.arch }}-
- name: Restore FFmpeg (GitHub)
if: matrix.platform == 'macos'
id: ffmpeg-cache-github
uses: actions/cache/restore@v6
with:
path: .cache/ffmpeg
key: ffmpeg-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('tooling/ffmpeg/build-ffmpeg.sh') }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
ffmpeg-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('tooling/ffmpeg/build-ffmpeg.sh') }}-
ffmpeg-${{ runner.os }}-${{ matrix.arch }}-
- name: Build FFmpeg
if: matrix.platform != 'windows'
shell: bash
run: |
if [ -f .cache/ffmpeg/.build-complete ]; then
echo "FFmpeg restored from cache; skipping the build"
else
rm -rf .cache/ffmpeg
tooling/ffmpeg/build-ffmpeg.sh
# The cargo cache may hold ffmpeg-sys-next rlibs that bundled
# the previous archives (cargo does not track archive
# changes); flag a refresh so the build relinks the new one.
echo "FFMPEG_REBUILT=1" >> "$GITHUB_ENV"
fi
- name: Save FFmpeg (WarpCache)
if: always() && matrix.platform == 'linux'
uses: WarpBuilds/cache/save@v2
env:
WARPBUILD_RUNNER_VERIFICATION_TOKEN: ${{ env.WARPBUILD_RUNNER_VERIFICATION_TOKEN }}
with:
path: .cache/ffmpeg
key: ${{ steps.ffmpeg-cache.outputs.cache-primary-key }}
- name: Save FFmpeg (GitHub)
if: always() && matrix.platform == 'macos'
uses: actions/cache/save@v6
with:
path: .cache/ffmpeg
key: ${{ steps.ffmpeg-cache-github.outputs.cache-primary-key }}
# ------------------------------------------------------------------
# Build environment
# ------------------------------------------------------------------
# ocio-sys builds a stub bridge unless these are set; the oak-core
# ocioutils tests need the real library.
# tooling/ocio-env.sh: vendored static OCIO (the [patch.crates-io]
# ocio-sys tracks shaloong/ocio-rs main, whose vendored sources build
# on GCC >= 16).
- name: Configure build environment (Linux)
if: matrix.platform == 'linux'
shell: bash
run: |
{
echo "CC=clang"
echo "CXX=clang++"
} >> "$GITHUB_ENV"
bash tooling/ocio-env.sh >> "$GITHUB_ENV"
ffmpeg="$PWD/.cache/ffmpeg"
echo "FFMPEG_DIR=$ffmpeg" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=$ffmpeg/lib/pkgconfig" >> "$GITHUB_ENV"
- name: Configure build environment (macOS)
if: matrix.platform == 'macos'
shell: bash
run: |
# Vendored static OCIO (same as every non-Windows platform via
# tooling/ocio-env.sh); no OCIO_INSTALL_DIR override.
bash tooling/ocio-env.sh >> "$GITHUB_ENV"
ffmpeg="$PWD/.cache/ffmpeg"
echo "FFMPEG_DIR=$ffmpeg" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=$ffmpeg/lib/pkgconfig" >> "$GITHUB_ENV"
- name: Configure build environment (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
# FFmpeg comes from the prebuilt archive extracted above.
$ffmpeg = "$env:GITHUB_WORKSPACE\.cache\ffmpeg"
"FFMPEG_DIR=$ffmpeg" >> $env:GITHUB_ENV
"PKG_CONFIG_PATH=$ffmpeg\lib\pkgconfig" >> $env:GITHUB_ENV
# The test binaries load the FFmpeg DLLs: without this the
# runner reports STATUS_DLL_NOT_FOUND when the first test
# executable starts.
"$ffmpeg\bin" >> $env:GITHUB_PATH
# Bundled OCIO: ocio-sys' vendored sources build with the MSVC
# toolchain (what they need — the MSYS2 package was the
# workaround, not the preference), so no OCIO_INSTALL_DIR and
# no OCIO_RS_NO_MSVC_INCLUDES anywhere.
"OCIO_RS_ENABLE_REAL=1" >> $env:GITHUB_ENV
"OCIO_RS_LINK=static" >> $env:GITHUB_ENV
# ------------------------------------------------------------------
# Cargo caches: whole target/ dir plus ~/.cargo, shared per
# distro+arch (WarpCache for Linux, GitHub for macOS/Windows).
# ------------------------------------------------------------------
- name: Cache cargo artifacts (WarpCache)
if: matrix.platform == 'linux'
uses: WarpBuilds/rust-cache@v2
env:
WARPBUILD_RUNNER_VERIFICATION_TOKEN: ${{ env.WARPBUILD_RUNNER_VERIFICATION_TOKEN }}
with:
shared-key: oak-ci-${{ matrix.distro }}-${{ matrix.arch }}
cache-on-failure: true
- name: Cache cargo artifacts (GitHub)
if: matrix.platform != 'linux'
uses: Swatinem/rust-cache@v2
with:
shared-key: oak-ci-${{ matrix.distro }}-${{ matrix.arch }}
cache-on-failure: true
# A freshly built FFmpeg must not keep linking the archives the
# cargo cache bundled in ffmpeg-sys-next's rlib (cargo does not
# track them); drop just that package so the link picks up the new
# .cache/ffmpeg. Skipped on cache hits and on Windows (prebuilt).
- name: Refresh the FFmpeg binding
if: matrix.platform != 'windows' && env.FFMPEG_REBUILT == '1'
run: cargo clean -p ffmpeg-sys-next
# ------------------------------------------------------------------
# Build & test
# ------------------------------------------------------------------
# `cargo check` (not build): the Test step links the test binaries
# anyway, and a full build would codegen every workspace crate twice
# (once without and once with cfg(test)).
- name: Build
run: cargo check --workspace --locked
# xvfb + 24-bit screen: the gpui #[gpui::test] tests open real
# windows and render through wgpu on Mesa's software Vulkan
# (lavapipe). The watchdog bounds the step: a deadlocked test
# produces no output and no failure, so after 1800 s it dumps every
# hung process's thread stacks and kills the suite.
- name: Test (Linux)
if: matrix.platform == 'linux'
timeout-minutes: 45
shell: bash
env:
# lavapipe is present in these images: a missing adapter must
# fail the GPU acceptance tests instead of silently skipping
# them (Debian is the x64 reference; the other distros keep the
# historical lenient policy).
OAK_REQUIRE_GPU: ${{ matrix.distro == 'debian' && '1' || '0' }}
run: |
run_suite() {
xvfb-run -a -s "-screen 0 1920x1080x24" cargo test --workspace --locked &
TEST_PID=$!
# The watchdog inherits the step's stdout/stderr; detach it so
# it cannot keep the runner's I/O pipes open after the step
# ends ("WaitDelay expired before I/O complete" otherwise).
(
sleep 1800
echo "::warning::test suite exceeded 1800s; dumping hung-process stacks"
for p in $(pgrep -f 'target/debug/deps/|target/debug/oak-worker'); do
echo "===== thread stacks of pid $p ($(readlink /proc/$p/exe 2>/dev/null)) ====="
gdb -batch -ex 'thread apply all bt' -p "$p" || true
done
pkill -9 -f 'target/debug/deps/' || true
pkill -9 -f 'target/debug/oak-worker' || true
) >/dev/null 2>&1 &
WATCHDOG_PID=$!
wait $TEST_PID
rc=$?
kill $WATCHDOG_PID 2>/dev/null || true
# Reap the watchdog so no child holds the step's pipes.
wait $WATCHDOG_PID 2>/dev/null || true
return $rc
}
# Retry once (same policy as the Windows job): worker-pool
# startup under full-suite parallelism has flaked once
# (full_res_worker_outlives_a_dropped_project: the render
# manager's process dispatcher failed to start while every other
# test passed). A real regression fails both passes.
if ! run_suite; then
echo "first pass failed; retrying once for worker-pool flakes"
run_suite
fi
# The runner's GUI session doubles as the display for the gpui
# #[gpui::test] windows; wgpu renders through Metal.
- name: Test (macOS)
if: matrix.platform == 'macos'
timeout-minutes: 40
run: |
# Retry once (same policy as the other platforms). A hang trips
# the in-script watchdog, which samples the test processes (the
# offending test's native stack lands in the log) before killing
# the suite.
run_suite() {
cargo test --workspace --locked &
TEST_PID=$!
(
sleep 900
echo "::warning::macOS test suite exceeded 900s; sampling hung processes"
for p in $(pgrep -f 'target/debug/deps/' || true); do
echo "===== sample of pid $p ====="
sample "$p" 2 10 2>&1 | head -120 || true
done
pkill -9 -f 'target/debug/deps/' || true
pkill -9 -f 'target/debug/oak-worker' || true
) &
WATCHDOG_PID=$!
wait $TEST_PID
rc=$?
kill $WATCHDOG_PID 2>/dev/null || true
wait $WATCHDOG_PID 2>/dev/null || true
return $rc
}
if ! run_suite; then
echo "first pass failed; retrying once for worker-pool flakes"
run_suite
fi
- name: Test (Windows)
if: matrix.platform == 'windows'
timeout-minutes: 40
shell: pwsh
run: |
cargo test --workspace --locked
if ($LASTEXITCODE -ne 0) {
# Retry once: a few gpui keystroke tests flake on Windows CI —
# a synthetic keystroke is occasionally never delivered (the
# undo/redo pair and a plain 's' toggle both failed once,
# each identically to its pass state). A real regression
# fails both passes.
Write-Host "first pass failed; retrying once for gpui keystroke flakes"
cargo test --workspace --locked
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}
# ------------------------------------------------------------------
# Failure diagnostics
# ------------------------------------------------------------------
# A crashing (SIGSEGV) test gives no Rust backtrace; rerun the
# crashing test binaries under gdb to capture the native stack.
# `--args` is required — plain `--` makes gdb treat the test args as
# a core file. The extra probes target loader-stage crashes (the
# copier_test SIGSEGV happens inside ld.so's dl_main): si_addr/si_code
# pin down the fault type, the dynsym dump exposes symbols the
# executable exports for interposition, strace shows the last loader
# syscalls, and valgrind catches a corrupting static initializer.
- name: Backtrace on test failure (Debian)
if: failure() && matrix.distro == 'debian'
shell: bash
run: |
apt-get install -y gdb strace valgrind
for name in node_e2e_test suites_test copier_test; do
BIN=$(ls -t target/debug/deps/$name-* | grep -v '\.d$' | head -1)
[ -n "$BIN" ] || continue
echo "===== $BIN ====="
file "$BIN" || true
echo "--- exported defined dynsyms:"
readelf --dyn-syms -W "$BIN" 2>/dev/null | grep -v ' UND ' | tail -n +4 | head -30 || true
echo "--- strace tail:"
strace -f "$BIN" --list 2>&1 | tail -15 || true
echo "--- valgrind tail:"
valgrind -q "$BIN" --list 2>&1 | tail -25 || true
echo "--- gdb:"
xvfb-run -a gdb -batch \
-ex run \
-ex 'bt' \
-ex 'p $_siginfo.si_code' \
-ex 'p/x $_siginfo._sifields._sigfault.si_addr' \
-ex 'x/6i $rip' \
--args "$BIN" --nocapture || true
done
# Same idea for the other distro containers (gdb is installed with
# the system dependencies).
- name: Backtrace on test failure (containers)
if: failure() && matrix.platform == 'linux' && matrix.distro != 'debian'
shell: bash
run: |
for name in oak_render oakapp oak_plugin; do
BIN=$(ls -t target/debug/deps/$name-* 2>/dev/null | grep -v '\.d$' | head -1)
[ -n "$BIN" ] || continue
echo "===== $BIN ====="
timeout 900 xvfb-run -a gdb -batch \
-ex run \
-ex 'thread apply all bt' \
--args "$BIN" || true
done
# A SIGSEGV in a test binary gives no Rust backtrace; Apple's crash
# reports carry the native stack, so surface the newest ones.
- name: Crash reports (macOS)
if: failure() && matrix.platform == 'macos'
run: |
for f in $(ls -t ~/Library/Logs/DiagnosticReports/*.ips 2>/dev/null | head -3); do
echo "===== $f"
head -c 6000 "$f"
echo
done
# ------------------------------------------------------------------
# OFX plugin discovery end-to-end (x64 Linux reference)
# ------------------------------------------------------------------
# Build a minimal but real OFX plugin into a .ofx.bundle, point
# OFX_PLUGIN_PATH at it and let the scan_probe example run the full
# host path (directory scan -> dlopen -> setHost -> load -> describe
# -> register). The assertion is the plugin's registration line; CI
# machines have no system-wide OFX plugins, so the fixture is the
# only discovery.
- name: Build OFX fixture plugin
if: matrix.distro == 'debian'
run: crates/oak-plugin/tests/fixtures/build_fixture.sh .cache/ofx-fixture
- name: Probe OFX plugin discovery
if: matrix.distro == 'debian'
run: |
OFX_PLUGIN_PATH="$PWD/.cache/ofx-fixture" \
cargo run --locked -p oak-plugin --example scan_probe > probe.log 2>&1
grep -q 'type_id=rs.oak.CiTestPlugin' probe.log
# A project carrying a plugin node must survive save/load (the
# serializer resolves plugin types via the dynamic factory).
OAK_OFX_FIXTURE_DIR="$PWD/.cache/ofx-fixture" \
cargo test --locked -p oak-plugin --test ofx_roundtrip