The Debian container moves from bookworm to trixie. Two packages were renamed in trixie and their old names are now transitional dummies: pkg-config is provided by pkgconf, and libgl1-mesa-dev split into libgl-dev + libglvnd-dev. The CI and CD Debian dependency lists stay byte-for-byte identical. trixie ships libva 2.22, which has the vaMapBuffer2 FFmpeg 8 expects, so only openKylin still needs the bundled vcpkg libva copies.
627 lines
28 KiB
YAML
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 13, 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:13","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 pkgconf 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 \
|
|
libgl-dev libglvnd-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
|