From f31efeca1c6a96d2278cdf64c5021637e97b1c63 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 7 Oct 2026 18:20:52 +1100 Subject: [PATCH 1/3] fix(bump-v1): annotate the release tag before moving v1 With v1 and the release tag both lightweight on one commit, dunamai picks "v1" and copier reads the template version as 1. Port the annotate step from python-project-template 1.8.1. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/bump-v1.yml | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.github/workflows/bump-v1.yml b/.github/workflows/bump-v1.yml index fe51165..6cc43e2 100644 --- a/.github/workflows/bump-v1.yml +++ b/.github/workflows/bump-v1.yml @@ -106,6 +106,47 @@ jobs: print(f"changelog documents {version}, and [Unreleased] is empty") PY + # Runs after every refusal above, so a release that is going to be turned + # away never has its tag rewritten. + - name: Annotate the release tag + env: + GH_TOKEN: ${{ github.token }} + RELEASE_SHA: ${{ github.sha }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + set -euo pipefail + # The Move step below puts v1 on this same commit, and copier reads a + # template's version through dunamai, which takes the commit's newest + # tag by date. An annotated tag's date is when it was tagged; a + # lightweight tag's is its commit's. With both lightweight they tie on + # date and dunamai breaks the tie by name, which picks "v1". Copier + # then parses that as version 1 and records `_commit: v1` in every + # consumer's .copier-answers.yml, and a consumer above 1.0.0 can fail + # `copier update` with "Downgrades are not supported". + # + # Publishing a release for a tag that does not exist yet -- the GitHub + # UI's default, and `gh release create` without --verify-tag -- makes + # that tag lightweight. Rather than refuse the release over it, give + # the tag an annotation here so it outranks v1 and copier reads + # "v1.2.0". Annotating v1 too would break it again: v1 is re-tagged on + # every release, so it would always be the newer of the two. + # + # 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. + type=$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${RELEASE_TAG}" --jq .object.type) + if [ "$type" = "tag" ]; then + echo "${RELEASE_TAG} is already annotated" + exit 0 + fi + tag_sha=$(gh api -X POST "repos/${GITHUB_REPOSITORY}/git/tags" \ + -f tag="${RELEASE_TAG}" -f message="${RELEASE_TAG}" \ + -f object="${RELEASE_SHA}" -f type=commit --jq .sha) + gh api -X PATCH "repos/${GITHUB_REPOSITORY}/git/refs/tags/${RELEASE_TAG}" \ + -f sha="${tag_sha}" -F force=true + echo "${RELEASE_TAG} annotated as ${tag_sha} (still on ${RELEASE_SHA})" + - name: Move v1 env: GH_TOKEN: ${{ github.token }} From f67864ced52e798ec0d602817054c6c36eb3ed31 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 7 Oct 2026 18:20:52 +1100 Subject: [PATCH 2/3] docs: name the Actions setting template-update needs to open its PR Co-Authored-By: Claude Opus 5.5 --- README.md | 6 ++++++ copier.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/README.md b/README.md index f5aa620..30d6a33 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,12 @@ demand) and opens a PR when the template's *scaffolded files* have changed. Reusable-workflow changes need no update run: consumers pin `@v1`, so moving the tag propagates those immediately. +The workflow opens its PR with the default `GITHUB_TOKEN`, which needs +*Settings → Actions → General →* **Allow GitHub Actions to create and approve +pull requests** turned on. Without it the run pushes `chore/template-update` +and then fails with `GitHub Actions is not permitted to create or approve pull +requests`. + ## Adopt the template in an existing Worker repo `copier update` needs a `.copier-answers.yml` recording which template diff --git a/copier.yml b/copier.yml index 9c70fd7..3d76718 100644 --- a/copier.yml +++ b/copier.yml @@ -49,6 +49,12 @@ _message_after_copy: | Routes Write on the zone) and CLOUDFLARE_ACCOUNT_ID as repository secrets. Editor cannot create a Worker, so do the FIRST deploy by hand: npx wrangler login && npm run deploy + {% if use_template_update %} + + The weekly template-update workflow opens its PR with the default token: + turn on Settings > Actions > General > "Allow GitHub Actions to create and + approve pull requests". + {% endif %} project_name: type: str From 8e33f34de35c64d5831aff2afe90d8f593e436e3 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 7 Oct 2026 18:20:52 +1100 Subject: [PATCH 3/3] chore(changelog): note the tag annotation fix and the Actions setting Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f6f7b..97ebfe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Fixed + +- `bump-v1.yml` annotates the release tag before moving `v1` onto the same + commit. With both tags lightweight, copier read the template's version as + `1`, so the weekly template update rewrote consumers' `_commit` to `v1` + instead of the release they were on. v1.1.0 shipped this way. Ported from + python-project-template 1.8.1. + +### Added + +- The README and the post-copy message name the repository setting the + template-update workflow needs: **Allow GitHub Actions to create and approve + pull requests**. Without it the run pushes its branch and fails to open the + PR. + ## [1.1.0] - 2026-09-22 ### Added