Unit 5.8 · about 4 h
34 / 324Dependency locks, run manifests and quality gates
A result that needs your laptop to reproduce is a rumor. Locks plus manifests plus gates make the same code produce the same result on another machine.
- implement
You can now
- Lock an application with
uv lockand reproduce the environment withuv sync --locked. - Record code, configuration, data identity, seeds and output checksums in a run manifest.
- Wire format, lint, typecheck, test, build and clean-install gates with
pre-commitand CI.
Covers
- Dependency locks, clean environments and repeatable builds
- Data identity, configuration, seeds, manifests and checksums
- Type, lint, test, build and installation gates
Study, in this order
3 steps
- Packaging and shipping codeThe Missing Semester of Your CS Education
Dependencies and Environments with
venvand uv; Artifacts and Packaging withpyproject.tomlanduv build; Releases and Versioning with pins versus ranges; Reproducibility withuv lock, lockfile contents, hermetic Nix and Bazel, Dependabot and rollback; Configuration - Code Quality: gates and automationThe Missing Semester of Your CS Education
Formatting; Linting; Pre-commit hooks; Continuous integration with matrices and badges; Command runners
- Versions, environments and packaging recipesStas' Python Cookbook - debugging and testing
Versions and Dependencies: Querying installed package versions and Virtual environments; Packaging and Requirements: Modern packaging with
pyproject.toml
Focus on
- Applications pin, libraries range.
uv.lockrecords exact versions with hashes, and CI enforces it withuv sync --locked. - A manifest names everything the run needs: commit sha, config, data checksum, seed, environment lock and output hash. Diff the manifest, not memories.
- Gates run in order of cost: format, lint, typecheck, fast tests, build, clean-environment install. Pre-commit runs the cheap ones locally; CI runs all of them.
- Hermetic builds pin the toolchain too, for bit-identical artifacts. Reach for Nix or Bazel only when lockfiles plus containers still diverge.
Reproduce closed-book
uv init plus uv lock on a fresh package and get a uv.lock opening with version = 1 and revision = 3 (uv 0.11.32). Add a dependency to pyproject.toml without relocking and compare uv sync --locked, which errors, with plain uv sync, which rewrites the lock. Create a venv, diff printenv before and after, and see VIRTUAL_ENV and VIRTUAL_ENV_PROMPT appear with python resolving inside the venv.Traps
- Committing the
venvor syncing without--lockedin CI. Plainuv syncquietly re-locks whenpyproject.tomlchanged, so CI tests versions nobody committed;--lockedfails on the stale lock. - Pinning
==in a reusable library. Your pin becomes every downstream conflict, so range the library and pin the app. - CI that tests the working tree but never installs the built wheel. Packaging bugs ship green, so add a clean-install smoke step.
Practice
Missing Semester environment and lockfile exercises
Exercises, items 1 and 2: printenv diff across venv activation; package with pyproject.toml plus lockfile inspection
Missing Semester formatter, linter and CI exercises
Exercises, items 1 and 3: formatter plus linter with pre-commit hooks; CI on every push with an intentional breakage
Break the build on purpose once and confirm CI catches it before review.
Self-test
-
uv.lockexists but CI runsuv syncwithout--locked. What breaks?Nothing whilepyproject.tomlanduv.lockagree: uv installs the locked versions. When someone editspyproject.tomlwithout relocking, plainuv syncre-resolves and rewrites the lock in CI, while--lockedexits with an error. Verified with uv 0.11.32. -
Why does the lecture recommend ranges for libraries and pins for apps?
Libraries compose with other dependencies, so strict pins conflict. Apps are final consumers, so exact pins give every run identical inputs. -
A manifest lists code sha, config, data checksum, seed, lockfile hash and output hash. Identical code gives a different output: where do you look?
The config, data, seed and environment entries. The manifest turns same code with different results into a named diff. -
Pre-commit passes but CI fails on lint. What usually differs?
Versions or scope: hooks pinned differently from CI, or CI checks files the hooks skip. Pin the same tool versions in both.
Completion criterion
Stop when
A clean checkout runs one command to a green gate, uv sync --locked reproduces the environment per the lock hashes, and the run manifest explains any output change.
Leads to
- 6.7 Health checks, containers, supervision and runbooks Operate the released artifact with supervision, health checks and runbooks.
- 6.6 Structured logs, metrics and traces Emit the logs, metrics and traces the manifest points at.
Comes after
- 3.8 Imports, virtual environments and src-layout packagingLock the dependencies this package declares.
- 5.6 Git history, merges, rebases, bisect and recoveryVersion releases and lockfiles on top of a disciplined history.
- 5.7 SSH trust, keys, forwarding and tmux sessionsRun clean-environment builds on the remote host you can now reach reliably.
- 7.9 Repeatable CMake builds and test targetsCapture the compiler, interpreter and test configuration as reproducible evidence.
Resources for this unit
- The Missing Semester of Your CS Education Complete the 2026 lectures on the shell, command-line environment, development tools, debugging and profiling, Git, packaging and shipping code, and code quality. Apply every exercise to the same stage project.
- Stas' Python Cookbook - debugging and testing Use Part III and the packaging sections for debugger, traceback, pytest, logging, profiling and clean-environment recipes.
Also in this section
- pytest documentation Use Get Started, fixtures, parametrization, monkeypatch, caplog and temporary paths to build behavior and failure-path tests without order dependence.
- Hypothesis documentation Follow the Quick start and property-based testing guides, then generate operation histories and retain the minimized example for one real defect.
- The Art of Debugging Use the Unix, Python and compiled-code sections during a real seeded defect. Follow the evidence and tiny-reproducer workflow rather than reading the unfinished book front to back.
- Pro Git Use Chapters 2-3 and the sections on debugging, internals and recovery while constructing a history for bisect and recovering a deliberately displaced commit.
- AlgoMaster Python - mocking Use the Mocking lesson for
unittest.mock:MockversusMagicMock,patchtargets,autospecand the over-mocking trap. The pytest docs remain the reference for fixtures andmonkeypatch.