Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0d880e0
feat: add Linux desktop automation support via AT-SPI2 (Phase 1+2)
claude Apr 4, 2026
e6c2e31
refactor: code review cleanup for Linux platform support
claude Apr 4, 2026
a3f3992
feat: Linux input synthesis, screenshots, and app lifecycle (Phase 3+4)
claude Apr 4, 2026
ca22e32
refactor: consolidate Linux env detection, simplify input actions
claude Apr 4, 2026
cb1a368
feat: add Linux CI smoke test with Xvfb and AT-SPI2
claude Apr 4, 2026
4f1ec98
fix: remove pre-session screenshot from Linux replay test
claude Apr 4, 2026
bb049ce
fix: Linux CI — add missing node-gtk build deps and AT-SPI2 env
claude Apr 4, 2026
c97a806
fix: explicitly rebuild node-gtk native module in Linux CI
claude Apr 4, 2026
e51f5f9
fix: use node-pre-gyp directly to build node-gtk from source
claude Apr 4, 2026
4b2a0e3
refactor: replace node-gtk with Python subprocess for AT-SPI2
claude Apr 4, 2026
1dc9637
chore: drop pre-installed packages from Linux CI apt-get
claude Apr 4, 2026
484614d
feat: surface support for Linux, unit tests, stronger CI assertions
claude Apr 4, 2026
1ba6d20
docs: add cross-platform snapshot traversal contract
claude Apr 4, 2026
885bd65
fix: update lockfile after removing node-gtk optional dependency
claude Apr 4, 2026
8df648b
fix: address review findings in Linux platform code
claude Apr 4, 2026
e993073
fix(ci): explicitly install all Linux a11y dependencies
claude Apr 4, 2026
90753b5
fix: quote multi-word role value in Linux smoke test selector
claude Apr 4, 2026
fea873c
fix: use valid selector keys in Linux smoke test
claude Apr 4, 2026
34c6e63
chore: cleanup pass — menubar warning, fix contract doc example
claude Apr 4, 2026
f82feb0
chore: add Python bytecache to gitignore
claude Apr 4, 2026
8b02cbf
feat: harden Linux platform — capability matrix, CI, error handling
claude Apr 4, 2026
b4e89bf
fix: apply depth/interactive filtering to Linux snapshots
claude Apr 4, 2026
86ecc89
fix: address review findings — error reporting, Wayland, timeout
claude Apr 4, 2026
6cb62f8
feat: appName/windowTitle selectors, clipboard, input-action tests
claude Apr 4, 2026
0dbe11f
chore: cache tool detection for screenshot/clipboard, extract get_app…
claude Apr 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
name: Linux

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
name: Typecheck & Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup toolchain
uses: ./.github/actions/setup-node-pnpm

- name: Typecheck
run: pnpm typecheck

- name: Unit tests
run: pnpm test:unit

smoke-linux:
name: Smoke Tests
needs: validate
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 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/**
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ node_modules/
.pnpm-store/
dist/
.DS_Store
__pycache__/
*.pyc
*.log
test/screenshots/*.png
test/artifacts/
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"test:replay:ios": "node --experimental-strip-types src/bin.ts test test/integration/replays/ios/simulator",
"test:replay:ios-device": "node --experimental-strip-types src/bin.ts test test/integration/replays/ios/device",
"test:replay:android": "node --experimental-strip-types src/bin.ts test test/integration/replays/android",
"test:replay:macos": "node --experimental-strip-types src/bin.ts test test/integration/replays/macos"
"test:replay:macos": "node --experimental-strip-types src/bin.ts test test/integration/replays/macos",
"test:replay:linux": "node --experimental-strip-types src/bin.ts test test/integration/replays/linux"
},
"files": [
"bin",
Expand All @@ -57,6 +58,7 @@
"!ios-runner/**/*.xcuserstate",
"macos-helper",
"!macos-helper/**/.build",
"src/platforms/linux/atspi-dump.py",
"skills",
"README.md",
"LICENSE"
Expand Down
34 changes: 34 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading