feat: add Linux desktop automation support via AT-SPI2 #5
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| # Install native build deps BEFORE pnpm install so node-gtk can compile. | |
| - name: Install Linux desktop dependencies | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y -qq \ | |
| build-essential \ | |
| gobject-introspection \ | |
| libgirepository1.0-dev \ | |
| libglib2.0-dev \ | |
| libcairo2-dev \ | |
| at-spi2-core \ | |
| libatk-adaptor \ | |
| gir1.2-atspi-2.0 \ | |
| xvfb \ | |
| xdotool \ | |
| scrot \ | |
| gnome-calculator \ | |
| dbus-x11 \ | |
| wmctrl | |
| - name: Setup toolchain | |
| uses: ./.github/actions/setup-node-pnpm | |
| - name: Build node-gtk native module | |
| run: | | |
| # node-gtk uses node-pre-gyp. The S3-hosted prebuilt likely doesn't | |
| # exist for this Node ABI (v127 / Node 22), and pnpm install silently | |
| # skips failed optional dep builds. Run node-pre-gyp directly inside | |
| # the package to force a from-source build. | |
| NODE_GTK_DIR=$(ls -d node_modules/.pnpm/node-gtk@*/node_modules/node-gtk) | |
| echo "node-gtk at: $NODE_GTK_DIR" | |
| cd "$NODE_GTK_DIR" | |
| npx @mapbox/node-pre-gyp install --fallback-to-build --update-binary 2>&1 | |
| ls -la lib/binding/*/node_gtk.node | |
| - 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 will be empty" | |
| fi | |
| - name: Verify environment | |
| run: | | |
| echo "=== Display ===" | |
| xdotool getdisplaygeometry || echo "Display check failed" | |
| echo "=== D-Bus ===" | |
| echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" | |
| dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames 2>&1 | head -20 | |
| echo "=== node-gtk ===" | |
| node -e "import('node-gtk').then(gi => { const Atspi = gi.require('Atspi', '2.0'); console.log('node-gtk + AT-SPI2 GI bindings OK'); process.exit(0); }).catch(e => { console.error('node-gtk FAILED:', e.message); process.exit(1); })" | |
| echo "=== xdotool ===" | |
| xdotool version | |
| echo "=== scrot ===" | |
| scrot --version 2>&1 || echo "scrot OK" | |
| - 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/** |