Files
callstack__agent-device/.github/workflows/linux.yml
Michał Pierzchała f45228ae71 ci: skip device lanes for root-level docs-only changes (#1781 A9) (#1791)
* ci: skip device lanes for root-level docs-only changes (#1781 A9)

Add AGENTS.md, CHANGELOG.md, CONTEXT.md, CONTRIBUTING.md, LICENSE, and
SECURITY.md to the pull_request paths-ignore block in ios.yml,
android.yml, linux.yml, macos.yml, ci.yml, and size.yml. These
root-level docs files were the only gap left after docs/**,
website/**, and README.md — PRs #1568 (SECURITY.md only), #1697
(CONTEXT.md + docs/adr only), and #1722 (AGENTS.md + docs/) each still
triggered a full 9-15 min macOS iOS run despite touching only prose.

Why each file is safe to ignore for every one of these six workflows:

- None of the four device workflows (ios/android/linux/macos) or their
  composite actions read any of these six files at runtime; the only
  hits from `grep -rln` across scripts/, src/, test/, and
  .github/actions/ are prose comments pointing humans at CONTEXT.md or
  AGENTS.md sections (e.g. scripts/layering/check.ts,
  scripts/wire-compat/run.ts, src/mcp/tool-ref-pins.ts) — never an
  `fs.readFileSync`/`readFile` of the file itself.
- The check-affected selector (scripts/check-affected/model.ts)
  already classifies all six as pure docs: `isDocs()` matches any
  `.md` file plus the literal `LICENSE`, and `docsOwnership()` only
  special-cases `website/docs/docs/commands.md` (unrelated). So these
  files already select zero checks — they only ever produced
  `docsOnlyPaths` entries, never `SelectionReason`s.
- Because they select zero checks, the gate-manifest's path-coverage
  category derivation (`scripts/gate/model.ts` `categories()`, which
  iterates `plan.reasons`) never records a category for them, so
  ci.yml has nothing check-manifest-only that these six files would
  need to keep reachable. `pnpm check:gate-manifest` and
  `pnpm check:gate-manifest:test` both stay green after the change
  (48 checks / 33 lanes, 28/28 gate tests passing).
- size.yml's bundle-size job (scripts/size-report.mjs) measures the
  `pnpm build` dist output and startup timing only — no reference to
  any of these six files. (npm packs LICENSE/README.md into the
  publishable tarball, but that's a `pnpm check:package` node-22.12
  concern in ci.yml's packaged-cli job, which is driven by `dist`
  contents and `package.json`, not by LICENSE/README prose — already
  evidenced by README.md being ignored here since before this change.)

Scope disclosure: `mutation-affected.yml` uses a `paths:` allowlist
(not paths-ignore) so it's structurally unaffected; `test-app-build-cache.yml`
has no path filter at all. Neither was touched.

actionlint and `pnpm check:gate-manifest`/`:test` pass on the changed
workflows.

* test: pin root-doc paths-ignore entries with a regression test

Addresses review feedback on #1791 from thymikee: the docs-only
classifier for AGENTS.md/CHANGELOG.md/CONTEXT.md/CONTRIBUTING.md/
LICENSE/SECURITY.md across ios.yml/android.yml/linux.yml/macos.yml/
ci.yml/size.yml had no regression pin. Neither check:gate-manifest
(only proves a *registered check* is reachable) nor actionlint (only
validates YAML shape) nor generic Markdown coverage would catch a
single dropped entry — e.g. LICENSE reappearing in one workflow's
paths-ignore list but not another's would silently put a full 9-15 min
device run back on prose-only PRs.

test/ci/root-docs-paths-ignore.test.ts parses the six real workflow
files and asserts, using the same matchesGlob the gate-manifest model
uses to decide lane triggering, that each of the six root docs is
ignored by each workflow's pull_request paths-ignore. Registered in
vitest.config.ts's unit-core project next to its sibling
upload-agent-device-artifacts.test.ts (parse-only, no device/subprocess
lane needed).

Verified red on main (all 36 file x doc assertions fail — confirmed via
a throwaway script reading `git show main:.github/workflows/*.yml`)
and green on this branch (6/6). Full unit-core project (873 files /
6641 tests) still passes; check:gate-manifest and
check:gate-manifest:test unchanged (48 checks / 33 lanes, 28/28).
2026-08-18 09:57:09 +02:00

132 lines
4.3 KiB
YAML

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
- 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 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: 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/**