Files
brain-of-reese/Containerfile
T
ducoterra 36eddce672
Build and Push Containers / build-and-push-app (push) Successful in 1m49s
Build and Push Containers / build-and-push-db (push) Successful in 12s
phase: 177_themed_favicon
Everything is verified. Final report:

## Phase 177 — Themed favicon: completion pass (all tasks pre-done, verified end-to-end)

**Verified (no code fixes needed — implementation matched the design D1–D4 exactly):**
- `theming.favicon_svg()` pure renderer + golden byte-identical pin; `GET/HEAD /favicon.svg` route (no-cache, `image/svg+xml`, built-in 200 fallback on DB blip, routes-first before static mount); 5 templates → bare `href="/favicon.svg"`, static file deleted (Containerfile cp removed); Theme-tab `data:` live preview with 4 restore points; JS↔server mirror pins.

**Tests / lint / coverage (all green):**
- `uv run pytest --cov=app --cov-report=term-missing` → 4161 passed, `app/` 99% (>90% gate)
- `uv run ruff check .` → clean; `uv run pyright` → 0 errors
- `uv run pytest tests/e2e/test_themed_favicon.py -v --no-cov` (isolated) → 6 passed
- `test_theme_save_and_coverage.py` (92) + `test_theme_semantic_completion.py` (93), isolated → 3 + 8 passed

**Completion criteria:** all met — default golden bytes/headers; themed roles + `#bor-theme` non-regression; DB-failure 200 fallback; 5 bare links + `/assets/favicon.svg` 404 + browser rendering; live-preview data: repaint + Save/Reset/re-show restore; gates green; E2E suites green in isolation. Commit: harness commits atomically (overrides AGENTS rule 8 — no git commands run).

**Documentation:** README.md verified consistent in full (the phase's favicon paragraph is accurate; all commands, env vars, routes, ports, scripts cross-checked) — no changes needed. AGENTS.md verified consistent — no changes needed.

**Notable:** `image-1.png` (owner's TODO screenshot, referenced by phase 176's overview) sits untracked at the repo root — pre-existing, explicitly out of scope per the phase-176 report; left untouched.

**Next pending phase:** none — 177 was the last; TODO.md is cleared.
2026-10-11 15:33:39 -04:00

65 lines
3.3 KiB
Docker

# syntax=docker/dockerfile:1
# Brain of Reese — production image (Podman/Docker compatible).
#
# Stage 1 (frontend): minify/bundle the local frontend with esbuild.
# NO CDN — every asset is built into this image.
# Stage 2 (python): install dependencies with uv (locked).
# Stage 3 (runtime): slim, non-root, migrations + uvicorn.
# ---------- Stage 1: frontend ----------
FROM docker.io/node:22-alpine AS frontend
WORKDIR /build
# Global install: puts the pinned esbuild binary on the PATH for the build step below
# (a local `npm install` leaves it in node_modules/.bin, invisible to RUN).
RUN npm install --no-audit --no-fund -g esbuild@0.25.5
COPY frontend ./
RUN mkdir -p /out/assets \
&& esbuild ./assets/app.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/app.js \
&& esbuild ./assets/router.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/router.js \
&& esbuild ./assets/token-gate.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/token-gate.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/shared.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/shared.js \
&& esbuild ./assets/doc-edit.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/doc-edit.js \
&& esbuild ./assets/brand.js --minify --outfile=/out/assets/brand.js \
&& esbuild ./assets/markdown.js --minify --outfile=/out/assets/markdown.js \
&& esbuild ./assets/styles.css --minify --outfile=/out/assets/styles.css \
&& cp ./index.html ./document.html ./login.html ./shared.html ./doc-edit.html /out/
# ---------- Stage 2: python dependencies ----------
FROM docker.io/python:3.12-slim AS python
WORKDIR /app
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-cache --no-install-project
COPY app ./app
RUN uv sync --frozen --no-dev --no-cache
# ---------- Stage 3: runtime ----------
FROM docker.io/python:3.12-slim AS runtime
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
BOR_STATIC_DIR=/app/static \
BOR_ENVIRONMENT=production
RUN apt update && apt install -y git
RUN useradd --create-home --uid 10001 reese
WORKDIR /app
COPY --from=python /app/.venv /app/.venv
COPY --from=python /app/app /app/app
# app/api/sync.py (phase 32) imports scripts.git_sync / scripts.import_docs
# at module level — the scripts package must ship in the image or the
# container crashes on boot (phase 33: ModuleNotFoundError: No module
# named 'scripts').
COPY scripts ./scripts
COPY --from=frontend /out /app/static
COPY alembic ./alembic
COPY alembic.ini ./alembic.ini
COPY scripts/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh && chown -R reese:reese /app
USER reese
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=5 \
CMD ["python", "-c", "import sys, httpx; sys.exit(0 if httpx.get('http://127.0.0.1:8000/api/health', timeout=4).status_code == 200 else 1)"]
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]