Skip to content

Tag each release automatically when its version reaches main - #176

Merged
rocklambros merged 2 commits into
integrationfrom
ci/tag-release
Sep 22, 2026
Merged

rocklambros merged 2 commits into
integrationfrom
ci/tag-release

Conversation

@rocklambros

@rocklambros rocklambros commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

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.

The checkout keeps its credentials because the tag push authenticates with them. zizmor's artipacked audit 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

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>
@github-actions

This comment has been minimized.

@rocklambros rocklambros removed the status:needs-triage Not yet triaged. Applied by the issue forms label Sep 21, 2026
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>
@rocklambros
rocklambros merged commit 25ae2b1 into integration Sep 22, 2026
8 checks passed
@rocklambros
rocklambros deleted the ci/tag-release branch September 22, 2026 03:21
@github-project-automation github-project-automation Bot moved this from Needs triage to Done in ACS Project Tracker Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant