Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Build context exclusions. Until 2026-08-03 this file did not exist, and its
# absence produced a silently broken image.
#
# ## The defect it closes
#
# All three service Dockerfiles do `COPY src ./src` and then build INSIDE the
# image with `npx tsc -b tsconfig.json`. `tsc -b` is a COMPOSITE build: it reads
# `.tsbuildinfo` to decide what still needs emitting. With no `.dockerignore`,
# the working tree's `.tsbuildinfo` files were copied into the build context, so
# `tsc -b` concluded everything was already built — and emitted nothing.
#
# The build exited 0. The image had a `dist/` directory with most files in it and
# no `dist/main.js`. The failure surfaced far downstream, at runtime, as
#
# Error: Cannot find module '/repo/src/apps/core-api/dist/main'
#
# in CrashLoopBackOff. Reproduced and then fixed by measurement on 2026-08-03:
# delete the `.tsbuildinfo` files, rebuild, `main.js` appears.
#
# CI never saw it because CI builds from a fresh checkout, which has no
# `.tsbuildinfo` — the inverse of the usual failure, green in CI and broken on
# every developer machine that had run a build before.
#
# The Dockerfiles also assert their entrypoint exists after building, so a
# regression here fails the BUILD instead of reaching a cluster.
#
# ## Note on `dist`
#
# Excluded for the same reason and one more: every runner stage copies
# `--from=builder`, so a host `dist` is never the artifact that ships. Letting it
# into the context only risks a stale local build masquerading as a fresh one.

# ── The incremental-build state that caused the defect ──────────────────────
**/*.tsbuildinfo

# ── Build output: always produced inside the image, never taken from the host ──
**/dist
**/build
**/out

# ── Reinstalled in the image by `npm ci`; copying the host tree is both slow
# and wrong (native modules are built for the host platform) ──────────────
**/node_modules

# ── Test, coverage and tooling noise: not needed to build, and every megabyte
# is context upload time ──────────────────────────────────────────────────
**/coverage
**/.nyc_output
**/*.log
**/.turbo
**/.cache

# ── VCS and editor ──────────────────────────────────────────────────────────
.git
.github
.gitignore
**/.DS_Store
.vscode
.idea
.obsidian

# ── Local infrastructure and evidence: not inputs to a service image ────────
**/.evidence
product/infra/kind
docker-compose*.yml

# ── Secrets must never enter a build context, even though none are tracked ──
**/.env
**/.env.*
!**/.env.example
10 changes: 10 additions & 0 deletions src/apps/agent-runtime-api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ RUN npm ci --legacy-peer-deps
RUN npm run build:policy && \
npx tsc -b tsconfig.json

# `tsc -b` is incremental and exits 0 when a `.tsbuildinfo` claims the output is
# current — emitting nothing. That shipped a core-api image with no
# `dist/main.js`, found only at runtime as CrashLoopBackOff. `.dockerignore` now
# keeps that state out of the context; this asserts the outcome regardless of
# cause, so a missing entrypoint fails the BUILD instead of a cluster.
RUN test -f src/apps/agent-runtime-api/dist/main.js || { \
echo "FATAL: tsc -b exited 0 but src/apps/agent-runtime-api/dist/main.js was not emitted."; \
echo " Check that .dockerignore still excludes **/*.tsbuildinfo."; \
exit 1; }

# ── Runner ──────────────────────────────────────────────────────────────────
FROM node:20-alpine AS runner

Expand Down
15 changes: 15 additions & 0 deletions src/apps/core-api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ RUN npm ci --legacy-peer-deps
RUN npm run build:policy && \
npx tsc -b tsconfig.json

# The build above can succeed and emit NOTHING. `tsc -b` is incremental: given a
# `.tsbuildinfo` that claims the output is current, it exits 0 without writing a
# single file. That is how this image shipped without `dist/main.js` and met
# CrashLoopBackOff with `Cannot find module '/repo/src/apps/core-api/dist/main'`
# — a runtime failure whose cause was three layers upstream.
#
# `.dockerignore` now keeps `.tsbuildinfo` out of the context, which removes the
# known trigger. This line is the assertion that does not depend on knowing the
# trigger: whatever the reason, an image without its entrypoint fails HERE.
RUN test -f src/apps/core-api/dist/main.js || { \
echo "FATAL: tsc -b exited 0 but src/apps/core-api/dist/main.js was not emitted."; \
echo " An incremental build believed the output was current. Check that"; \
echo " .dockerignore still excludes **/*.tsbuildinfo."; \
exit 1; }

# ── Runner ──────────────────────────────────────────────────────────────────
FROM node:20-alpine AS runner

Expand Down
10 changes: 10 additions & 0 deletions src/packages/mcp-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ RUN npm run build:policy
# same as `npm run build`). Building the whole graph avoids per-image ordering bugs.
RUN npx tsc -b tsconfig.json

# `tsc -b` is incremental and exits 0 when a `.tsbuildinfo` claims the output is
# current — emitting nothing. That shipped a core-api image with no
# `dist/main.js`, found only at runtime as CrashLoopBackOff. `.dockerignore` now
# keeps that state out of the context; this asserts the outcome regardless of
# cause, so a missing entrypoint fails the BUILD instead of a cluster.
RUN test -f src/packages/mcp-server/dist/main.js || { \
echo "FATAL: tsc -b exited 0 but src/packages/mcp-server/dist/main.js was not emitted."; \
echo " Check that .dockerignore still excludes **/*.tsbuildinfo."; \
exit 1; }

# ── Runner ──────────────────────────────────────────────────────────────────
FROM node:20-alpine AS runner

Expand Down
Loading