Skip to content

Commit 9a3570f

Browse files
authored
feat: Daemon modularization + capability-gated command routing + CI/test pipeline (#20)
* feat: Daemon modularization + capability-gated command routing + CI/test pipeline updates * update android test * fix arch * no serial * fixup tests locally; update runner on CI * increase timeout * drop unnecessary node build
1 parent d3ac97d commit 9a3570f

37 files changed

Lines changed: 2839 additions & 1994 deletions

.github/actions/build-docs/action.yml

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,9 @@ inputs:
99
runs:
1010
using: 'composite'
1111
steps:
12-
- name: Setup pnpm
13-
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
14-
with:
15-
version: 9.12.2
16-
17-
- name: Setup Node.js
18-
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
19-
with:
20-
node-version: 22
21-
cache: 'pnpm'
22-
23-
- name: Install dependencies (root)
12+
- name: Setup toolchain
2413
if: ${{ github.event.action != 'closed' }}
25-
run: pnpm install --frozen-lockfile
26-
shell: bash
14+
uses: ./.github/actions/setup-node-pnpm
2715

2816
- name: Build docs
2917
if: ${{ github.event.action != 'closed' }}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: 'Setup Node and pnpm'
2+
description: 'Setup pnpm, setup Node.js, and optionally install dependencies'
3+
4+
inputs:
5+
node-version:
6+
description: 'Node.js version'
7+
required: false
8+
default: '22'
9+
pnpm-version:
10+
description: 'pnpm version'
11+
required: false
12+
default: '9.12.2'
13+
install-deps:
14+
description: 'Whether to install dependencies with pnpm install --frozen-lockfile'
15+
required: false
16+
default: 'true'
17+
18+
runs:
19+
using: 'composite'
20+
steps:
21+
- name: Setup pnpm
22+
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
23+
with:
24+
version: ${{ inputs.pnpm-version }}
25+
26+
- name: Setup Node.js
27+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
28+
with:
29+
node-version: ${{ inputs.node-version }}
30+
cache: 'pnpm'
31+
32+
- name: Install dependencies
33+
if: ${{ inputs.install-deps == 'true' }}
34+
run: pnpm install --frozen-lockfile
35+
shell: bash

.github/workflows/ci.yml

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
permissions:
10+
contents: read
11+
12+
concurrency:
13+
group: ci-${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
unit:
18+
name: Unit Tests
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 20
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
24+
25+
- name: Setup toolchain
26+
uses: ./.github/actions/setup-node-pnpm
27+
28+
- name: Run unit tests
29+
run: pnpm test:unit
30+
31+
typecheck:
32+
name: Typecheck
33+
runs-on: ubuntu-latest
34+
timeout-minutes: 20
35+
steps:
36+
- name: Checkout
37+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
38+
39+
- name: Setup toolchain
40+
uses: ./.github/actions/setup-node-pnpm
41+
42+
- name: Run typecheck
43+
run: pnpm typecheck
44+
45+
integration-smoke:
46+
name: Integration Smoke
47+
runs-on: macos-latest
48+
timeout-minutes: 60
49+
continue-on-error: true
50+
steps:
51+
- name: Checkout
52+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
53+
54+
- name: Setup toolchain
55+
uses: ./.github/actions/setup-node-pnpm
56+
57+
- name: Run smoke integration tests
58+
run: |
59+
pnpm build:all
60+
pnpm test:smoke
61+
62+
integration-android:
63+
name: Integration Android
64+
runs-on: ubuntu-latest
65+
timeout-minutes: 80
66+
continue-on-error: true
67+
steps:
68+
- name: Checkout
69+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
70+
71+
- name: Setup toolchain
72+
uses: ./.github/actions/setup-node-pnpm
73+
74+
- name: Resolve agent-device home
75+
id: android-agent-home
76+
run: echo "dir=$HOME/.agent-device" >> "$GITHUB_OUTPUT"
77+
78+
- name: Enable KVM
79+
run: |
80+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
81+
sudo udevadm control --reload-rules
82+
sudo udevadm trigger --name-match=kvm
83+
84+
- name: Run Android integration test
85+
uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # v2.35.0
86+
with:
87+
api-level: 35
88+
arch: x86_64
89+
profile: pixel_7
90+
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -no-metrics
91+
script: node --test test/integration/android.test.ts
92+
93+
- name: Upload Android artifacts
94+
if: always()
95+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
96+
with:
97+
name: android-artifacts
98+
if-no-files-found: ignore
99+
path: |
100+
${{ steps.android-agent-home.outputs.dir }}/daemon.log
101+
${{ steps.android-agent-home.outputs.dir }}/sessions/**
102+
test/screenshots/**
103+
104+
integration-ios:
105+
name: Integration iOS
106+
runs-on: macos-latest
107+
timeout-minutes: 80
108+
continue-on-error: true
109+
steps:
110+
- name: Checkout
111+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
112+
113+
- name: Setup toolchain
114+
uses: ./.github/actions/setup-node-pnpm
115+
116+
- name: Resolve agent-device home
117+
id: ios-agent-home
118+
run: echo "dir=$HOME/.agent-device" >> "$GITHUB_OUTPUT"
119+
120+
- name: Select and start iOS simulator
121+
run: |
122+
UDID="$(
123+
xcrun simctl list devices -j | node -e "
124+
const fs = require('node:fs');
125+
const payload = JSON.parse(fs.readFileSync(0, 'utf8'));
126+
const all = Object.values(payload.devices ?? {}).flat();
127+
const available = all.filter((d) => d.isAvailable);
128+
const preferred =
129+
available.find((d) => d.state === 'Booted') ??
130+
available.find((d) => d.name === 'iPhone 17 Pro') ??
131+
available[0];
132+
if (!preferred?.udid) process.exit(1);
133+
process.stdout.write(preferred.udid);
134+
"
135+
)"
136+
xcrun simctl boot "$UDID" || true
137+
echo "IOS_UDID=$UDID" >> "$GITHUB_ENV"
138+
139+
- name: Build iOS integration artifacts
140+
run: pnpm build:xcuitest
141+
142+
- name: Wait for iOS simulator boot
143+
run: xcrun simctl bootstatus "$IOS_UDID" -b
144+
145+
- name: Run iOS integration test
146+
env:
147+
AGENT_DEVICE_DAEMON_TIMEOUT_MS: "180000"
148+
run: node --test test/integration/ios.test.ts
149+
150+
- name: Upload iOS artifacts
151+
if: always()
152+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
153+
with:
154+
name: ios-artifacts
155+
if-no-files-found: ignore
156+
path: |
157+
${{ steps.ios-agent-home.outputs.dir }}/daemon.log
158+
${{ steps.ios-agent-home.outputs.dir }}/sessions/**
159+
test/screenshots/**

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
pages: write
1414
steps:
1515
- name: Checkout
16-
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
16+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1717
with:
1818
persist-credentials: true
1919

0 commit comments

Comments
 (0)