This document describes how surface-audit releases are managed across
PyPI, GitHub Releases, GHCR, and the reusable GitHub Action.
surface-audit uses two related but different versioning surfaces:
- Package release tags such as
v1.0.7are immutable release markers for the Python package, GitHub Release assets, SBOMs, signatures, and GHCR image tags. - Major action tags such as
v1are moving compatibility tags for the reusable GitHub Action.v1points to the latest compatible1.xrelease, not specifically tov1.0.0.
For example, after v1.0.7 was released, both v1.0.7 and v1 point
to the same release commit. Users can choose:
# Gets compatible bug fixes on the 1.x line automatically.
uses: fillbyte/surface-audit@v1
# Pins the exact action release for maximum reproducibility.
uses: fillbyte/[email protected]This follows GitHub's documented action-maintenance guidance: create
semantic version tags such as v1.1.3 and keep major tags such as v1
current with the latest compatible release.
The same release is named differently depending on where users consume it:
| Surface | Example | Meaning |
|---|---|---|
| PyPI package | 1.0.7 |
Exact Python package version installed by pip or pipx. |
| GitHub Release tag | v1.0.7 |
Exact release commit and assets. The v prefix is a Git tag convention. |
| GitHub Action tag | v1 |
Moving major tag for the latest compatible 1.x action release. |
| GHCR image tag | 1.0.7 |
Exact container image for one release. |
| GHCR image tag | 1.0 |
Moving image tag for the latest compatible 1.0.x patch. |
| GHCR image tag | 1 |
Moving image tag for the latest compatible 1.x image. |
| GHCR image tag | latest |
Convenience image tag for the newest stable release. |
So v1 does not mean "the original v1.0.0 forever." It means "the
latest compatible release on the 1.x line." After v1.0.7, it is
normal for v1 to point at the same commit as v1.0.7.
Each tagged release publishes four surfaces:
- PyPI: the canonical Python package installed by
pip,pipx, or Python environments. - GitHub Releases: wheel, sdist, CycloneDX SBOM, and Sigstore bundles.
- GHCR: container tags for
latest, major, major/minor, and exact version, for examplelatest,1,1.0, and1.0.7. - GitHub Action: the repository root
action.yml, consumed throughuses: fillbyte/surface-audit@....
GitHub Packages may show the GHCR image after the first successful container release. PyPI packages do not appear in GitHub's Packages panel because PyPI is a separate registry.
flowchart LR
pr["Pull request"] --> checks["Required checks"]
checks --> review["Maintainer review"]
review --> main["Squash merge to main"]
main --> tag["Annotated vX.Y.Z tag"]
tag --> release["Release workflow"]
release --> approval["pypi environment approval"]
approval --> pypi["Publish to PyPI"]
release --> ghrel["GitHub Release assets"]
release --> ghcr["GHCR image tags"]
release --> major["Move v1 action tag"]
Changes to main go through a pull request. The branch rules require:
- one approving review
- code owner review
- required status checks for Python 3.10, 3.11, 3.12, 3.13, 3.14, distribution build, and the Python and Actions CodeQL analyses
- linear history and squash merges
The pypi environment requires maintainer approval before the PyPI
publish job can proceed. This is intentional. GitHub Actions cannot
approve pull request reviews in this repository, and auto-merge is
disabled.
-
Update
pyproject.tomlwith the new version. -
Add the release notes to
CHANGELOG.md. -
Open a pull request and wait for CI, Pages, and CodeQL checks.
-
Merge with squash after review.
-
Create and push the exact release tag, replacing
vX.Y.Zwith the version being released:git tag -a vX.Y.Z -m "vX.Y.Z" git push origin vX.Y.Z -
Approve the
pypienvironment deployment when the release workflow pauses. -
Confirm the release workflow completed successfully.
-
Confirm PyPI, GitHub Release assets, GHCR tags, and the major action tag.
The release workflow updates the major action tag automatically:
git tag -fa v1 -m "Update v1 to vX.Y.Z" "$GITHUB_SHA"
git push origin refs/tags/v1 --forceForce-updating v1 is expected because it is a moving compatibility
tag. Exact tags such as v1.0.7 should only be moved to recover from a
failed release before users depend on it; normal releases should create
a new exact tag.
If the release workflow fails after an exact tag has been pushed:
- Inspect the failing job logs.
- Fix the release workflow or packaging issue on
mainthrough a pull request. - Re-point the exact tag only if the failed release did not complete successfully.
- Re-run the tag-triggered workflow by pushing the corrected tag.
- Delete or cancel failed historical runs when GitHub permissions allow it.
Never upload files manually to PyPI or GHCR unless automation is fully
blocked and the manual action is documented in CHANGELOG.md.
gh release view vX.Y.Zlists wheel, sdist, SBOM, and Sigstore JSON files.- PyPI reports the new version.
- GHCR exposes
latest, major, major/minor, and exact version tags. git rev-parse v1^{commit}matchesgit rev-parse vX.Y.Z^{commit}.gh run list --limit 10shows no current failed release run.- The local tree is clean after
make clean.