Packaging & distribution: pinning, pex/shiv & containers
Hash-pinned deps, single-file zipapps, and distroless tool containers.
A tool that only runs on the author’s laptop is a liability. Shipping it means pinning exactly what it depends on so it is reproducible, packaging it so it installs cleanly, and — for anything that runs in CI or on servers — putting it in a minimal container so its runtime is controlled. The goal is that the tool you tested is byte-for-byte the tool that runs, everywhere.
Pin dependencies by hash, not by hope
requirements.txt with loose version ranges means two installs a week apart can pull different code — a supply-chain risk and a reproducibility hole. Pin every direct and transitive dependency to an exact version AND a hash, so pip refuses anything that does not match. pip-tools (or uv, or Poetry’s lock) compiles a fully-pinned, hashed lockfile from your high-level requirements; commit the lock and install from it with --require-hashes.
# requirements.in (what you actually want)# click>=8# httpx>=0.27# compile to a fully pinned, hashed lockfilepip-compile --generate-hashes -o requirements.txt requirements.in# requirements.txt now contains, for every transitive package:# click==8.1.7 \# --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28# install with hash enforcement — a tampered or swapped package is rejectedpip install --require-hashes -r requirements.txt
Single-file executables with pex/shiv
Sometimes you want to hand someone one file that just runs, without a virtualenv dance. pex and shiv build a zipapp: your code plus its dependencies bundled into a single executable that runs on any compatible Python. It is not a static binary (it still needs a Python interpreter), but it removes "did you pip install everything" as a failure mode and makes distribution a copy.
# shiv: bundle the package + deps into one executable fileshiv -c sec-rotate -o sec-rotate.pyz sec-tools# runs anywhere with a compatible python, no venv setup./sec-rotate.pyz --help# pex is the other common choice (Pants/Twitter lineage)pex sec-tools -c sec-rotate -o sec-rotate.pex --python-shebang='/usr/bin/env python3'
Minimal, non-root tool containers
For CI and server use, a container fixes the runtime exactly. Build it in stages — install and compile in a fat builder, copy only the artifact into a tiny final image — and run it as a non-root user on a distroless or slim base so the attack surface is a fraction of a full OS. The tool ships with its interpreter and its pinned deps and nothing else.
FROM python:3.12-slim AS buildWORKDIR /appCOPY requirements.txt .RUN pip install --require-hashes --no-cache-dir --target=/deps -r requirements.txtCOPY . .# distroless: no shell, no package manager, tiny attack surfaceFROM gcr.io/distroless/python3-debian12:nonrootWORKDIR /appCOPY --from=build /deps /depsCOPY --from=build /app /appENV PYTHONPATH=/depsUSER nonrootENTRYPOINT ["python", "-m", "sec_tools.cli"]