diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efc75863..3d07ede8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -214,13 +214,48 @@ jobs: - if: always() run: docker rm -f openbot-ci >/dev/null 2>&1 || true + # The two packages `static` cannot reach. + # + # Root `typecheck` is `bun run --filter '*' typecheck`, which enumerates workspaces, and `workspaces` + # is app, server and worker. `agent-computer` and `supervisor` are deployables of their own with their + # own lockfiles, and both have shipped a `typecheck` script that nothing ever ran — so a change to + # the process holding the only `spawn` in the deployment, or to the only thing holding a Docker + # socket, reached an image without a type check. + # + # A separate job rather than another line in `static` because each installs from its own lockfile: + # the root install cannot produce their dependency trees. Matrixed so each reports its own result, + # and `fail-fast: false` so one failing does not hide the other's answer. + deployables: + name: types, ${{ matrix.package }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + package: [agent-computer, supervisor] + # Nothing here needs a browser, and the install would otherwise be a few hundred megabytes for + # `agent-computer`. Bun does not run an untrusted package's postinstall anyway; this says so on + # purpose rather than relying on it. + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: 1.3.14 + - run: bun install --frozen-lockfile + working-directory: ${{ matrix.package }} + - run: bun run typecheck + working-directory: ${{ matrix.package }} + # One check for branch protection to require. A new job above is covered by this without anybody # remembering to add it to a list, and a job that was skipped for the wrong reason is not a pass. verify: name: verify runs-on: ubuntu-latest if: always() - needs: [static, test, build, migrations, image] + needs: [static, deployables, test, build, migrations, image] steps: - name: Require every check env: diff --git a/CHANGELOG.md b/CHANGELOG.md index c2456167..7429998b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,16 @@ Sessions survive and nobody signs in again. ### Changed +- **The images are built from the lockfiles that are committed.** Every deployable but the root one + installed with a plain `bun install`, and its `bun.lock` was not even in the build context, so the + committed file could not have been honoured and each build resolved dependencies afresh. An image + built next month was not the image built today, which is the drift the pinned Bun version was + already there to prevent, one layer down — and it is also what a build provenance attestation is + signing. `agent-computer`, `supervisor`, `agent-bot` and `agent-langgraph` now copy their lockfile + and install `--frozen-lockfile`. CI also typechecks `agent-computer` and `supervisor`, which each + shipped a `typecheck` script that nothing ran: they are not workspaces, so root `typecheck` never + reached the process holding the only `spawn` in the deployment, or the only one holding a Docker + socket. - **This deployment does not search documents itself.** A Bot answers from a live system by calling that system's own search as the person asking, so the vendor decides what they may see and there is no second copy of anybody's documents here to keep in step, to secure, or to leave behind when diff --git a/Dockerfile b/Dockerfile index 473ba1a6..24b97ac0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,8 +45,11 @@ COPY server/package.json server/package.json COPY worker/package.json worker/package.json RUN bun install --frozen-lockfile -COPY agent-computer/package.json agent-computer/package.json -RUN cd agent-computer && bun install +# Its lockfile too, and installed against it. `agent-computer/bun.lock` is committed but was not in +# the build context, so it could not have been honoured: this resolved afresh every build, which is +# the drift the note about pinning Bun above is there to prevent, one layer down. +COPY agent-computer/package.json agent-computer/bun.lock agent-computer/ +RUN cd agent-computer && bun install --frozen-lockfile # A second tree with the build-time dependencies left out, for the runtime stage to take. Vite, # biome and the test tooling are a gigabyte that nothing in a running container imports. diff --git a/agent-bot/Dockerfile b/agent-bot/Dockerfile index 6251f01a..2a3c720b 100644 --- a/agent-bot/Dockerfile +++ b/agent-bot/Dockerfile @@ -5,8 +5,9 @@ FROM oven/bun:1.3-alpine WORKDIR /app RUN chown bun:bun /app USER bun -COPY --chown=bun:bun agent-bot/package.json ./agent-bot/ -RUN cd agent-bot && bun install +COPY --chown=bun:bun agent-bot/package.json agent-bot/bun.lock ./agent-bot/ +# Installed against the committed lockfile, which was not in the build context before. +RUN cd agent-bot && bun install --frozen-lockfile # Preserve the repo layout so `../../shared/bot-prompt` resolves the same in the image and locally. COPY --chown=bun:bun shared ./shared diff --git a/agent-computer/Dockerfile b/agent-computer/Dockerfile index 594db89e..be36bffc 100644 --- a/agent-computer/Dockerfile +++ b/agent-computer/Dockerfile @@ -11,8 +11,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends unzip \ ENV PATH="/root/.bun/bin:${PATH}" WORKDIR /app -COPY agent-computer/package.json ./ -RUN bun install +COPY agent-computer/package.json agent-computer/bun.lock ./ +# Installed against the committed lockfile. It was not in the build context before, so the file +# existed and could not have been honoured. +RUN bun install --frozen-lockfile COPY agent-computer/src ./src diff --git a/agent-langgraph/Dockerfile b/agent-langgraph/Dockerfile index a8223fa8..c3c49e7b 100644 --- a/agent-langgraph/Dockerfile +++ b/agent-langgraph/Dockerfile @@ -5,8 +5,9 @@ FROM oven/bun:1.3-alpine WORKDIR /app -COPY agent-langgraph/package.json ./agent-langgraph/ -RUN cd agent-langgraph && bun install +COPY agent-langgraph/package.json agent-langgraph/bun.lock ./agent-langgraph/ +# Installed against the committed lockfile, which was not in the build context before. +RUN cd agent-langgraph && bun install --frozen-lockfile # The repo's own layout, kept exactly, so `../../shared/bot-prompt` resolves the same here as it # does on a laptop. Flattening src to /app/src would change the depth and break only on deploy. diff --git a/supervisor/Dockerfile b/supervisor/Dockerfile index e45689f0..6e75fe2d 100644 --- a/supervisor/Dockerfile +++ b/supervisor/Dockerfile @@ -10,8 +10,9 @@ FROM ghcr.io/spiffe/spire-server:1.15.1 AS spire FROM oven/bun:1.3.14-alpine WORKDIR /app -COPY supervisor/package.json ./ -RUN bun install +COPY supervisor/package.json supervisor/bun.lock ./ +# Installed against the committed lockfile, which was not in the build context before. +RUN bun install --frozen-lockfile COPY --from=spire /opt/spire/bin/spire-server /usr/local/bin/spire-server