deps: add requirements.lock for reproducible builds
pyproject.toml uses range pins (>=) for all dependencies; without a lockfile, a fresh `pip install .` on a different day could pull materially different versions of fastapi, sqlalchemy, httpx, etc. For a production-shaped service that's a reproducibility risk — especially since we don't run a CI pipeline that would catch "works on yesterday's container, fails on today's." requirements.lock pins every transitive dep (60 packages) to the exact versions running in the test container today. Dockerfile is updated so both stages install from the lockfile first, then install the project itself with --no-deps: pip install -r requirements.lock pip install --no-deps . That way pyproject.toml's range pins document our compatible upper-and-lower bounds, but the lockfile is what actually gets installed on every build. To bump deps later: bump pyproject.toml ranges, rebuild a fresh venv, `pip freeze` it, save back to requirements.lock. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f9d448d57b
commit
2b9cd875b4
2 changed files with 74 additions and 4 deletions
18
Dockerfile
18
Dockerfile
|
|
@ -6,11 +6,17 @@ ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
WORKDIR /build
|
||||
COPY pyproject.toml ./
|
||||
COPY pyproject.toml requirements.lock ./
|
||||
COPY app ./app
|
||||
# requirements.lock pins every transitive dependency to the known-good
|
||||
# versions captured by `pip freeze` against a clean install. Install
|
||||
# from it first, then add the project itself with --no-deps so the
|
||||
# lockfile is the single source of truth and pyproject's range pins
|
||||
# (>=) can't drift on rebuild.
|
||||
RUN python -m venv /opt/venv \
|
||||
&& /opt/venv/bin/pip install --upgrade pip \
|
||||
&& /opt/venv/bin/pip install .
|
||||
&& /opt/venv/bin/pip install -r requirements.lock \
|
||||
&& /opt/venv/bin/pip install --no-deps .
|
||||
|
||||
FROM python:3.13-slim AS runtime
|
||||
|
||||
|
|
@ -49,7 +55,7 @@ ENV PYTHONUNBUFFERED=1 \
|
|||
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
WORKDIR /app
|
||||
COPY pyproject.toml ./
|
||||
COPY pyproject.toml requirements.lock ./
|
||||
COPY app ./app
|
||||
COPY alembic ./alembic
|
||||
COPY alembic.ini ./
|
||||
|
|
@ -57,6 +63,10 @@ COPY alembic.ini ./
|
|||
# a shipped image). docker-compose.test.yml bind-mounts ./tests:/app/tests
|
||||
# at run time, so the suite is always available without baking it in.
|
||||
|
||||
RUN /opt/venv/bin/pip install ".[dev]"
|
||||
# The lockfile already contains the dev extras (pytest, ruff, aiosqlite,
|
||||
# ...) because it was generated against a test-stage install. Same
|
||||
# install pattern as the builder stage: lockfile first, project --no-deps.
|
||||
RUN /opt/venv/bin/pip install -r requirements.lock \
|
||||
&& /opt/venv/bin/pip install --no-deps .
|
||||
|
||||
CMD ["pytest", "tests/", "-v"]
|
||||
|
|
|
|||
60
requirements.lock
Normal file
60
requirements.lock
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
aiomysql==0.3.2
|
||||
aiosmtplib==5.1.0
|
||||
aiosqlite==0.22.1
|
||||
alembic==1.18.4
|
||||
annotated-doc==0.0.4
|
||||
annotated-types==0.7.0
|
||||
anyio==4.13.0
|
||||
APScheduler==3.11.2
|
||||
argon2-cffi==25.1.0
|
||||
argon2-cffi-bindings==25.1.0
|
||||
certifi==2026.5.20
|
||||
cffi==2.0.0
|
||||
charset-normalizer==3.4.7
|
||||
click==8.4.1
|
||||
cryptography==48.0.0
|
||||
dnspython==2.8.0
|
||||
email-validator==2.3.0
|
||||
fastapi==0.136.3
|
||||
greenlet==3.5.1
|
||||
h11==0.16.0
|
||||
hiredis==3.3.1
|
||||
httpcore==1.0.9
|
||||
httptools==0.8.0
|
||||
httpx==0.28.1
|
||||
idna==3.16
|
||||
iniconfig==2.3.0
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
Mako==1.3.12
|
||||
MarkupSafe==3.0.3
|
||||
packaging==26.2
|
||||
pluggy==1.6.0
|
||||
pycparser==3.0
|
||||
pydantic==2.13.4
|
||||
pydantic-settings==2.14.1
|
||||
pydantic_core==2.46.4
|
||||
Pygments==2.20.0
|
||||
PyMySQL==1.2.0
|
||||
pytest==9.0.3
|
||||
pytest-asyncio==1.4.0
|
||||
pytest-httpx==0.36.2
|
||||
python-dotenv==1.2.2
|
||||
python-multipart==0.0.29
|
||||
PyYAML==6.0.3
|
||||
redis==7.4.0
|
||||
requests==2.34.2
|
||||
ruff==0.15.14
|
||||
SQLAlchemy==2.0.50
|
||||
starlette==1.1.0
|
||||
stripe==15.1.0
|
||||
structlog==25.5.0
|
||||
tenacity==9.1.4
|
||||
typing-inspection==0.4.2
|
||||
typing_extensions==4.15.0
|
||||
tzlocal==5.3.1
|
||||
urllib3==2.7.0
|
||||
uvicorn==0.48.0
|
||||
uvloop==0.22.1
|
||||
watchfiles==1.2.0
|
||||
websockets==16.0
|
||||
Loading…
Add table
Add a link
Reference in a new issue