name: ci
on:
push:
branches: ["**"]
tags: ["v[0-9]+.[0-9]+.[0-9]+*"]
pull_request:
workflow_dispatch:
inputs:
day-main:
description: Build against day's main branch instead of the newest release
type: boolean
default: false
measure-sizes:
description: Compare native-only and Servo release artifact sizes
type: boolean
default: true
schedule:
- cron: "0 11 * * *"
permissions:
contents: read
jobs:
host:
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, features: gtk }
- { os: ubuntu-latest, features: qt }
- { os: macos-15, features: appkit }
- { os: xcode-27, features: appkit }
- { os: windows-latest, features: winui }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: daybrite/actions/.github/actions/day-deps@v1
with:
day-deps: ${{ inputs.day-main && 'main' || 'auto' }}
- name: Install the toolkit and web engine libraries
if: runner.os == 'Linux'
uses: daybrite/actions/.github/actions/setup-day-deps@v1
with:
target: linux-${{ matrix.features }}
pack: false
extras: true
java: false
- run: cargo fmt --all -- --check
if: matrix.features == 'gtk'
- run: cargo fmt --manifest-path servo/Cargo.toml --all -- --check
if: matrix.features == 'gtk'
- name: Verify native consumer dependency isolation
if: matrix.features == 'gtk'
run: python3 scripts/check-native-resolution.py
- run: cargo clippy --all-targets -- -D warnings
- run: cargo clippy --features ${{ matrix.features }} -- -D warnings
- run: cargo test
- name: Test AppKit renderer registration
if: matrix.features == 'appkit'
run: cargo test --features appkit --lib
- name: Test Windows evaluation payload conversion
if: runner.os == 'Windows'
run: |
cmake -S tests/xaml -B target/xaml-tests
cmake --build target/xaml-tests --config Release
ctest --test-dir target/xaml-tests -C Release --output-on-failure
- uses: actions/setup-node@v6
if: matrix.features == 'gtk'
with:
node-version: '22'
- run: node --test tests/*.mjs
if: matrix.features == 'gtk'
- run: python3 -m unittest discover -s scripts -p test_renderer_comparison.py -v
if: matrix.features == 'gtk'
- run: python3 -m unittest discover -s scripts -p test_windows_angle.py -v
if: matrix.features == 'gtk'
- run: python3 -m unittest discover -s scripts -p test_renderer_size.py -v
if: matrix.features == 'gtk'
cross-check:
strategy:
fail-fast: false
matrix:
include:
- { os: macos-15, target: aarch64-apple-ios-sim, features: uikit }
- { os: ubuntu-latest, target: aarch64-linux-android, features: material }
- { os: ubuntu-latest, target: wasm32-unknown-unknown, features: dom, rustflags: '--cfg getrandom_backend="custom"' }
runs-on: ${{ matrix.os }}
env:
RUSTFLAGS: ${{ matrix.rustflags }}
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: daybrite/actions/.github/actions/day-deps@v1
with:
day-deps: ${{ inputs.day-main && 'main' || 'auto' }}
- uses: actions/setup-java@v5
if: matrix.features == 'material'
with:
distribution: temurin
java-version: '17'
- name: Test Android resource content-type handling
if: matrix.features == 'material'
run: |
javac -d target/android-resource-tests src/DayWebContentType.java tests/android/ContentTypeTest.java
java -cp target/android-resource-tests dev.daybrite.day.piece.webview.ContentTypeTest
- run: cargo clippy --target ${{ matrix.target }} --features ${{ matrix.features }} -- -D warnings
renderer-comparison:
name: renderer comparison (${{ matrix.target }})
strategy:
fail-fast: false
matrix:
include:
- { os: xcode-27, target: macos-appkit }
- { os: macos-15, target: macos-gtk }
- { os: macos-15, target: macos-qt }
- { os: ubuntu-24.04, target: linux-gtk }
- { os: ubuntu-24.04, target: linux-qt }
- { os: windows-latest, target: windows-winui }
- { os: ubuntu-24.04, target: android-material }
runs-on: ${{ matrix.os }}
timeout-minutes: 240
permissions:
contents: read
env:
CARGO_BUILD_JOBS: 2
CARGO_INCREMENTAL: 0
CARGO_PROFILE_DEV_DEBUG: 0
DAY_TARGET: ${{ matrix.target }}
DAY_LOG: warn
RENDERER_ROOT: .
DEMO_DIR: demo
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: daybrite/actions/.github/actions/day-deps@v1
with:
day-deps: ${{ inputs.day-main && 'main' || 'auto' }}
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- uses: daybrite/actions/.github/actions/setup-day-cli@v1
- uses: daybrite/actions/.github/actions/setup-day-deps@v1
with:
target: ${{ matrix.target }}
pack: false
extras: true
java: ${{ matrix.target == 'android-material' || matrix.target == 'harmony-arkui' }}
packages: >-
${{ runner.os == 'macOS' && 'python@3.12 llvm'
|| runner.os == 'Windows' && 'webview2-runtime llvm cmake ninja'
|| runner.os == 'Linux' && 'clang-19 libclang-19-dev llvm-19-dev cmake ninja-build
pkg-config libudev-dev libegl1-mesa-dev libgles2-mesa-dev libgl1-mesa-dev
libfontconfig1-dev libfreetype6-dev libx11-dev libxrandr-dev libxcursor-dev libxi-dev xvfb'
|| '' }}
- name: Configure macOS Servo tools
if: runner.os == 'macOS'
shell: bash
run: |
echo "DAY_BUILD_TOOL_PATH=$(brew --prefix python@3.12)/libexec/bin:$(brew --prefix llvm)/bin" >> "$GITHUB_ENV"
echo "$(brew --prefix python@3.12)/libexec/bin" >> "$GITHUB_PATH"
echo "$(brew --prefix llvm)/bin" >> "$GITHUB_PATH"
test "$(uname -m)" = arm64
- name: Configure Linux Servo tools
if: runner.os == 'Linux'
shell: bash
run: |
# setup-day-deps installs the toolkit and the extra Servo packages above.
# Check before compiling Servo so a missing library fails in setup.
case "${{ matrix.target }}" in
linux-gtk)
pkg-config --print-errors --exists 'librsvg-2.0 >= 2.52'
;;
linux-qt)
pkg-config --print-errors --exists Qt6Svg
;;
esac
echo /usr/lib/llvm-19/bin >> "$GITHUB_PATH"
echo LIBCLANG_PATH=/usr/lib/llvm-19/lib >> "$GITHUB_ENV"
echo LIBGL_ALWAYS_SOFTWARE=1 >> "$GITHUB_ENV"
- uses: ilammy/msvc-dev-cmd@v1
if: runner.os == 'Windows'
- name: Test Linux graphics context switching
if: startsWith(matrix.target, 'linux-')
shell: bash
run: |
rustc --edition 2024 --test tests/linux/gl-context.rs -D warnings -o "$RUNNER_TEMP/linux-gl-context"
xvfb-run -a "$RUNNER_TEMP/linux-gl-context"
- name: Configure Windows Servo tools
if: runner.os == 'Windows'
shell: pwsh
run: |
'C:/Program Files/LLVM/bin' >> $env:GITHUB_PATH
'LIBCLANG_PATH=C:/Program Files/LLVM/bin' >> $env:GITHUB_ENV
# Git Bash prepends its own link.exe, even after msvc-dev-cmd sets PATH.
$linker = Join-Path $env:VCToolsInstallDir 'bin/Hostx64/x64/link.exe'
if (!(Test-Path $linker)) { throw "MSVC linker missing: $linker" }
"CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=$linker" >> $env:GITHUB_ENV
clang --version
# mozangle 0.7.1's build script links libGLESv2.dll by passing all 196 object paths to
# cl.exe on one command line. Under the runner's D:\a\<repo>\<repo>\demo\build\day\
# flavors\servo\cargo\<target>\debug\debug\build\mozangle-<hash>\out prefix that is
# ~36k characters, past Windows' 32,767 limit: CreateProcess fails with error 206 ("The
# filename or extension is too long") and the script panics. Day canonicalizes its
# project root, so subst/junction aliases resolve back to the long workspace path.
# Clone the checked-out commit to a physically short path on this ephemeral runner.
# Remove once mozangle links through a response file.
$shortRoot = Join-Path ([IO.Path]::GetPathRoot($env:GITHUB_WORKSPACE)) 'wv'
if (Test-Path $shortRoot) { throw "Short checkout already exists: $shortRoot" }
git clone --local --no-hardlinks $env:GITHUB_WORKSPACE $shortRoot
if ($LASTEXITCODE -ne 0) { throw 'Creating the short checkout failed' }
$revision = git -C $env:GITHUB_WORKSPACE rev-parse HEAD
git -C $shortRoot checkout --detach $revision
if ($LASTEXITCODE -ne 0) { throw 'Selecting the CI revision failed' }
"RENDERER_ROOT=$shortRoot" >> $env:GITHUB_ENV
"DEMO_DIR=$shortRoot/demo" >> $env:GITHUB_ENV
- name: Install Servo Android NDK
if: matrix.target == 'android-material'
shell: bash
run: |
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" 'ndk;28.2.13676358'
echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/28.2.13676358" >> "$GITHUB_ENV"
echo DAY_ANDROID_ABI=x86_64 >> "$GITHUB_ENV"
- name: Cache Servo OpenHarmony native SDK
if: matrix.target == 'harmony-arkui'
uses: actions/cache@v6
with:
path: ${{ runner.temp }}/servo-ohos-sdk-6.0.0.1/native
key: servo-ohos-native-linux-6.0.0.1-42955c8106ca
- name: Install Servo OpenHarmony native SDK (API 20)
if: matrix.target == 'harmony-arkui'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
# Keep the API-18 packaging SDK installed by setup-day-deps intact.
bash scripts/install-servo-ohos-sdk.sh
{
echo DAY_OHOS_ARCH=x86_64
echo DAY_OHOS_SHOT_SETTLE_MS=2500
} >> "$GITHUB_ENV"
- uses: Swatinem/rust-cache@v2
with:
workspaces: ${{ env.DEMO_DIR }} -> build/day/flavors/servo/cargo/${{ matrix.target }}/debug
shared-key: servo-${{ matrix.target }}
- name: Build ARM64 mobile application
if: matrix.target == 'android-material' || matrix.target == 'harmony-arkui'
working-directory: ${{ env.DEMO_DIR }}
env:
DAY_ANDROID_ABI: arm64-v8a
DAY_OHOS_ARCH: arm64
shell: bash
run: '"$DAY_BIN" build -t "$DAY_TARGET" --flavor servo'
- name: Build the mixed-renderer application
id: mixed-build
working-directory: ${{ env.DEMO_DIR }}
shell: bash
run: '"$DAY_BIN" build -t "$DAY_TARGET" --flavor servo'
- name: Stage and check Windows ANGLE runtime
if: runner.os == 'Windows'
working-directory: ${{ env.RENDERER_ROOT }}
shell: bash
run: python scripts/stage-windows-angle.py "demo/build/day/flavors/servo/cargo/$DAY_TARGET/debug/debug"
- name: Enable emulator acceleration
if: matrix.target == 'android-material' || matrix.target == 'harmony-arkui'
shell: bash
run: sudo chmod a+rw /dev/kvm
- name: Boot Android comparison device
if: matrix.target == 'android-material'
shell: bash
run: |
"$DAY_BIN" devices setup -t android-material --name day-servo-ci --device pixel_7 --os 36 --arch x86_64
SERIAL="$("$DAY_BIN" devices boot -t android-material --device day-servo-ci --wait --headless)"
echo "ANDROID_SERIAL=$SERIAL" >> "$GITHUB_ENV"
"$ANDROID_HOME/platform-tools/adb" -s "$SERIAL" shell settings put global hide_error_dialogs 1
"$ANDROID_HOME/platform-tools/adb" -s "$SERIAL" shell settings put secure immersive_mode_confirmations confirmed
- uses: actions/cache@v6
if: matrix.target == 'harmony-arkui'
with:
path: ~/.cache/ohos-qemu/v20260919
key: ohos-qemu-v20260919-x86_64_virt-phone
- name: Boot OpenHarmony comparison device
if: matrix.target == 'harmony-arkui'
shell: bash
run: bash scripts/boot-servo-ohos-ci.sh
- name: Prepare native ArkWeb for comparison
if: matrix.target == 'harmony-arkui'
uses: daybrite/actions/.github/actions/setup-harmony-webview@main
- name: Run mixed-renderer regression and comparison scripts
working-directory: ${{ env.DEMO_DIR }}
shell: bash
env:
WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS: ${{ matrix.target == 'linux-gtk' && '1' || '' }}
run: |
set -euo pipefail
run_day() {
if [[ "$DAY_TARGET" == linux-* ]]; then
xvfb-run -a -s '-screen 0 1600x1200x24' "$DAY_BIN" "$@"
else
"$DAY_BIN" "$@"
fi
}
run_day launch -t "$DAY_TARGET" --flavor servo --script experiments/servo.yaml
run_day launch -t "$DAY_TARGET" --flavor servo --script experiments/renderer-comparison.yaml
- name: Verify matched captures and browser pixels
working-directory: ${{ env.RENDERER_ROOT }}
shell: bash
run: |
python -m venv "$RUNNER_TEMP/renderer-checks"
if [[ "$RUNNER_OS" == Windows ]]; then
CHECK_PYTHON="$RUNNER_TEMP/renderer-checks/Scripts/python.exe"
else
CHECK_PYTHON="$RUNNER_TEMP/renderer-checks/bin/python"
fi
"$CHECK_PYTHON" -m pip install 'Pillow==11.3.0'
"$CHECK_PYTHON" scripts/verify-renderer-comparison.py demo/build/day/flavors/servo/screenshots --target "$DAY_TARGET"
"$CHECK_PYTHON" scripts/verify-webview-render.py demo/build/day/flavors/servo/screenshots --pattern 'renderer-*-proof.png' --color 00ff00
- uses: actions/upload-artifact@v7
if: ${{ !cancelled() && steps.mixed-build.outcome == 'success' }}
with:
name: screenshots-renderer-comparison-${{ matrix.target }}
path: ${{ env.DEMO_DIR }}/build/day/flavors/servo/screenshots/
if-no-files-found: warn
- name: Upload script diagnostics
if: always()
uses: actions/upload-artifact@v7
with:
name: renderer-comparison-reports-${{ matrix.target }}
path: ${{ env.DEMO_DIR }}/build/day/flavors/servo/reports/dayscript/
if-no-files-found: warn
- name: Compare native and Servo release sizes
if: ${{ !cancelled() && inputs.measure-sizes == true && steps.mixed-build.outcome == 'success' }}
working-directory: ${{ env.RENDERER_ROOT }}
env:
DAY_ANDROID_ABI: arm64-v8a,x86_64
DAY_OHOS_ARCH: arm64
MACOSX_DEPLOYMENT_TARGET: '15.5'
shell: bash
run: python scripts/measure-renderer-size.py --target "$DAY_TARGET" --profile release
- name: Upload renderer size reports
if: always()
uses: actions/upload-artifact@v7
with:
name: renderer-sizes-${{ matrix.target }}
path: ${{ env.DEMO_DIR }}/build/renderer-sizes/
if-no-files-found: ignore
demo:
needs: renderer-comparison
uses: daybrite/actions/.github/workflows/dayapp.yml@v1
permissions:
contents: write pages: write id-token: write secrets: inherit
with:
project-path: demo
targets: macos-appkit, linux-gtk, linux-qt, windows-winui, ios-uikit, android-material,
harmony-arkui, web-dom
target-packages: |
windows-winui: webview2-runtime
day-deps: ${{ inputs.day-main && 'main' || 'auto' }}
publish-crates: true
publish-crates-paths: . servo
scripts: dayscript/webview.yaml
script-setup: |
if [[ "$DAY_SCRIPT_TARGET" == android-material ]]; then
# The normal Day stream omits activity/configuration events. Preserve those on
# failure so a startup remount can be distinguished from a lost/repeated action.
android_script_diagnostics() {
local diagnostic_status=$?
if (( diagnostic_status != 0 )); then
echo '::group::Android activity and application diagnostics'
timeout 10s "$ANDROID_HOME/platform-tools/adb" -s "$ANDROID_SERIAL" \
logcat -d -v threadtime ActivityTaskManager:I ActivityManager:I \
WindowManager:I AndroidRuntime:V Day:V day:V '*:S' || true
echo '::endgroup::'
fi
return "$diagnostic_status"
}
trap android_script_diagnostics EXIT
fi
preflight-checks: fmt, clippy, test
deploy-web: true
ios-devices: |
device=iPhone, orientation=portrait
android-devices: |
device=pixel_7, os=36, orientation=portrait
harmony-rendering:
needs: demo
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
pattern: screenshots-harmony-arkui*
path: harmony-screenshots
merge-multiple: true
- run: python3 -m pip install 'Pillow==11.3.0'
- run: python3 -m unittest discover -s scripts -p 'test_*.py' -v
- run: python3 scripts/verify-webview-render.py harmony-screenshots