Annotate the release tag before moving v1 - #12
Merged
Merged
Conversation
v1 ends up on the same commit as the release tag it is moved to, and copier reads a template's version with `git describe --tags`. That prefers an annotated tag and otherwise takes the newest, so with both tags lightweight it answered "v1" — copier parsed that as version 1 and refused every consumer's update as a downgrade from 1.x. This stranded projects rather than merely breaking the scheduled runs: an explicit `--vcs-ref v1.8.0` reads the same commit and failed identically, so no working update path existed. Annotating the release tag fixes it, because it outranks the lightweight v1 that the Move step creates. Both annotated would break again, v1 being re-tagged on every release and so always the newer. Verified against a local clone of this repo: with v1.8.0 annotated and v1 lightweight on the same commit, `git describe --tags` answers v1.8.0 and `copier update --vcs-ref v1` reports "Updating to template version 1.8.0", writing `_commit: v1.8.0`. Cutting 1.8.1 from an annotated tag is also what repairs 1.8.0, without force-pushing over a published ref.
v1 ends up on the same commit as the release tag it is moved to, and copier reads a template's version with `git describe --tags`. That prefers an annotated tag and otherwise takes the newest, so with both tags lightweight it answered "v1" — copier parsed that as version 1 and refused every consumer's update as a downgrade from 1.x. This stranded projects rather than merely breaking the scheduled runs: an explicit `--vcs-ref v1.8.0` reads the same commit and failed identically, so no working update path existed. Wrong since 1.6.0, the first release whose tag was created by publishing. Publishing a release for a tag that does not yet exist is what makes it lightweight, so annotate the tag here instead of demanding the release be cut differently. Releases can still be published from the UI. The rewrite keeps the same target commit and the release references the tag by name, so the published release is unaffected, and it lands seconds after publication. Annotating v1 as well would reintroduce the bug, since v1 is re-tagged every release and would always be the newer. Placed after the existing refusals so a release that is going to be turned away never has its tag rewritten. Verified against a local clone: with the release tag annotated and v1 lightweight on one commit, `git describe --tags` answers the release tag and `copier update --vcs-ref v1` reports "Updating to template version 1.8.0" in a real consumer checkout.
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.
Overview
Fixes the
copier updatedowngrade failure, which is worse than it first looked: publishing 1.8.0 movedv1onto the same commit asv1.8.0, and every consumer update path is now blocked, not just the scheduled ones.Root cause
Copier reads a template's version via
Template.commit→git describe --tags --always, then parses it with dunamai (copier/_template.py).git describeranks candidate tags on the same commit by annotation first, then recency.bump-v1.ymlcreatesv1through the refs API, which makes it lightweight — and publishing a release for a tag that doesn't exist yet (the GitHub UI's default) creates a lightweight tag too. Two lightweight tags tie on annotation, andv1wins the tiebreak.So
describeanswersv1, dunamai reads version1, and copier refuses any project above 1.0.0:Reproduced against a real consumer checkout. Critically, this is not just the
--vcs-ref v1default the scheduled workflow passes — an explicit--vcs-ref v1.8.0resolves the same commit and fails identically. There is currently no working update path for any of the four consumers. Copier exits 1, so the scheduled runs fail loudly rather than silently no-op'ing.This has been broken since 1.6.0, the first release whose tag was created by publishing rather than pushed with
git tag -a. Tags v1.0.0 → v1.5.2 are all annotated; v1.6.0, v1.7.0 and v1.8.0 are not. It went unnoticed because nothing rancopier updatein that window.The invariant
Measured on a clone of this repo, all three combinations on one commit:
v1git describe --tagsv1❌v1.8.0✅v1❌Both-annotated breaks again because
v1is re-tagged on every release and is therefore always the newer of the two. So the rule is precise: release tag annotated,v1lightweight. The Move step already guarantees the second half by construction.What changed
bump-v1.yml: anAnnotate the release tagstep that creates a tag object over the released commit and force-updates the ref, when publishing left the tag lightweight. Placed after the existing refusals, so a release that is going to be turned away never has its tag rewritten. Idempotent — it exits early if the tag is already annotated.CHANGELOG.md: the invariant documented alongside the other two version-mechanics notes at the top, plus a[1.8.1]section.Annotating rather than refusing keeps releases publishable straight from the GitHub UI. The rewrite keeps the same target commit, and the release references the tag by name, so the published release is unaffected; it lands seconds after publication, before the tag has realistically been fetched anywhere.
Verification
v1lightweight on the same commit:git describe --tags→v1.8.0, andcopier update --defaults --trust --vcs-ref v1in a real consumer checkout reportsUpdating to template version 1.8.0, writes_commit: v1.8.0, and applies the basedpyright migration.git cat-file -tconfirms the two states the step discriminates on: lightweight →commit, annotated →tag.bump-v1.yml's changelog gate against the edited file forv1.8.1:PASS.Refuse to move v1 off main→Require a changelog entry→Annotate the release tag→Move v1. The job already holdscontents: write.Releasing this
Publish
v1.8.1however you like, including straight from the UI — the workflow now repairs the tag itself. That release is what unblocks the four consumers: they land on 1.8.1 rather than 1.8.0, identical bar this fix.v1.8.0is left as-is rather than force-pushed over.