Install CI's Python tools from a hash-pinned lock file - #9
Merged
Conversation
Every CI job installed PlatformIO, and one also gcovr, with a bare `pip install name==version`. A version number says which release was asked for; it does not say that what arrived is what the maintainer published. Six of Scorecard's pinned-dependency findings pointed at exactly these lines. tools/requirements-ci.in now holds the versions, and tools/requirements-ci.txt is generated from it with the full transitive closure pinned by hash: 28 packages, 445 hashes. Every job installs with --require-hashes, so a package that does not match fails the install rather than running. The lock has to be resolved for the runner rather than for a maintainer's machine, because dependencies are conditional on both platform and Python version. Resolving it on Windows with Python 3.13 silently omits typing-extensions, which anyio requires on 3.12 and CI would then refuse to install under --require-hashes. The regeneration command in the .in header pins both, and the reason is written down next to it. The PlatformIO and gcovr version environment variables are gone; the lock file is the single place a version is chosen. Cache keys now hash the lock instead of interpolating those variables, so the cache follows the dependency set without anyone having to remember to bump a key. Verified by resolving and hash-checking the install for linux / Python 3.12, which is what the runners use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every CI job installed PlatformIO, and one also gcovr, with a bare
pip install name==version. A version number states which release was askedfor; it does not establish that what arrived is what the maintainer published.
Six of OpenSSF Scorecard's pinned-dependency findings pointed at exactly these
lines.
What changed
tools/requirements-ci.inholds the versions, and is the only place they arechosen.
tools/requirements-ci.txtis generated from it with the full transitiveclosure pinned by hash — 28 packages, 445 hashes.
pip install --require-hashes -r tools/requirements-ci.txt, so a package whose contents do not match itsrecorded hash fails the install instead of running.
PLATFORMIO_VERSIONandGCOVR_VERSIONenvironment variables are gone.Cache keys now hash the lock file, so the cache follows the dependency set
instead of relying on someone remembering to bump a key.
Resolve the lock for the runner, not for your laptop
Dependencies are conditional on both platform and Python version, and
--require-hashesfails the entire install if one is missing rather thanfetching it. Resolving this set on Windows with Python 3.13 omits
typing-extensions, whichanyiorequires on Python 3.12 — the CI runnerswould then refuse to install anything.
The regeneration command in the header of
requirements-ci.inpins both theversion and the platform for that reason:
Verification
The install was resolved and hash-checked for
linux/ Python 3.12, matchingthe runners, before this was pushed. CI on this pull request exercises all six
install steps for real.
🤖 Generated with Claude Code