refactor: move platform provider composition out of daemon #6018
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: | |
| paths-ignore: | |
| - 'docs/**' | |
| - 'website/**' | |
| - 'README.md' | |
| - 'AGENTS.md' | |
| - 'CHANGELOG.md' | |
| - 'CONTEXT.md' | |
| - 'CONTRIBUTING.md' | |
| - 'LICENSE' | |
| - 'SECURITY.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 | |
| # Unbounded, this step could not fail — it could only stall. On 2026-08-19 a slow mirror | |
| # held apt past the job's 30-minute budget on four main-branch runs and several unrelated | |
| # PRs, which reported as a red check on branches that never ran a line of project code. | |
| # Two bounds fix that: apt retries and times out each mirror request itself (a blip | |
| # self-heals without a manual re-run), and `timeout-minutes` is the backstop for a mirror | |
| # that drips bytes slowly enough to defeat the socket timeout. A healthy install takes | |
| # about a minute, so six is generous. | |
| - name: Install Linux desktop dependencies | |
| timeout-minutes: 6 | |
| run: | | |
| set -euo pipefail | |
| APT_OPTS=( | |
| -o Acquire::Retries=2 | |
| -o Acquire::http::Timeout=15 | |
| -o Acquire::https::Timeout=15 | |
| # The runner's own unattended-upgrades timer holds the dpkg lock; wait, don't hang. | |
| -o DPkg::Lock::Timeout=60 | |
| ) | |
| sudo apt-get update -qq "${APT_OPTS[@]}" | |
| sudo apt-get install -y -qq "${APT_OPTS[@]}" \ | |
| 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 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 | |
| - name: Execute Linux replay smoke test | |
| uses: ./.github/actions/run-gate | |
| with: | |
| gate: replay-linux | |
| args: | | |
| --retries | |
| 2 | |
| --report-junit | |
| test/artifacts/replays-linux.junit.xml | |
| - name: Reset daemon before Linux command evidence | |
| run: pnpm clean:daemon | |
| - name: Execute Linux command evidence | |
| uses: ./.github/actions/run-gate | |
| with: | |
| gate: linux-command-evidence | |
| - 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/** |