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 }} 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 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