Tag each release automatically when its version reaches main - #176
Merged
Merged
Conversation
v0.1.2 reached `main` with #59 on 2026-09-09 and stayed untagged until today, when it was tagged by hand on `7743826`, the commit that first carried it. Tagging was a manual step with no owner, so nothing noticed the gap. `Tag release` makes it automatic. A push to `main` that changes `version.txt` is the release event, and the workflow tags that commit `v<version>` as an annotated tag, which matches how v0.1.1 and v0.1.2 are shaped. It exits quietly when the tag already exists, so reruns and unrelated pushes are safe. A manual dispatch from any branch other than `main` is skipped, since it would tag a commit `main` never published. It also refuses to tag when `version.txt`, `pyproject.toml`, and `uv.lock` disagree. The release version lives in those three files and no other check keeps them in step. A tag is the point where a mismatch becomes permanent, so the run fails with all three values in the error. `version.txt` is attacker-controlled by anyone with write access and becomes a git ref here, so it passes the same semver check `sync_version.yml` uses before it reaches `git tag`. No `${{ }}` expression appears inside `run:`. The job holds `contents: write` and nothing else, and the only action is `actions/checkout` at the SHA every other workflow pins. The workflow lives on `main` only after the next promotion, so the first release it tags is the next version bump that reaches `main` after that. Tested against a local bare remote with the step's own script, extracted from the YAML. An existing version exits 0 with "already exists". A new version in all three files creates an annotated tag and a rerun is a no-op. A stale `uv.lock` fails with the three values. An invalid version fails before touching git. `actionlint` is clean and `uv run pytest` gives 278 passed, 1 skipped. CONTRIBUTING.md's Release Process section now says tagging is automatic. Signed-off-by: Rock Lambros <rock@rockcyber.com>
rocklambros
requested review from
GangGreenTemperTatum,
afogel,
bar-capsule,
fewdisc,
mamicidal and
sclintonowasp
as code owners
September 21, 2026 16:14
This comment has been minimized.
This comment has been minimized.
zizmor's artipacked audit flags a checkout that leaves the job token in .git/config. This job pushes the tag with that token and uploads no artifacts, which is the leak the audit guards against, so the finding is suppressed inline with the reason. Signed-off-by: rocklambros <rock@rockcyber.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.
v0.1.2 reached
mainwith #59 on 2026-09-09 and stayed untagged until today, when it was tagged by hand on7743826, the commit that first carried it. Tagging was a manual step with no owner, so nothing noticed the gap.Tag releasemakes it automatic. A push tomainthat changesversion.txtis the release event, and the workflow tags that commitv<version>as an annotated tag, which matches how v0.1.1 and v0.1.2 are shaped. It exits quietly when the tag already exists, so reruns and unrelated pushes are safe. A manual dispatch from any branch other thanmainis skipped, since it would tag a commitmainnever published.It also refuses to tag when
version.txt,pyproject.toml, anduv.lockdisagree. The release version lives in those three files and no other check keeps them in step. A tag is the point where a mismatch becomes permanent, so the run fails with all three values in the error.version.txtis attacker-controlled by anyone with write access and becomes a git ref here, so it passes the same semver checksync_version.ymluses before it reachesgit tag. No${{ }}expression appears insiderun:. The job holdscontents: writeand nothing else, and the only action isactions/checkoutat the SHA every other workflow pins.The workflow lives on
mainonly after the next promotion, so the first release it tags is the next version bump that reachesmainafter that.Tested against a local bare remote with the step's own script, extracted from the YAML. An existing version exits 0 with "already exists". A new version in all three files creates an annotated tag and a rerun is a no-op. A stale
uv.lockfails with the three values. An invalid version fails before touching git.actionlintis clean anduv run pytestgives 278 passed, 1 skipped.The checkout keeps its credentials because the tag push authenticates with them. zizmor's
artipackedaudit flags that, and the finding is suppressed inline with the reason: the job uploads no artifacts, which is the leak the audit exists to catch. With #179 merged, zizmor reports nothing on this workflow.CONTRIBUTING.md's Release Process section now says tagging is automatic.
Signed-off-by: Rock Lambros rock@rockcyber.com