Skip to content

feat: add Linux desktop automation support via AT-SPI2 #7

feat: add Linux desktop automation support via AT-SPI2

feat: add Linux desktop automation support via AT-SPI2 #7

Workflow file for this run

name: Linux
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
smoke-linux:
name: Smoke Tests
runs-on: ubuntu-latest
timeout-minutes: 30
env:
# Force X11 mode (Xvfb) — no Wayland on CI.
XDG_SESSION_TYPE: x11
DISPLAY: ":99"
# Headless GTK: avoid dconf issues, enable accessibility bridge.
GSETTINGS_BACKEND: memory
NO_AT_BRIDGE: "0"
GTK_A11Y: atspi
GTK_MODULES: "gail:atk-bridge"
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Linux desktop dependencies
run: |
# python3-gi, gir1.2-atspi-2.0, at-spi2-core, dbus-x11 are
# pre-installed on Ubuntu GitHub Actions runners.
sudo apt-get update -qq
sudo apt-get install -y -qq \
xvfb \
xdotool \
scrot \
libatk-adaptor \
gnome-calculator \
wmctrl
- name: Setup toolchain
uses: ./.github/actions/setup-node-pnpm
- name: Start Xvfb and D-Bus
run: |
# Start virtual framebuffer (1280x1024, 24-bit color)
Xvfb :99 -screen 0 1280x1024x24 &
sleep 1
# Start a D-Bus session and export its env vars for subsequent steps.
# dbus-launch forks a persistent daemon, so it survives the step.
eval "$(dbus-launch --sh-syntax)"
echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" >> "$GITHUB_ENV"
echo "DBUS_SESSION_BUS_PID=$DBUS_SESSION_BUS_PID" >> "$GITHUB_ENV"
- name: Start AT-SPI2 registry
run: |
# The registry must start AFTER DBUS_SESSION_BUS_ADDRESS is available
# (it was written to GITHUB_ENV in the previous step).
ATSPI_REG=$(find /usr -name at-spi2-registryd -type f 2>/dev/null | head -1)
if [ -n "$ATSPI_REG" ]; then
"$ATSPI_REG" &
sleep 2
echo "AT-SPI2 registry started: $ATSPI_REG"
else
echo "::warning::at-spi2-registryd not found — accessibility tree may be empty"
fi
- name: Verify environment
run: |
echo "=== Display ==="
xdotool getdisplaygeometry
echo "=== D-Bus ==="
echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
echo "=== AT-SPI2 Python bindings ==="
python3 -c "import gi; gi.require_version('Atspi', '2.0'); from gi.repository import Atspi; print('OK')"
echo "=== AT-SPI2 tree dump (quick test) ==="
python3 src/platforms/linux/atspi-dump.py --surface desktop --max-nodes 5 | python3 -m json.tool | head -20
echo "=== xdotool ==="
xdotool version
- name: Run Linux replay smoke test
run: |
pnpm clean:daemon
node --experimental-strip-types src/bin.ts test test/integration/replays/linux \
--retries 3 \
--report-junit test/artifacts/replays-linux.junit.xml
- name: Upload Linux artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: linux-artifacts
if-no-files-found: ignore
path: |
test/artifacts/**
test/screenshots/**