feat: add Docker image, static server and GitLab CI
The server is a placeholder for the one in docs/ARCHITECTURE.md — it serves public/ and a health endpoint, nothing more. Reads are confined to public/ by resolving the path and checking it stays inside the root; string-matching ".." is not enough, since encodings and URL normalisation both change what the string looks like and only the resolved path says which file would open. The image runs typecheck and tests during build, ships no source or toolchain, and runs unprivileged. CI's feed job fails if the event count collapses. A source that quietly stops yielding events is what a parser-only pipeline is most prone to, and nothing else would surface it. Everything is offline, so a red pipeline always means the code changed rather than a wiki being down. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
71bff72ee9
commit
517066dc65
+42
@@ -0,0 +1,42 @@
|
||||
# Build the static site, then serve it from a distroless-ish runtime.
|
||||
#
|
||||
# Two stages so the image ships the built assets and nothing else: no source,
|
||||
# no fixtures, no toolchain. The build is fully offline — it parses checked-in
|
||||
# fixtures rather than fetching anything — so the image is reproducible and
|
||||
# needs no network at build time.
|
||||
|
||||
FROM oven/bun:1.3-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
# Dependencies first, so a source-only change reuses this layer.
|
||||
COPY package.json bun.lock ./
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
COPY tsconfig.json index.html ./
|
||||
COPY src ./src
|
||||
COPY scripts ./scripts
|
||||
COPY fixtures ./fixtures
|
||||
COPY test ./test
|
||||
|
||||
# Fail the image on a type error or a failing test rather than shipping it.
|
||||
RUN bun run typecheck && bun test
|
||||
RUN bun run build
|
||||
|
||||
|
||||
FROM oven/bun:1.3-alpine AS runtime
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
|
||||
# Run unprivileged. The bun image ships a `bun` user; use it rather than root.
|
||||
COPY --from=build --chown=bun:bun /app/public ./public
|
||||
COPY --from=build --chown=bun:bun /app/serve.ts ./serve.ts
|
||||
USER bun
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD bun -e "await fetch('http://127.0.0.1:'+(process.env.PORT??3000)+'/api/health').then(r=>{if(!r.ok)process.exit(1)})"
|
||||
|
||||
CMD ["bun", "run", "serve.ts"]
|
||||
Reference in New Issue
Block a user