fix(build): Containerfile builds again — relative module imports, all four pages and shared assets in the image

This commit is contained in:
2026-08-24 22:54:30 -04:00
parent 0adc9b5801
commit d7a4064616
16 changed files with 616 additions and 222 deletions
@@ -0,0 +1,91 @@
# Task 01 — Fix the build: relative imports, tag removal, full stage-1 asset coverage
**Phase:** `23_containerfile_build` · **Source:** `TODO.md` L6 —
*"Fix Containerfile build not working"*
## Objective
Make `podman build -f Containerfile .` succeed and ship the **complete**
frontend in the image: all four pages, all four bundled page modules,
the classic `markdown.js`, and the minified `styles.css` — with
`header.js` evaluated exactly once per page.
## Work
1. **Reproduce the failure** and record it in
`.agent/reports/23_containerfile_build/` (log excerpt):
- fast: `npx -y esbuild@0.25.5` on a copy of `frontend/` → the
`Could not resolve "/assets/header.js"` error (root cause, already
reproduced during conversion);
- authoritative: `podman build -f Containerfile .` → stage 1 fails
at the same line.
2. **`frontend/assets/{app,sources,document,login}.js`** — change the
header import from absolute URL to relative (one line each; the
specifiers are currently `from "/assets/header.js"`):
```js
import { … } from "./header.js";
```
(owner-confirmed A4 — relative over esbuild alias; dev-server
behavior is unchanged since the files are side-by-side and the
module URL resolves to the same file.)
3. **Remove the four redundant direct `header.js` tags** (owner-confirmed
A4-2 — the single-evaluation design from `00_phase.md`):
- `frontend/index.html` (~line 119) —
`<script type="module" src="/assets/header.js"></script>`;
- `frontend/sources.html` (~line 125), `frontend/document.html`
(~line 80), `frontend/login.html` (~line 67) — same tag.
- Update the surrounding HTML comments that describe the
header-before-page-script load order (e.g. index.html ~lines
115–119): the order is now guaranteed by the page script's own
`import` (hoisted, evaluated before the page script body calls
`initSharedHeader()`).
4. **`Containerfile` stage 1** — cover the whole app (keep the pinned
`esbuild@0.25.5` and the existing flags):
```dockerfile
RUN mkdir -p /out/assets \
&& esbuild ./assets/app.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/app.js \
&& esbuild ./assets/sources.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/sources.js \
&& esbuild ./assets/document.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/document.js \
&& esbuild ./assets/login.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/login.js \
&& esbuild ./assets/markdown.js --minify --outfile=/out/assets/markdown.js \
&& esbuild ./assets/styles.css --minify --outfile=/out/assets/styles.css \
&& cp ./index.html ./sources.html ./document.html ./login.html /out/
```
(`markdown.js` is a classic script — minify only, **no** `--bundle`;
it exposes globals used by the pages.)
5. **Verify locally (no podman):** with esbuild 0.25.5, all four module
bundles + the markdown minify succeed on the real `frontend/` (not a
copy — the copy was only for the diagnosis).
6. **`podman build -f Containerfile .`** → green.
7. **Image smoke test** (results + log excerpt into the report dir):
- throwaway Postgres 17 + pgvector (`podman compose up -d db` and
point the container at it, or a one-off container with the same
env as `compose.yaml`);
- run the built image (migrations run via the entrypoint);
- `GET /`, `/sources.html`, `/document.html`, `/login.html` → 200;
- `GET /assets/app.js` → 200, minified (single-line-ish), and
contains the header code (e.g. the `clearChatStorage` function
body); `GET /assets/markdown.js`, `/styles.css`, the other three
page modules → 200;
- No CDN rule: none of the four served pages contain an `http(s)://`
`src`/`href` asset reference.
- Teardown the throwaway containers when done.
8. **Dev-server regression check** (the tag removal touches dev page
load — confirm boot order still holds): `uv run uvicorn
app.main:app --reload`, load all four pages, check the sign-out
binding exists exactly once (DevTools: no duplicate listener — one
`POST /api/logout` per click) and `initSharedHeader()` ran. (The
isolated E2E regressions run in task 02.)
## Testing & Quality
- Steps 5–8 above; `uv run ruff check . && uv run pyright` clean
(no Python changes, but keep the gate green).
## Completion Criteria
- [ ] The recorded build failure is fixed at the root cause (relative
imports) — not masked by an alias/patch.
- [ ] All four direct `header.js` tags removed + comments updated; the
page scripts' `import "./header.js"` is the only header load.
- [ ] Stage 1 produces: 4 HTML pages, 4 bundled modules, minified
`markdown.js`, minified `styles.css`.
- [ ] `podman build` green; image smoke all-200 + No CDN + single
header evaluation; dev-server boot unchanged (step 8).
- [ ] Log/screenshot evidence in `.agent/reports/23_containerfile_build/`.