The classic Python build workflow with pip, setuptools, build and twine, the files it needs, how to lint, scan and test, and a complete GitHub Actions pipeline.
Posted
DevOps pipeline loop motif with the title Python with pip and setuptools
The classic Python toolchain splits responsibilities between small tools: pip installs packages, setuptools (or another backend) knows how to build a package, build is the standard front end that calls the backend, and twine uploads the result. It is still the most common setup in enterprise codebases and the base every newer tool builds on.
In CI, each stage of the generic build pipeline maps to a pip and build command, plugin or scanner. Press play, or step through it:
Source + pyproject.toml: A commit triggers CI. pyproject.toml declares the project and build backend, and requirements.txt (compiled by pip-tools) pins versions with hashes.
Install: pip install -r requirements.txt --require-hashes in a fresh venv installs exactly the locked versions; CI caches ~/.cache/pip.
Lint and format: ruff check . && ruff format --check . lint and check formatting; mypy or Pyright add type checking.
Install the project: pip install -e ".[dev]" installs the project and its dev tools, so tests run against the real package metadata.
Test and coverage: pytest --cov=src --cov-fail-under=80 runs the tests and fails if line coverage drops below 80%.
Security scans in parallel: bandit -r src -ll checks our code, pip-audit checks the dependencies against known vulnerabilities, and Gitleaks scans for secrets.
Quality gate: Lint, tests with the 80% coverage gate and both scanners are required checks; one failure stops the pipeline.
Fail the build (fail): A failed check stops the pipeline at the gate, so nothing is packaged, signed or published.
Build the distributions (pass): python -m build creates the wheel (.whl) and sdist (.tar.gz) in dist/, and twine check validates their metadata.
SBOM and signing (pass): cyclonedx-py writes sbom.json from requirements.txt, and the distributions are signed with Sigstore cosign.
Publish (pass): twine upload publishes dist/ to PyPI or a private index (Artifactory, Nexus, devpi).
Two separate flows.Libraries are built into wheels and published; applications are usually installed from a pinned requirements.txt into a virtual environment or container.
PEP 517/518 made the backend pluggable: the [build-system] table tells any front end which backend to install and call. python setup.py install and setup.py bdist_wheel are deprecated.
pip-tools (pip-compile) turns loose requirements in requirements.in into a fully pinned, hashed requirements.txt, which is the classic way to get a lockfile.