From 78df80b18cb5f5355eb8d0903ca6ae4e0165a4f4 Mon Sep 17 00:00:00 2001 From: productdevbook Date: Thu, 20 Aug 2026 11:19:27 +0200 Subject: [PATCH 1/2] ci: print the resolved Python closure, for one run The pin has to come from a resolution rather than a guess: a version invented to make a file look pinned is the same shape as the four ways of reaching green that this repository rules out. pip is not installed on the machine this was written from, so CI takes the measurement. Removed in the next commit. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 219bd2d..474d290 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,6 +84,15 @@ jobs: with: python-version: "3.13" - run: pip install pyyaml openapi-spec-validator + # TEMPORARY, for one run: the exact closure to write into + # scripts/requirements.txt. Removed in the next commit on this branch. + - name: What that resolved to + run: | + python3 -m venv /tmp/pin + /tmp/pin/bin/pip install --quiet pyyaml openapi-spec-validator + echo "----- BEGIN CLOSURE -----" + /tmp/pin/bin/pip freeze + echo "----- END CLOSURE -----" - run: python3 scripts/validate_specs.py --strict # A field this branch stopped documenting, said out loud. Not a failure: # upstream is allowed to withdraw a field, and only a person can tell that From 632d203595587731d801bdd92b2dc8b889e25eb9 Mon Sep 17 00:00:00 2001 From: productdevbook Date: Thu, 20 Aug 2026 11:22:02 +0200 Subject: [PATCH 2/2] ci: pin the Python packages the spec jobs install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three jobs ran `pip install pyyaml openapi-spec-validator` unpinned, and one of them holds `contents: write` and curls four third-party hosts. This repository pins all 34 `uses:` to a commit and gives CLAUDE.md a section on why a tag is a mutable pointer whoever owns the action can move. A floating PyPI resolution executing on the runner is the same pointer under a different name. The versions come from a resolution rather than a guess: run 32353300009 on this branch printed what those two names pull, in a fresh venv so the runner's preinstalled packages could not muddy it. That step is gone again. It resolved to **twenty-one** packages. Pinning only the two that are named — which is what the issue asked for and what I would have written — would have left nineteen floating, including jsonschema, pydantic and rpds-py. That is most of the surface, and it is the reason to measure rather than assume. One file for all three call sites, including the job that needs PyYAML alone. A second list is a second thing to keep in step, and the seconds it saves are not worth that. Not done: `--require-hashes`. It needs the hashes from the same kind of run and is a larger change to how the file is maintained; the pins close the moving target, which is what the issue was about. Closes #209 Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 13 ++---------- .github/workflows/spec-drift.yml | 2 +- scripts/requirements.txt | 36 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 scripts/requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 474d290..fedc4dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,16 +83,7 @@ jobs: - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: "3.13" - - run: pip install pyyaml openapi-spec-validator - # TEMPORARY, for one run: the exact closure to write into - # scripts/requirements.txt. Removed in the next commit on this branch. - - name: What that resolved to - run: | - python3 -m venv /tmp/pin - /tmp/pin/bin/pip install --quiet pyyaml openapi-spec-validator - echo "----- BEGIN CLOSURE -----" - /tmp/pin/bin/pip freeze - echo "----- END CLOSURE -----" + - run: pip install -r scripts/requirements.txt - run: python3 scripts/validate_specs.py --strict # A field this branch stopped documenting, said out loud. Not a failure: # upstream is allowed to withdraw a field, and only a person can tell that @@ -118,7 +109,7 @@ jobs: - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: "3.13" - - run: pip install pyyaml + - run: pip install -r scripts/requirements.txt # The detector before what it detects: a check nobody has seen fail is a # check nobody knows works. - run: python3 scripts/coverage.py --self-test diff --git a/.github/workflows/spec-drift.yml b/.github/workflows/spec-drift.yml index bcf6dae..70a1d83 100644 --- a/.github/workflows/spec-drift.yml +++ b/.github/workflows/spec-drift.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: "3.13" - - run: pip install pyyaml openapi-spec-validator + - run: pip install -r scripts/requirements.txt - name: Refetch the specs under today's date run: | set -euo pipefail diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000..b2ab003 --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,36 @@ +# What the spec tooling runs on, pinned to a resolution rather than a guess. +# +# Every `uses:` in .github/workflows is a full commit SHA, because a tag is a +# mutable pointer whoever owns it can move. A floating `pip install` executing +# on the same runner is that pointer under a different name — and one of the +# jobs that does it can write to this repository. +# +# The two packages actually asked for are PyYAML and openapi-spec-validator. +# The other nineteen are what they pull, which is why naming only the two would +# have left most of the surface floating. +# +# One file for every job, including the one that needs PyYAML alone: a second +# list is a second thing to keep in step, and the seconds it saves are not +# worth that. To move it, add `pip freeze` to a run in a fresh venv and paste +# what it prints — the numbers here came from run 32353300009. + +annotated-types==0.8.0 +attrs==26.1.0 +jsonschema==4.26.0 +jsonschema-path==0.5.0 +jsonschema-specifications==2025.9.1 +lazy-object-proxy==1.12.0 +openapi-schema-validator==0.9.0 +openapi-spec-validator==0.9.0 +pathable==0.6.0 +pydantic==2.13.4 +pydantic-settings==2.15.0 +pydantic_core==2.46.4 +python-dotenv==1.2.3 +PyYAML==6.0.3 +referencing==0.37.0 +rfc3339-validator==0.1.4 +rpds-py==2026.6.3 +six==1.17.0 +typing-inspection==0.4.4 +typing_extensions==4.16.0