ci(release): move to actions/attest, and let the SBOM name the tool that built the release - #127
Merged
Conversation
The v0.5.0-rc.1 run - the first time this step ever executed - logged that actions/attest-sbom is deprecated. Its README says the same: deprecated in favour of actions/attest, kept working as a wrapper over it, and "all of the existing action inputs are compatible". Verified against actions/attest's own action.yml rather than taken on trust: sbom-path is there with the same meaning, and providing it is what makes this an SBOM attestation rather than build provenance. Both inputs stay as they were, so this is a change of name and pinned SHA. artifact-metadata: write is deliberately NOT added, and the comment now says why: it is needed for the artifact storage record, which requires push-to-registry, which defaults to false and is unused here. Granting a permission we do not need would contradict the line above it. What a tag will have to confirm: that the emitted predicate type stays https://spdx.dev/Document/v2.3. The step runs before the release is created, so a failure publishes nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The row said NOASSERTION, and that was CORRECT rather than broken: the entry carries no module, component_rows() falls back to "bundled", and the generator deliberately maps "bundled" to empty rather than pretending it is a version. Nothing was asking. So the SBOM could not name a component that ships a piece of itself - the bootloader - inside the executable, under its own licence. It is read through importlib.metadata rather than by importing the package: the answer is identical, and asking would otherwise drag a build tool into --license on any machine that has it. What makes the answer honest is WHERE it is asked - tools/sbom.py runs in the release job minutes after PyInstaller froze the archive, so the installed distribution is the one that made it. Inside the shipped executable there is nothing to ask, and the fallback stays "bundled" rather than "-": the bootloader really is in there, and "-" is this module's word for "not present here". So the binary's own --license output does not change, and the SBOM keeps saying NOASSERTION there. Measured on the real bundle before choosing this scope: VCRUNTIME140.dll does carry a file version and could be read the same way, but that needs ctypes prototypes, a native-prototype guard and a non-Windows fallback. libffi-8.dll carries no version resource at all, so that row stays NOASSERTION honestly. This closes half of the PyInstaller item in the backlog: what built the release is now reported. Pinning which version builds it is untouched and stays a separate decision. Guards: two in tests/test_sbom.py, both proved by mutation and registered - one for the version being reported, one for absence not becoming a guess. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`tools/sbom.py -o -` does not mean stdout - it wrote a file literally named `-` in the project root, and `git add -A` swept it into the previous commit. Co-Authored-By: Claude Opus 5 <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.
Two changes to the release plumbing, both prompted by the
v0.5.0-rc.1run - the first time the attestation step had ever executed.The action is deprecated
That run logged
actions/attest-sbom has been deprecated, please use actions/attest instead. The action's own README says the same: deprecated, kept working as a wrapper overactions/attest, and "all of the existing action inputs are compatible".Checked against
actions/attest'saction.ymlrather than taken on trust:sbom-pathis there with the same meaning, and supplying it is what makes the run an SBOM attestation instead of build provenance. Both inputs therefore stay exactly as they were, so this is a change of name and pinned SHA (v4.2.2, read from the API on 2026-08-12).🔴
artifact-metadata: writeis deliberately not added, and the comment now says why - because it is exactly the kind of thing a later reader "fixes" by granting it. That permission exists to create the artifact STORAGE RECORD, andcreate-storage-recordrequirespush-to-registry, which defaults to false and is unused here. It is a valid permission key, so adding it would parse. It would just grant something we do not need, against the line standing right above it.What a tag has to confirm: that the emitted predicate type stays
https://spdx.dev/Document/v2.3. That is inferred from the wrapper relationship, not measured. The failure mode is cheap - the attestation step runs before the release is created, so a failure publishes nothing at all.The SBOM could not name the tool that built it
v0.5.0-rc.1's SBOM saidNOASSERTIONforPyInstaller (bootloader). That was correct rather than broken: the entry carries no module,component_rows()falls back to"bundled", and the generator deliberately mapsbundledto empty instead of pretending it is a version. Nothing was asking. The result was a bill of materials that could not name a component which ships a piece of itself - the bootloader - inside the executable, under its own licence.It is now read through
importlib.metadata, which does not import the package: the answer is identical, and asking would otherwise drag a build tool into--licenseon any machine that has it. What makes the answer honest is where it is asked -tools/sbom.pyruns in the release job minutes after PyInstaller froze the archive, so the installed distribution is the one that made it.Inside the shipped executable there is nothing to ask, and the fallback stays
"bundled"rather than"-". The bootloader really is in there, and-is this module's word for "not present here"; the first attempt returned it and would have made the binary's own--licensereport a component as missing. So--licenseis unchanged for users, and the SBOM keeps saying NOASSERTION there.Measured on the real bundle before choosing this scope, rather than guessed:
importlib.metadataresolves itVCRUNTIME140.dllcarries FileVersion14.42.34438.0libffi-8.dllcarries no version resource at allThis closes half of the PyInstaller backlog item: what built the release is now reported. Pinning which version builds it is untouched and remains a separate decision - the two are independent.
Verified
python -m pytest tests- 1121 passed - andsmoke_gui.pyOK, after the change tolegal.py; the workflow edit was additionally parsed withyaml.safe_load, since a syntax error there would only surface at tag time.Two guards in
tests/test_sbom.py, both proved by mutation and registered: one for the version being reported, one for absence not becoming a guess. Generated locally, the SBOM now readsPyInstaller (bootloader) 6.21.0whilelibffiand the Microsoft C Runtime stayNOASSERTION.