Closes the unpinned-deps gotcha that broke production once already (Starlette 1.0 shipping in 2026-04 changed the TemplateResponse signature; our floating requirements.txt picked it up on the next rebuild and the dashboard 500'd until 1.0.0-12 patched the call sites). Mechanics: * `requirements.in` — human-edited input, identical contents to the old `requirements.txt`. * `requirements.txt` — now an autogenerated lockfile (876 lines, every transitive pinned with sha256 hashes). Regenerated via `scripts/regenerate-lockfile.sh`, which runs `pip-compile --generate-hashes --strip-extras` in a clean python:3.12-slim container so the script has no host dependencies. * Dockerfile installs with `pip install --require-hashes` — refuses any package whose sha256 doesn't match the lockfile, defending against compromised PyPI mirrors and accidental version drift. Verification: * Container boots clean on the hash-locked install (1.0.0-25). * /health returns 200 with all checks green. * Daily security scan (pip-audit + bandit + gitleaks) returns 0 findings against the new lockfile. Future deps changes: edit requirements.in, run the regenerate script, review the diff, rebuild, commit both files. README §"Updating dependencies" walks through it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.5 KiB
Bash
Executable file
45 lines
1.5 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Regenerate requirements.txt from requirements.in.
|
|
#
|
|
# Run this whenever you add, remove, or change a constraint in
|
|
# requirements.in — never edit requirements.txt by hand. The output is
|
|
# a fully-pinned lockfile with sha256 hashes, consumed at image-build
|
|
# time with `pip install --require-hashes`.
|
|
#
|
|
# Runs pip-compile in a clean python:3.12-slim container so this script
|
|
# has no host dependencies — Docker is enough.
|
|
#
|
|
# Usage:
|
|
# ./scripts/regenerate-lockfile.sh
|
|
#
|
|
# After it runs:
|
|
# - Review the diff (`git diff requirements.txt`) — bumps to
|
|
# transitive deps may be CVE fixes or breaking changes
|
|
# - Rebuild the container locally to confirm install + boot
|
|
# - Commit requirements.in AND requirements.txt together
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
if [ ! -f requirements.in ]; then
|
|
echo "fatal: requirements.in not found in $REPO_ROOT" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Regenerating requirements.txt from requirements.in ..."
|
|
docker run --rm \
|
|
-v "$REPO_ROOT:/work" \
|
|
-w /work \
|
|
python:3.12-slim \
|
|
bash -c "
|
|
pip install --quiet --no-cache-dir --disable-pip-version-check pip-tools 2>&1 | tail -3
|
|
pip-compile --quiet --generate-hashes --strip-extras \
|
|
--output-file=requirements.txt requirements.in
|
|
chown $(id -u):$(id -g) requirements.txt
|
|
"
|
|
|
|
echo "Done. New lockfile is $(wc -l < requirements.txt) lines."
|
|
echo "Review: git diff requirements.txt"
|
|
echo "Verify: docker compose build app && docker compose up -d app"
|