Release Hygiene¶
Why Release Hygiene Is a Product Surface¶
Release hygiene in this repository is a delivery-control surface, not a clerical appendix. It is where a project decision is converted into a stable public artifact under explicit constraints.
The practical reason for this page is to keep release outputs bounded and reproducible:
- release tags should advance only when dependency and workflow drift are validated,
- publishing should fail fast when toolchain assumptions change,
- rollback paths must be deterministic when a late job fails.
In short: release hygiene reduces the operational risk of version churn.
This project pins GitHub Actions by full commit SHA in release workflows. Before tagging a release, validate those pins so GitHub Actions does not fail during job setup after the tag has already been pushed.
Run:
python tools/check_github_action_refs.py .github/workflows/publish.yml
python tools/check_version_sync.py
GITHUB_REF_NAME=vX.Y.Z python tools/check_release_tag_version.py
python tools/check_meta_distribution.py
python tools/build_reproducible_release.py --outdir dist
twine check dist/*
The reproducible builder derives SOURCE_DATE_EPOCH from the exact Git HEAD
unless the caller supplies it explicitly. Wheels are built through the declared
PEP 517 backend. Source distributions are then rewritten without changing
package file names or bytes: member order, ownership, permissions, timestamps,
PAX metadata, and the gzip header are canonicalized. Unsafe paths, links,
unexpected artifact sets, invalid epochs, and destination collisions fail
closed. A release candidate is reproducible only when two clean builds from the
same commit produce identical SHA-256 values for both wheel and sdist.
The action-reference guard checks every remote owner/repo@ref action in the
workflow, requires full 40-character commit SHAs, and verifies each ref resolves
through the GitHub API. Local actions and docker:// actions are ignored.
The scpn-meta distribution guard emits non-publishing JSON evidence for the
optional meta-transfer console surface. It verifies package metadata, the
spo and scpn-meta entry points, the importable Click target, and that
publishing remains disabled unless an explicit future publication step is added.
Download-and-run installer steps must also be pinned to explicit tool versions.
Release control outcome¶
- This page defines the operational boundary for what gets published, and it is used as a hard gate before any version movement.
- Treat release hygiene evidence as mandatory evidence for runtime assumptions, action compatibility, and package metadata integrity.
- Keep all tooling references current so a tag cannot depend on stale action revisions or unverified metadata.
For example, the Kani workflow installs
kani-verifierwith a fixed--versionbefore runningcargo kani setup; do not reintroduce an unboundedcargo install <tool>path in CI.
If a tag-triggered publish run fails before uploading, delete the failed tag, bump the release metadata, fix the workflow on a pull request, and tag the new version only after CI and the local release guards pass.
If PyPI upload succeeds but a later release job fails, do not reuse the same
version. PyPI files are immutable, so the repair must land on main with a new
patch version and a new tag.
For Rust wheel builds in publish.yml, Linux manylinux jobs must pass an
explicit CPython interpreter path such as
/opt/python/cp312-cp312/bin/python to maturin. The manylinux container PATH
does not guarantee a usable python3 selector for every target. Cross-running
that ARM interpreter from an x86 runner fails with Exec format error, so the
Linux ARM wheel job must run on a native ubuntu-24.04-arm runner.
For the container job, refresh pinned base-image digests before tagging. A stale
tag@sha256 pair fails before security scanning because Docker cannot resolve
the source metadata. The runtime stays on a pinned Python image whose CPython
minor has no fixable HIGH or CRITICAL scanner findings at release time. The
Trivy and Grype gates still scan the image, but they fail the release only on
fixable HIGH or CRITICAL findings; unfixed distribution advisories are recorded
by the scanner output and must be revisited when fixed package versions exist.
The container FFI builder should use the same CPython minor as the runtime image
and install the stable Rust toolchain used by CI, not the workspace MSRV,
because locked transitive crates may require newer Cargo manifest support and a
mismatched CPython wheel will not import at runtime.
Fuzzing Findings¶
ClusterFuzzLite failures are treated as release blockers when the crash is
reproducible. For policy YAML fuzzing, malformed YAML parser failures must be
contained at the loader boundary and reported as ValueError parse errors,
rather than escaping as uncaught parser exceptions. Add a regression test with
the minimized payload before rerunning or merging the release PR.