-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
77 lines (67 loc) · 3.53 KB
/
Copy pathDockerfile
File metadata and controls
77 lines (67 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# Runtime image for the API and the worker.
#
# One image, two roles. The API and the worker run the same code against the
# same database and differ only in the command they are started with, so
# building them separately would mean two things to keep in step for no benefit.
#
# The build context is the REPOSITORY ROOT, not `app/`. That is load-bearing.
# Evidence adapters resolve their fixtures with
# `Path(__file__).resolve().parents[3]`, which walks
# `.../app/kernel_api/adapters/x.py` up to the repository root - so `fixtures/`,
# `build/_schemas/` and `app/policy/` must sit beside `app/` inside the image at
# the same relative depth. An image built with `app/` as the context resolves
# every fixture to `/fixtures` and finds nothing.
FROM python:3.12-slim AS runtime
# Runtime and transitive versions are resolved in uv.lock and exported with
# hashes to app/requirements-runtime.lock. The project is then installed without
# dependency resolution, so Docker, CI, and a clean clone use the same graph.
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# `psycopg[binary]` ships its own libpq, so no system package is needed. That is
# the only reason this image can stay on `slim` without apt-get.
WORKDIR /srv/merchval
COPY app/ ./app/
COPY fixtures/ ./fixtures/
COPY build/ ./build/
# The slim base is not rebuilt on our schedule; roll its packages forward so
# published security fixes land in the image (trivy HIGH gate in
# security-scan.yml). Runtime dependencies still come from pip only.
RUN apt-get update \
&& apt-get upgrade -y \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /srv/merchval/app
# The base image ships build tools pinned below their CVE fixes, and
# setuptools vendors its own copies of jaraco.context and wheel - so the
# setuptools pin itself must move past CVE-2025-47273, CVE-2026-23949,
# and CVE-2026-24049.
RUN python -m pip install --upgrade pip \
&& python -m pip install setuptools==84.0.0 jaraco.context==6.1.0 wheel==0.46.2 \
&& python -m pip install --require-hashes -r requirements-runtime.lock \
&& python -m pip install --no-deps . \
&& python -m pip check
# `pip install .` is not editable, so `kernel_api` also lands in site-packages.
# Two copies now exist, and `_repo_root()` gives a different answer depending on
# which one is imported: from the source tree it resolves to `/srv/merchval` and
# finds the fixtures, from site-packages it resolves to `/usr/local/lib` and
# finds nothing - silently, because a missing order is a fact the packet records
# rather than an error.
#
# Today the source copy wins only because `python -m` puts the working directory
# first and the working directory happens to be this one. Naming the path makes
# that a property of the image instead of a coincidence, and keeps it true for
# anything started from a different directory.
ENV PYTHONPATH=/srv/merchval/app
# Run as a non-root user. The application writes nothing to its own tree at
# runtime - artifacts go to a mounted volume - so the tree stays owned by root
# and is read-only to the process.
RUN useradd --create-home --uid 1000 merchval
USER merchval
EXPOSE 8000
# No default credential and no bypass. An image that shipped `unsafe_open` would
# undo Sprint 07 for anyone who ran it without reading the compose file.
ENV AUTH_MODE=api_key
# Overridden by the compose file per role. The default is the API because that
# is what a bare `docker run` is most likely to want.
CMD ["python", "-m", "uvicorn", "kernel_api.app:app", "--host", "0.0.0.0", "--port", "8000"]