Skip to content

Commit 1faff16

Browse files
mpstatonclaude
andcommitted
ci(github-actions): build all 16 images and run the unit groups on every PR
The gap that let a twelve-day outage go unnoticed. Two properties conspired: `pnpm build` on a laptop passes regardless, because tsconfig.base.json is sitting right there on disk — the missing COPY only exists inside the image — and a failed Railway deploy is silent, because the previous container keeps serving and the health check keeps passing. So the Docker build only ever ran in production, and production only ever whispered when it broke. This moves that build to the pull request, where it fails loudly and early. Sixteen images in one matrix, fail-fast off so a single broken image cannot hide the state of the other fifteen. The two build shapes are both covered and they are genuinely different: frontends build from the repo root because they need the pnpm workspace, packages/ and tsconfig.base.json; services build from their own directory with a self-contained tsconfig. A per-file audit could not tell them apart — "does this Dockerfile copy a tsconfig?" answered yes for all sixteen while six were broken — so only an actual build catches this class. Nothing is pushed anywhere. The artifact is the exit code. No submodule checkout: clients/ is gitignored out of the Railway build context and no Dockerfile COPYs it, so fetching it here would only buy a credentials problem. Five unit groups run, mirroring scripts/test-all.sh. Group I (e2e) is deliberately excluded — it drives a real backend chain over a live WebSocket and wants NATS plus SurrealDB, so without service containers it would produce a red badge that means nothing, which is worse than no badge. Follow-up. design:drift runs non-gating behind `|| true`, because it reports 99 known F6/F8 failures today. It is there to keep that number visible and to catch the contrast checks, which do pass 30/30. Drop the `|| true` once the debt burns down. Verified before committing: all five test commands pass locally, both build shapes were spot-checked with a real docker build, and the YAML parses to three jobs with sixteen matrix entries. Files changed: - .github/workflows/ci.yml (new — the repo had only pages.yml, so nothing built and nothing tested) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZJvZWco3z7SR2dEhFqEjA
1 parent 4676e5f commit 1faff16

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# CI — build every container image, run every unit-test group.
2+
#
3+
# WHY THIS EXISTS. On 2026-08-15 we found that every frontend Docker build had
4+
# been failing since 2026-08-03 on a single missing `COPY tsconfig.base.json`,
5+
# and nobody noticed for twelve days. Two properties conspired:
6+
#
7+
# 1. `pnpm build` on a laptop passes regardless, because tsconfig.base.json is
8+
# sitting right there on disk. The bug only exists inside the image.
9+
# 2. A failed Railway deploy is SILENT — the previous container keeps serving
10+
# and the health check keeps passing.
11+
#
12+
# So the Docker build only ever ran in production, and production only ever
13+
# whispered when it broke. This workflow moves that build to the one place that
14+
# fails loudly and early: the pull request.
15+
#
16+
# See changelog/2026-08-15_01_Every-Frontend-Deploy-Had-Been-Failing-For-Twelve-Days-On-One-Missing-COPY.md
17+
name: CI
18+
19+
on:
20+
pull_request:
21+
push:
22+
# The trunk is rebuild/turbo-rsbuild, NOT main — see the branch-tier model.
23+
branches: [rebuild/turbo-rsbuild]
24+
25+
# A newer push to the same branch makes the in-flight run irrelevant.
26+
concurrency:
27+
group: ci-${{ github.workflow }}-${{ github.ref }}
28+
cancel-in-progress: true
29+
30+
jobs:
31+
images:
32+
name: image · ${{ matrix.name }}
33+
runs-on: ubuntu-latest
34+
strategy:
35+
# One broken image should not hide the state of the other fifteen —
36+
# that is precisely the failure mode this workflow exists to prevent.
37+
fail-fast: false
38+
matrix:
39+
include:
40+
# Frontends build from the REPO ROOT: they need the pnpm workspace,
41+
# packages/, and tsconfig.base.json. This is the group that broke.
42+
- { name: shell, dockerfile: shell/Dockerfile, context: . }
43+
- { name: chat, dockerfile: apps/chat/Dockerfile, context: . }
44+
- { name: corpora-curator, dockerfile: apps/corpora-curator/Dockerfile, context: . }
45+
- { name: org-workbench, dockerfile: apps/org-workbench/Dockerfile, context: . }
46+
- { name: search-and-add, dockerfile: apps/search-and-add/Dockerfile, context: . }
47+
- { name: search-results, dockerfile: apps/search-results/Dockerfile, context: . }
48+
# Services build from their OWN directory — self-contained tsconfig,
49+
# npm install rather than the workspace. Different shape, same gate.
50+
- { name: content-ingest, dockerfile: services/content-ingest/Dockerfile, context: services/content-ingest }
51+
- { name: ingest, dockerfile: services/ingest/Dockerfile, context: services/ingest }
52+
- { name: prompt-runner, dockerfile: services/prompt-runner/Dockerfile, context: services/prompt-runner }
53+
- { name: prompt-store, dockerfile: services/prompt-store/Dockerfile, context: services/prompt-store }
54+
- { name: record-surrealdb-resolver, dockerfile: services/record-surrealdb-resolver/Dockerfile, context: services/record-surrealdb-resolver }
55+
- { name: response-store, dockerfile: services/response-store/Dockerfile, context: services/response-store }
56+
- { name: row-store, dockerfile: services/row-store/Dockerfile, context: services/row-store }
57+
- { name: social-search, dockerfile: services/social-search/Dockerfile, context: services/social-search }
58+
- { name: workspace, dockerfile: services/workspace/Dockerfile, context: services/workspace }
59+
- { name: xlsx-ingest, dockerfile: services/xlsx-ingest/Dockerfile, context: services/xlsx-ingest }
60+
steps:
61+
# No submodules. The client repos are gitignored out of the Railway build
62+
# context (.railwayignore) and no Dockerfile COPYs clients/, so fetching
63+
# them here would only buy us a credentials problem.
64+
- uses: actions/checkout@v4
65+
66+
- uses: docker/setup-buildx-action@v3
67+
68+
- name: Build ${{ matrix.name }}
69+
uses: docker/build-push-action@v6
70+
with:
71+
context: ${{ matrix.context }}
72+
file: ${{ matrix.dockerfile }}
73+
# Never push. The artifact is the exit code — proof it builds at all.
74+
push: false
75+
cache-from: type=gha,scope=${{ matrix.name }}
76+
cache-to: type=gha,mode=max,scope=${{ matrix.name }}
77+
78+
tests:
79+
name: unit tests
80+
runs-on: ubuntu-latest
81+
steps:
82+
- uses: actions/checkout@v4
83+
84+
- uses: pnpm/action-setup@v4
85+
86+
- uses: actions/setup-node@v4
87+
with:
88+
node-version: 22
89+
cache: pnpm
90+
91+
# --frozen-lockfile is itself a check: it fails when a package.json and
92+
# the lockfile have drifted, which a split-across-commits change can do.
93+
- run: pnpm install --frozen-lockfile
94+
95+
# Mirrors scripts/test-all.sh minus Group I. The e2e group drives a real
96+
# backend chain over a live WebSocket (e2e/harness.mjs wants NATS and
97+
# SurrealDB), so it needs service containers before it can run here.
98+
# Running it without them would produce a red badge that means nothing,
99+
# which is worse than no badge. Tracked as follow-up.
100+
- name: Group C — transport
101+
run: pnpm --filter @augment-it/workspace test
102+
- name: Groups B/D/H — workspace-service
103+
run: pnpm --dir services/workspace test
104+
- name: Groups E/J — resolver
105+
run: pnpm --dir services/record-surrealdb-resolver test
106+
- name: Group F — content-ingest
107+
run: pnpm --dir services/content-ingest test
108+
- name: Group G — corpora-curator
109+
run: pnpm --dir apps/corpora-curator test
110+
111+
design-drift:
112+
name: design drift
113+
runs-on: ubuntu-latest
114+
steps:
115+
- uses: actions/checkout@v4
116+
- uses: pnpm/action-setup@v4
117+
- uses: actions/setup-node@v4
118+
with:
119+
node-version: 22
120+
cache: pnpm
121+
- run: pnpm install --frozen-lockfile
122+
# Reports 99 known failures today, so it cannot gate yet — it runs to keep
123+
# the number visible and to catch the contrast checks, which DO pass 30/30.
124+
# Turn `|| true` off once the F6/F8 debt is burned down.
125+
- name: pnpm design:drift
126+
run: pnpm design:drift || true

0 commit comments

Comments
 (0)