diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 826a0cf..2002844 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,12 @@ on: push: tags: - 'v[0-9]*' + workflow_dispatch: + inputs: + tag: + description: 'Existing tag to create or update a GitHub Release for (e.g. v0.2.2)' + required: true + type: string permissions: contents: write @@ -12,8 +18,23 @@ jobs: release: name: Create GitHub Release runs-on: ubuntu-latest + env: + RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }} steps: + - name: Validate release tag + if: github.event_name == 'workflow_dispatch' + run: | + set -euo pipefail + tag="${{ github.event.inputs.tag }}" + if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "tag must look like vX.Y.Z, got: $tag" >&2 + exit 1 + fi + git ls-remote --exit-code origin "refs/tags/${tag}" >/dev/null + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ env.RELEASE_TAG }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: @@ -23,7 +44,7 @@ jobs: id: meta run: | pkg_name=$(python -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['name'])") - pkg_version="${GITHUB_REF_NAME#v}" + pkg_version="${RELEASE_TAG#v}" echo "name=${pkg_name}" >> "$GITHUB_OUTPUT" echo "version=${pkg_version}" >> "$GITHUB_OUTPUT" @@ -31,7 +52,7 @@ jobs: id: notes run: | set -euo pipefail - version="${GITHUB_REF_NAME#v}" + version="${RELEASE_TAG#v}" if [[ -f CHANGELOG.md ]]; then body="$(python scripts/extract-changelog.py "$version")" else @@ -47,7 +68,7 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: - tag_name: ${{ github.ref_name }} + tag_name: ${{ env.RELEASE_TAG }} name: ${{ steps.meta.outputs.name }} ${{ steps.meta.outputs.version }} body: ${{ steps.notes.outputs.body }} generate_release_notes: false diff --git a/RELEASING.md b/RELEASING.md index 0b50ff4..0be337f 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -34,6 +34,18 @@ Pushing a `vX.Y.Z` tag triggers two workflows: | `publish.yml` | Build wheel/sdist and publish to PyPI | | `release.yml` | Create the GitHub Release with notes from `CHANGELOG.md` | +## Recover a missing GitHub Release + +If PyPI publish succeeded but the GitHub Release workflow failed, rerun it from `main` +without retagging: + +```bash +gh workflow run "GitHub Release" --ref main -f tag=vX.Y.Z +``` + +The tag must already exist on the remote. The workflow checks out that tag, extracts the +matching `CHANGELOG.md` section, and creates or updates the GitHub Release. + ## Enforcement - **PR check** (`check-release.yml`): if `pyproject.toml` version changes, `CHANGELOG.md` must contain a matching `## [X.Y.Z]` section.