Skip to content

Commit ff5746b

Browse files
authored
perf: speed up CI tests with shared Docker image and more shards (#161)
1 parent eb6a83c commit ff5746b

File tree

6 files changed

+106
-94
lines changed

6 files changed

+106
-94
lines changed

.github/workflows/test.yml

Lines changed: 94 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,84 @@ jobs:
122122
cp -r dist/agent/web test-install/
123123
./test-install/bin/perry --version
124124
125-
test:
125+
docker:
126+
runs-on: ubuntu-latest
127+
steps:
128+
- uses: actions/checkout@v4
129+
130+
- name: Set up Docker Buildx
131+
uses: docker/setup-buildx-action@v3
132+
with:
133+
driver: docker
134+
135+
- name: Build base image
136+
uses: docker/build-push-action@v6
137+
with:
138+
context: ./perry
139+
file: ./perry/Dockerfile.base
140+
load: true
141+
tags: perry-base:latest
142+
143+
- name: Build full image
144+
uses: docker/build-push-action@v6
145+
with:
146+
context: ./perry
147+
load: true
148+
tags: perry:latest
149+
build-args: |
150+
BASE_IMAGE=perry-base:latest
151+
152+
- name: Save Docker image
153+
run: docker save perry:latest | gzip > perry-image.tar.gz
154+
155+
- name: Upload Docker image
156+
uses: actions/upload-artifact@v4
157+
with:
158+
name: docker-image
159+
path: perry-image.tar.gz
160+
retention-days: 1
161+
compression-level: 0
162+
163+
test-unit:
126164
runs-on: ubuntu-latest
127165
needs: build
166+
steps:
167+
- uses: actions/checkout@v4
168+
169+
- name: Download build artifacts
170+
uses: actions/download-artifact@v4
171+
with:
172+
name: dist
173+
path: dist/
174+
175+
- name: Set up Bun
176+
uses: oven-sh/setup-bun@v2
177+
with:
178+
bun-version: latest
179+
180+
- name: Cache bun dependencies
181+
uses: actions/cache@v4
182+
with:
183+
path: |
184+
~/.bun/install/cache
185+
node_modules
186+
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/package.json') }}
187+
restore-keys: |
188+
${{ runner.os }}-bun-
189+
190+
- name: Install dependencies
191+
run: bun install
192+
193+
- name: Run unit tests
194+
run: bun run test:unit
195+
196+
test:
197+
runs-on: ubuntu-latest
198+
needs: [build, docker]
128199
strategy:
129200
fail-fast: false
130201
matrix:
131-
shard: [1, 2, 3]
202+
shard: [1, 2, 3, 4, 5]
132203
steps:
133204
- uses: actions/checkout@v4
134205
with:
@@ -140,6 +211,14 @@ jobs:
140211
name: dist
141212
path: dist/
142213

214+
- name: Download Docker image
215+
uses: actions/download-artifact@v4
216+
with:
217+
name: docker-image
218+
219+
- name: Load Docker image
220+
run: gunzip -c perry-image.tar.gz | docker load
221+
143222
- name: Set up Bun
144223
uses: oven-sh/setup-bun@v2
145224
with:
@@ -161,44 +240,15 @@ jobs:
161240
bun install
162241
cd web && bun install
163242
164-
- name: Set up Docker Buildx
165-
uses: docker/setup-buildx-action@v3
166-
with:
167-
driver: docker
168-
169-
- name: Log in to Container Registry
170-
uses: docker/login-action@v3
171-
with:
172-
registry: ${{ env.REGISTRY }}
173-
username: ${{ github.actor }}
174-
password: ${{ secrets.GITHUB_TOKEN }}
175-
176-
- name: Build base image
177-
uses: docker/build-push-action@v6
178-
with:
179-
context: ./perry
180-
file: ./perry/Dockerfile.base
181-
load: true
182-
tags: perry-base:latest
183-
184-
- name: Build full image
185-
uses: docker/build-push-action@v6
186-
with:
187-
context: ./perry
188-
load: true
189-
tags: perry:latest
190-
build-args: |
191-
BASE_IMAGE=perry-base:latest
192-
193-
- name: Run tests (shard ${{ matrix.shard }}/3)
243+
- name: Run tests (shard ${{ matrix.shard }}/5)
194244
run: |
195-
# Skip Docker build in test setup since we already have the image
196245
export SKIP_DOCKER_BUILD=true
197-
bun run test --shard=${{ matrix.shard }}/3
246+
export SKIP_AGENT_UPDATES=true
247+
bun run test --shard=${{ matrix.shard }}/5
198248
199249
e2e:
200250
runs-on: ubuntu-latest
201-
needs: build
251+
needs: [build, docker]
202252
steps:
203253
- uses: actions/checkout@v4
204254
with:
@@ -210,6 +260,14 @@ jobs:
210260
name: dist
211261
path: dist/
212262

263+
- name: Download Docker image
264+
uses: actions/download-artifact@v4
265+
with:
266+
name: docker-image
267+
268+
- name: Load Docker image
269+
run: gunzip -c perry-image.tar.gz | docker load
270+
213271
- name: Set up Bun
214272
uses: oven-sh/setup-bun@v2
215273
with:
@@ -250,18 +308,6 @@ jobs:
250308
if: steps.playwright-cache.outputs.cache-hit == 'true'
251309
run: cd web && bun x playwright install-deps chromium
252310

253-
- name: Set up Docker Buildx
254-
uses: docker/setup-buildx-action@v3
255-
with:
256-
driver: docker
257-
258-
- name: Log in to Container Registry
259-
uses: docker/login-action@v3
260-
with:
261-
registry: ${{ env.REGISTRY }}
262-
username: ${{ github.actor }}
263-
password: ${{ secrets.GITHUB_TOKEN }}
264-
265311
- name: Check for Dockerfile changes
266312
id: docker-changes
267313
run: |
@@ -286,23 +332,6 @@ jobs:
286332
echo "is_fork=false" >> $GITHUB_OUTPUT
287333
fi
288334
289-
- name: Build base image
290-
uses: docker/build-push-action@v6
291-
with:
292-
context: ./perry
293-
file: ./perry/Dockerfile.base
294-
load: true
295-
tags: perry-base:latest
296-
297-
- name: Build full image
298-
uses: docker/build-push-action@v6
299-
with:
300-
context: ./perry
301-
load: true
302-
tags: perry:latest
303-
build-args: |
304-
BASE_IMAGE=perry-base:latest
305-
306335
- name: Configure agent for e2e tests
307336
run: |
308337
mkdir -p ~/.config/perry
@@ -322,7 +351,7 @@ jobs:
322351

323352
- name: Start agent
324353
run: |
325-
bun run src/index.ts agent run --port 7391 &
354+
SKIP_AGENT_UPDATES=true bun run src/index.ts agent run --port 7391 &
326355
sleep 5
327356
curl -s http://localhost:7391/health || (echo "Agent failed to start" && exit 1)
328357

perry/Dockerfile.base

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,6 @@
33
# Minimal image with Perry runtime, coding agents, and essential tools.
44
# Users can extend this with their own language runtimes and tools.
55

6-
# Stage 1: Build Neovim from source
7-
FROM ubuntu:noble AS neovim-builder
8-
9-
ENV DEBIAN_FRONTEND=noninteractive
10-
11-
RUN apt-get update && apt-get install -y --no-install-recommends \
12-
git \
13-
ninja-build \
14-
gettext \
15-
cmake \
16-
build-essential \
17-
curl \
18-
ca-certificates \
19-
&& rm -rf /var/lib/apt/lists/*
20-
21-
RUN git clone --depth 1 --branch v0.11.4 https://github.com/neovim/neovim.git /tmp/neovim \
22-
&& cd /tmp/neovim \
23-
&& make CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_INSTALL_PREFIX=/usr/local \
24-
&& make install \
25-
&& rm -rf /tmp/neovim
26-
27-
# Stage 2: Final image
286
FROM ubuntu:noble
297

308
ENV DEBIAN_FRONTEND=noninteractive
@@ -72,10 +50,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
7250
fzf \
7351
&& rm -rf /var/lib/apt/lists/*
7452

75-
# Copy Neovim from builder stage
76-
COPY --from=neovim-builder /usr/local/bin/nvim /usr/local/bin/nvim
77-
COPY --from=neovim-builder /usr/local/lib/nvim /usr/local/lib/nvim
78-
COPY --from=neovim-builder /usr/local/share/nvim /usr/local/share/nvim
53+
# Install Neovim from official prebuilt binary
54+
RUN curl -fsSL https://github.com/neovim/neovim/releases/download/v0.11.6/nvim-linux-x86_64.tar.gz \
55+
| tar -xz -C /opt \
56+
&& ln -s /opt/nvim-linux-x86_64/bin/nvim /usr/local/bin/nvim
7957

8058
# Install GitHub CLI
8159
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg -o /usr/share/keyrings/githubcli-archive-keyring.gpg \

src/client/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class ApiClient {
4242
this.token = options.token;
4343

4444
const token = this.token;
45-
const timeout = options.timeout || 30000;
45+
const timeout = options.timeout || 120000;
4646
const link = new RPCLink({
4747
url: `${this.baseUrl}/rpc`,
4848
fetch: (url, init) => {

src/workspace/manager.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,10 @@ export class WorkspaceManager {
374374
}
375375

376376
private async updateAgentBinaries(containerName: string): Promise<void> {
377+
if (process.env.SKIP_AGENT_UPDATES === 'true') {
378+
return;
379+
}
380+
377381
const updates = [
378382
{
379383
name: 'claude',

test/helpers/agent.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ export async function startTestAgent(options: TestAgentOptions = {}): Promise<Te
344344
...process.env,
345345
PERRY_CONFIG_DIR: configDir,
346346
PERRY_PORT: String(port),
347+
SKIP_AGENT_UPDATES: 'true',
347348
},
348349
stdio: ['pipe', 'pipe', 'pipe'],
349350
});

vitest.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { defineConfig } from 'vitest/config';
22

33
export default defineConfig({
44
test: {
5-
testTimeout: 120000,
6-
hookTimeout: 120000,
5+
testTimeout: 60000,
6+
hookTimeout: 60000,
77
pool: 'forks',
88
poolOptions: {
99
forks: {

0 commit comments

Comments
 (0)