You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Restructure the PyPI publish job in .github/workflows/release-python.yml so the CycloneDX SBOM (sbom-python / *.cdx.json) never lands in the dist/ upload directory. That makes the "stray SBOM breaks twine" failure structurally impossible, letting us delete the cleanup step and retire most of the CI guard that grew out of PR #50.
Background
The publish job currently downloads artifacts with:
merge-multiple: true pulls every uploaded artifact into dist/ — the wheels (wheels-*), the sdist, and the sbom-python artifact (ordvec-python.cdx.json). Because pypa/gh-action-pypi-publish uploads everything in dist/ and twine rejects a stray .cdx.json, PR #50 added a cleanup step (find dist -name '*.cdx.json' -delete) before publish, plus a CI guard (tests/release_publish_invariants.sh) asserting that cleanup runs in the right order.
That guard took five review rounds (deletion-proof → publish-scope → multi-line → non-executing-text → block-comments) because it hand-rolls YAML/step parsing in awk. It's correct now, but it's testing around a fragile design rather than removing the failure mode.
Proposed fix (make it structurally impossible)
Download only the wheel + sdist artifacts into dist/, so the SBOM is never present at upload time.
The sbom-python artifact is simply not downloaded into dist/ (download it elsewhere if the publish job needs it, or not at all).
Option B — pattern/exclude: keep one download but use a pattern: that matches the wheel/sdist artifacts and excludes sbom-python (verify actions/download-artifact glob/exclude semantics — it uses @actions/glob).
Payoff
Deletes the fragile cleanup-ordering invariant entirely (remove the find … -delete step).
tests/release_publish_invariants.sh shrinks to part 1 only — assert *.cdx.json is gitignored (git check-ignore), the simple/robust check. The whole publish-job awk (part 2) can go.
Removes the maintenance burden that drew 5 review rounds.
Acceptance criteria
publish job: dist/ contains only wheels + sdist at upload time (SBOM not present).
The Drop the SBOM from the PyPI upload dir (find … -delete) step removed.
tests/release_publish_invariants.sh part 2 (download→clean→publish ordering) removed; part 1 (gitignore) retained; the release-guard job in ci.yml kept (now just the gitignore check).
SBOM still produced + uploaded as the sbom-python artifact; PEP 740 attestation still covers dist/*.whl + dist/*.tar.gz.
Crate side: the *.cdx.json.gitignore entry (keeps cargo publish from seeing a dirty tree) is already the idiomatic structural fix — keep it.
Related but separate: consider actionlint + zizmor CI jobs for generic GitHub Actions hygiene (action pinning, permissions, injection) instead of relying on PR-bot findings.
Summary
Restructure the PyPI
publishjob in.github/workflows/release-python.ymlso the CycloneDX SBOM (sbom-python/*.cdx.json) never lands in thedist/upload directory. That makes the "stray SBOM breaks twine" failure structurally impossible, letting us delete the cleanup step and retire most of the CI guard that grew out of PR #50.Background
The
publishjob currently downloads artifacts with:merge-multiple: truepulls every uploaded artifact intodist/— the wheels (wheels-*), thesdist, and thesbom-pythonartifact (ordvec-python.cdx.json). Becausepypa/gh-action-pypi-publishuploads everything indist/and twine rejects a stray.cdx.json, PR #50 added a cleanup step (find dist -name '*.cdx.json' -delete) before publish, plus a CI guard (tests/release_publish_invariants.sh) asserting that cleanup runs in the right order.That guard took five review rounds (deletion-proof → publish-scope → multi-line → non-executing-text → block-comments) because it hand-rolls YAML/step parsing in awk. It's correct now, but it's testing around a fragile design rather than removing the failure mode.
Proposed fix (make it structurally impossible)
Download only the wheel + sdist artifacts into
dist/, so the SBOM is never present at upload time.Option A — targeted downloads (most explicit):
The
sbom-pythonartifact is simply not downloaded intodist/(download it elsewhere if the publish job needs it, or not at all).Option B — pattern/exclude: keep one download but use a
pattern:that matches the wheel/sdist artifacts and excludessbom-python(verifyactions/download-artifactglob/exclude semantics — it uses@actions/glob).Payoff
find … -deletestep).tests/release_publish_invariants.shshrinks to part 1 only — assert*.cdx.jsonis gitignored (git check-ignore), the simple/robust check. The whole publish-job awk (part 2) can go.Acceptance criteria
publishjob:dist/contains only wheels + sdist at upload time (SBOM not present).Drop the SBOM from the PyPI upload dir(find … -delete) step removed.tests/release_publish_invariants.shpart 2 (download→clean→publish ordering) removed; part 1 (gitignore) retained; therelease-guardjob inci.ymlkept (now just the gitignore check).sbom-pythonartifact; PEP 740 attestation still coversdist/*.whl+dist/*.tar.gz.Notes
*.cdx.json.gitignoreentry (keepscargo publishfrom seeing a dirty tree) is already the idiomatic structural fix — keep it.actionlint+zizmorCI jobs for generic GitHub Actions hygiene (action pinning, permissions, injection) instead of relying on PR-bot findings.Refs: PR #50; guard commits
49b97db→58a2fac.