name: Linux on: pull_request: paths-ignore: - "docs/**" - "website/**" - "README.md" - ".github/actions/build-docs/action.yml" - ".github/workflows/deploy.yml" - ".github/workflows/pr-preview.yml" - ".github/workflows/pr-preview-cleanup.yml" 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: | sudo apt-get update -qq sudo apt-get install -y -qq \ xvfb \ xdotool \ scrot \ at-spi2-core \ python3-gi \ gir1.2-atspi-2.0 \ libatk-adaptor \ dbus-x11 \ 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 [ -z "$ATSPI_REG" ]; then echo "::error::at-spi2-registryd not found. Install at-spi2-core." exit 1 fi "$ATSPI_REG" & sleep 2 # Health probe: verify the registry is reachable on the a11y bus if python3 -c "import gi; gi.require_version('Atspi','2.0'); from gi.repository import Atspi; d=Atspi.get_desktop(0); assert d is not None, 'desktop is None'; print(f'AT-SPI2 OK — {d.get_child_count()} apps')"; then echo "AT-SPI2 registry healthy" else echo "::error::AT-SPI2 registry started but health probe failed" exit 1 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 "::warning::AT-SPI2 tree dump returned no nodes (expected before any app is launched)" 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 2 \ --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/**