perf: add Docker layer caching to CI image builds (#163) #706
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: Tests | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| branches: [ main ] | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| BASE_IMAGE_NAME: ${{ github.repository }}-base | |
| jobs: | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Cache bun dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.bun/install/cache | |
| node_modules | |
| web/node_modules | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| run: | | |
| bun install | |
| cd web && bun install | |
| - name: Lint | |
| run: bun run lint | |
| - name: Format check | |
| run: bun run format:check | |
| - name: Typecheck | |
| run: bun x tsc --noEmit | |
| - name: Lint web | |
| run: bun run lint:web | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Cache bun dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.bun/install/cache | |
| node_modules | |
| web/node_modules | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| run: | | |
| bun install | |
| cd web && bun install | |
| - name: Build | |
| run: bun run build | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| retention-days: 1 | |
| binary: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Install dependencies | |
| run: bun install | |
| - name: Compile binary | |
| run: | | |
| bun build ./src/index.ts --compile --minify --outfile=perry-test | |
| - name: Test binary runs | |
| run: | | |
| ./perry-test --version | |
| ./perry-test --help | |
| - name: Test binary with web assets | |
| run: | | |
| mkdir -p test-install/bin | |
| cp perry-test test-install/bin/perry | |
| cp -r dist/agent/web test-install/ | |
| ./test-install/bin/perry --version | |
| docker: | |
| runs-on: ubuntu-latest | |
| services: | |
| registry: | |
| image: registry:3 | |
| ports: | |
| - 5000:5000 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver-opts: network=host | |
| - name: Build base image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: ./perry | |
| file: ./perry/Dockerfile.base | |
| push: true | |
| tags: localhost:5000/perry-base:latest | |
| cache-from: type=gha,scope=perry-base | |
| cache-to: type=gha,scope=perry-base,mode=max | |
| - name: Build full image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: ./perry | |
| tags: perry:latest | |
| build-args: | | |
| BASE_IMAGE=localhost:5000/perry-base:latest | |
| cache-from: type=gha,scope=perry-full | |
| cache-to: type=gha,scope=perry-full,mode=max | |
| outputs: type=docker,dest=perry-image.tar | |
| - name: Compress Docker image | |
| run: gzip perry-image.tar | |
| - name: Upload Docker image | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-image | |
| path: perry-image.tar.gz | |
| retention-days: 1 | |
| compression-level: 0 | |
| test-unit: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Cache bun dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.bun/install/cache | |
| node_modules | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| run: bun install | |
| - name: Run unit tests | |
| run: bun run test:unit | |
| test: | |
| runs-on: ubuntu-latest | |
| needs: [build, docker] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4, 5] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Download Docker image | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: docker-image | |
| - name: Load Docker image | |
| run: gunzip -c perry-image.tar.gz | docker load | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Cache bun dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.bun/install/cache | |
| node_modules | |
| web/node_modules | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| run: | | |
| bun install | |
| cd web && bun install | |
| - name: Run tests (shard ${{ matrix.shard }}/5) | |
| run: | | |
| export SKIP_DOCKER_BUILD=true | |
| export SKIP_AGENT_UPDATES=true | |
| bun run test --shard=${{ matrix.shard }}/5 | |
| e2e: | |
| runs-on: ubuntu-latest | |
| needs: [build, docker] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Download Docker image | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: docker-image | |
| - name: Load Docker image | |
| run: gunzip -c perry-image.tar.gz | docker load | |
| - name: Set up Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Cache bun dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.bun/install/cache | |
| node_modules | |
| web/node_modules | |
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-bun- | |
| - name: Install dependencies | |
| run: | | |
| bun install | |
| cd web && bun install | |
| - name: Get Playwright version | |
| id: playwright-version | |
| run: echo "version=$(cd web && bun pm ls | grep '@playwright/test' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" >> $GITHUB_OUTPUT | |
| - name: Cache Playwright browsers | |
| uses: actions/cache@v4 | |
| id: playwright-cache | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }} | |
| - name: Install Playwright browsers | |
| if: steps.playwright-cache.outputs.cache-hit != 'true' | |
| run: cd web && bun x playwright install chromium --with-deps | |
| - name: Install Playwright deps (cached) | |
| if: steps.playwright-cache.outputs.cache-hit == 'true' | |
| run: cd web && bun x playwright install-deps chromium | |
| - name: Check for Dockerfile changes | |
| id: docker-changes | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| BASE_SHA="${{ github.event.pull_request.base.sha }}" | |
| else | |
| BASE_SHA="${{ github.event.before }}" | |
| fi | |
| if git diff --name-only "$BASE_SHA" HEAD 2>/dev/null | grep -q "^perry/"; then | |
| echo "changed=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "changed=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Check if PR is from fork | |
| id: fork-check | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then | |
| echo "is_fork=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "is_fork=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Configure agent for e2e tests | |
| run: | | |
| mkdir -p ~/.config/perry | |
| if [ -n "$OPENCODE_TOKEN" ]; then | |
| echo '{"agents":{"opencode":{"server":{"hostname":"0.0.0.0"}}}}' > ~/.config/perry/config.json | |
| mkdir -p ~/.local/share/opencode | |
| echo '{"opencode":{"type":"api","key":"'"$OPENCODE_TOKEN"'"}}' > ~/.local/share/opencode/auth.json | |
| chmod 600 ~/.local/share/opencode/auth.json | |
| echo "OPENCODE_TOKEN=$OPENCODE_TOKEN" >> $GITHUB_ENV | |
| echo "OpenCode auth configured" | |
| else | |
| echo '{}' > ~/.config/perry/config.json | |
| echo "No OPENCODE_TOKEN - OpenCode tests will be skipped" | |
| fi | |
| env: | |
| OPENCODE_TOKEN: ${{ secrets.OPENCODE_TOKEN }} | |
| - name: Start agent | |
| run: | | |
| SKIP_AGENT_UPDATES=true bun run src/index.ts agent run --port 7391 & | |
| sleep 5 | |
| curl -s http://localhost:7391/health || (echo "Agent failed to start" && exit 1) | |
| - name: Create test workspace | |
| run: | | |
| bun run src/index.ts start test | |
| bun run src/index.ts list | |
| - name: Run Playwright tests (web/e2e) | |
| run: | | |
| if [ -n "$OPENCODE_TOKEN" ]; then | |
| cd web && bun run test:e2e | |
| else | |
| cd web && bun run test:e2e --grep-invert "OpenCode" | |
| fi | |
| env: | |
| CI: true | |
| OPENCODE_TOKEN: ${{ secrets.OPENCODE_TOKEN }} | |
| - name: Stop agent for web integration tests | |
| run: | | |
| pkill -f "bun run src/index.ts agent" || true | |
| sleep 2 | |
| - name: Run Playwright tests (test/web - self-contained) | |
| run: | | |
| bun run test:web | |
| env: | |
| CI: true | |
| - name: Upload Playwright report (web/e2e) | |
| uses: actions/upload-artifact@v4 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: playwright-report | |
| path: web/playwright-report/ | |
| retention-days: 7 | |
| - name: Upload Playwright report (test/web) | |
| uses: actions/upload-artifact@v4 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: playwright-report-web-integration | |
| path: playwright-report/ | |
| retention-days: 7 |