diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d89599fb..fb10adf3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,32 +6,158 @@ on: pull_request: branches: [ main ] +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + jobs: - test: + 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 + + build: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - 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 + + test: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - 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 + 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: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'npm' + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - - name: Set up Bun - uses: oven-sh/setup-bun@v2 + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Install dependencies - run: | - bun install - cd web && bun install + - name: Check for Dockerfile changes + id: docker-changes + run: | + # For PRs, compare against base branch + # For pushes, compare against previous commit + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + else + BASE_SHA="${{ github.event.before }}" + fi - - name: Typecheck - run: bun x tsc --noEmit + # Check if any files in perry/ directory changed + if git diff --name-only "$BASE_SHA" HEAD 2>/dev/null | grep -q "^perry/"; then + echo "Dockerfile or related files changed - will rebuild and update cache" + echo "changed=true" >> $GITHUB_OUTPUT + else + echo "No Dockerfile changes detected - using cached layers" + echo "changed=false" >> $GITHUB_OUTPUT + fi - - name: Build - run: bun run build + - name: Build workspace image (with cache) + uses: docker/build-push-action@v6 + with: + context: ./perry + load: true + tags: workspace:latest + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache-test + cache-to: ${{ steps.docker-changes.outputs.changed == 'true' && format('type=registry,ref={0}/{1}:buildcache-test,mode=max', env.REGISTRY, env.IMAGE_NAME) || '' }} - - name: Run tests - run: bun run test + - name: Run tests + run: | + # Skip Docker build in test setup since we already have the image + export SKIP_DOCKER_BUILD=true + bun run test diff --git a/test/setup/global.js b/test/setup/global.js index 6ab161dc..48a0491b 100644 --- a/test/setup/global.js +++ b/test/setup/global.js @@ -27,7 +27,26 @@ async function cleanupOrphanedResources() { } catch {} } +function imageExists() { + try { + execSync('docker image inspect workspace:latest', { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} + async function buildImage() { + if (process.env.SKIP_DOCKER_BUILD === 'true' && imageExists()) { + console.log('\n✅ Using existing workspace:latest image (SKIP_DOCKER_BUILD=true)\n'); + return; + } + + if (imageExists() && !process.env.FORCE_DOCKER_BUILD) { + console.log('\n✅ Using existing workspace:latest image\n'); + return; + } + return new Promise((resolve, reject) => { console.log('\n🏗️ Building workspace Docker image once for all tests...\n');