From 38fd67b67ed9324bca91ed7bf0e447468e06f445 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Thu, 7 May 2026 15:45:04 -0700 Subject: [PATCH] fix(ci): include bun.lock in image build for deterministic install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI evals all failed on PR #1363 with: error: Could not resolve: "smart-buffer". Maybe you need to "bun install"? error: Could not resolve: "ip-address". Maybe you need to "bun install"? at /opt/node_modules_cache/socks/build/client/socksclient.js:15 The cached node_modules layer in the pre-baked Docker image had `socks` (the new dep) but was missing its transitive deps (smart-buffer, ip-address). The image build copied only package.json into the build context — without bun.lock, `bun install` resolved a different tree than local `bun install` did, dropping required transitive deps. Reproduces locally as 229 packages (correct) when bun.lock is present or absent. Why CI diverged isn't fully understood — possibly Docker layer cache reuse across image rebuilds — but the deterministic fix is to include the lockfile in the image build context and use `--frozen-lockfile`, matching what every CI doc recommends. Changes: - .github/docker/Dockerfile.ci: COPY bun.lock alongside package.json, switch `bun install` → `bun install --frozen-lockfile` so any future lockfile drift fails loudly during image build instead of producing a partially-installed cache that breaks downstream eval jobs. - .github/workflows/evals.yml: include bun.lock in the image-tag hash so adding/removing a dep invalidates the image, AND copy bun.lock into the docker context alongside package.json. - .github/workflows/evals-periodic.yml: same updates. - .github/workflows/ci-image.yml: rebuild trigger now fires on bun.lock changes too; build context includes bun.lock. Image hash changes → fresh image gets built on next CI run → install matches the lockfile exactly → no missing transitive deps. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/docker/Dockerfile.ci | 10 +++++++--- .github/workflows/ci-image.yml | 3 ++- .github/workflows/evals-periodic.yml | 4 ++-- .github/workflows/evals.yml | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/docker/Dockerfile.ci b/.github/docker/Dockerfile.ci index 9dff7c0a..ebf4a4d1 100644 --- a/.github/docker/Dockerfile.ci +++ b/.github/docker/Dockerfile.ci @@ -89,10 +89,14 @@ RUN for i in 1 2 3; do \ && fc-cache -f \ && rm -rf /var/lib/apt/lists/* -# Pre-install dependencies (cached layer — only rebuilds when package.json changes) -COPY package.json /workspace/ +# Pre-install dependencies (cached layer — only rebuilds when package.json or +# bun.lock changes). Copy BOTH so install is deterministic and matches local +# resolution. Without bun.lock here, bun install resolved transitive deps +# differently in CI vs local (observed on v1.28.0.0: socks landed but +# smart-buffer + ip-address didn't make it into the cached node_modules). +COPY package.json bun.lock /workspace/ WORKDIR /workspace -RUN bun install && rm -rf /tmp/* +RUN bun install --frozen-lockfile && rm -rf /tmp/* # Install Playwright Chromium to a shared location accessible by all users ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers diff --git a/.github/workflows/ci-image.yml b/.github/workflows/ci-image.yml index 00d38637..1ca283ad 100644 --- a/.github/workflows/ci-image.yml +++ b/.github/workflows/ci-image.yml @@ -9,6 +9,7 @@ on: paths: - '.github/docker/Dockerfile.ci' - 'package.json' + - 'bun.lock' # Manual trigger workflow_dispatch: @@ -22,7 +23,7 @@ jobs: - uses: actions/checkout@v4 # Copy lockfile + package.json into Docker build context - - run: cp package.json .github/docker/ + - run: cp package.json bun.lock .github/docker/ - uses: docker/login-action@v3 with: diff --git a/.github/workflows/evals-periodic.yml b/.github/workflows/evals-periodic.yml index 20035c45..df16bcbc 100644 --- a/.github/workflows/evals-periodic.yml +++ b/.github/workflows/evals-periodic.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v4 - id: meta - run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json') }}" >> "$GITHUB_OUTPUT" + run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json', 'bun.lock') }}" >> "$GITHUB_OUTPUT" - uses: docker/login-action@v3 with: @@ -43,7 +43,7 @@ jobs: fi - if: steps.check.outputs.exists == 'false' - run: cp package.json .github/docker/ + run: cp package.json bun.lock .github/docker/ - if: steps.check.outputs.exists == 'false' uses: docker/build-push-action@v6 diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml index a7b1fd99..45d4b693 100644 --- a/.github/workflows/evals.yml +++ b/.github/workflows/evals.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v4 - id: meta - run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json') }}" >> "$GITHUB_OUTPUT" + run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json', 'bun.lock') }}" >> "$GITHUB_OUTPUT" - uses: docker/login-action@v3 with: @@ -43,7 +43,7 @@ jobs: fi - if: steps.check.outputs.exists == 'false' - run: cp package.json .github/docker/ + run: cp package.json bun.lock .github/docker/ - if: steps.check.outputs.exists == 'false' uses: docker/build-push-action@v6