Skip to content

Annotate the release tag before moving v1 - #12

Merged
MattFisher merged 2 commits into
mainfrom
claude/pyright-migration-implications-rt3usr
Aug 7, 2026
Merged

MattFisher merged 2 commits into
mainfrom
claude/pyright-migration-implications-rt3usr

Conversation

@MattFisher

@MattFisher MattFisher commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Overview

Fixes the copier update downgrade failure, which is worse than it first looked: publishing 1.8.0 moved v1 onto the same commit as v1.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.commitgit describe --tags --always, then parses it with dunamai (copier/_template.py). git describe ranks candidate tags on the same commit by annotation first, then recency. bump-v1.yml creates v1 through 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, and v1 wins the tiebreak.

So describe answers v1, dunamai reads version 1, and copier refuses any project above 1.0.0:

You are downgrading from 1.7.0 to 1. Downgrades are not supported.

Reproduced against a real consumer checkout. Critically, this is not just the --vcs-ref v1 default the scheduled workflow passes — an explicit --vcs-ref v1.8.0 resolves 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 ran copier update in that window.

The invariant

Measured on a clone of this repo, all three combinations on one commit:

release tag v1 git describe --tags
lightweight lightweight v1
annotated lightweight v1.8.0
annotated annotated v1

Both-annotated breaks again because v1 is re-tagged on every release and is therefore always the newer of the two. So the rule is precise: release tag annotated, v1 lightweight. The Move step already guarantees the second half by construction.

What changed

  • bump-v1.yml: an Annotate the release tag step 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

  • End-to-end against a local clone with the release tag annotated and v1 lightweight on the same commit: git describe --tagsv1.8.0, and copier update --defaults --trust --vcs-ref v1 in a real consumer checkout reports Updating to template version 1.8.0, writes _commit: v1.8.0, and applies the basedpyright migration.
  • git cat-file -t confirms the two states the step discriminates on: lightweight → commit, annotated → tag.
  • Ran bump-v1.yml's changelog gate against the edited file for v1.8.1: PASS.
  • Workflow parses as YAML; step order is Refuse to move v1 off mainRequire a changelog entryAnnotate the release tagMove v1. The job already holds contents: write.

Releasing this

Publish v1.8.1 however 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.0 is left as-is rather than force-pushed over.

claude added 2 commits August 7, 2026 04:27
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.
@MattFisher MattFisher changed the title Refuse to move v1 onto a lightweight release tag Annotate the release tag before moving v1 Aug 7, 2026
@MattFisher
MattFisher merged commit ba338ef into main Aug 7, 2026
3 checks passed
@MattFisher
MattFisher deleted the claude/pyright-migration-implications-rt3usr branch August 7, 2026 05:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants