From 47b8d1d72ef0b4b7f8a55a44c69f990f3bfdf4af Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 18 Sep 2026 12:20:26 +0100 Subject: [PATCH 1/3] Bump GitHub Actions to current major versions checkout v4 -> v7, setup-python v5 -> v7, upload-artifact v4 -> v7, download-artifact v4 -> v8. The intervening majors are Node 24 and ESM runtime updates; none of the inputs used here changed. --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7e2a16..834f884 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,11 +37,11 @@ jobs: - os: windows-latest arch: x86 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: submodules: recursive - name: Set up Python 3.x - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: "3.x" - name: Install dependencies @@ -50,7 +50,7 @@ jobs: run: python tests/download_test_data.py - name: Build and Test Wheels run: python -m cibuildwheel --output-dir wheelhouse - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: unicodedata2-${{ matrix.os }}-${{ matrix.arch }} path: wheelhouse/*.whl @@ -68,11 +68,11 @@ jobs: id-token: write # IMPORTANT: mandatory for trusted publishing contents: write # Needed to create GH release steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: submodules: recursive - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: "3.x" - name: Install dependencies @@ -80,7 +80,7 @@ jobs: python -m pip install --upgrade pip pip install --upgrade build twine - name: Download artifacts from build jobs - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: wheelhouse/ - name: Move wheels to dist/ directory From 734925c1c173fbc64a80f9b4b497380bcf65a06b Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 18 Sep 2026 12:21:28 +0100 Subject: [PATCH 2/3] Replace archived actions/create-release with the gh CLI actions/create-release was archived in 2021 and still declares a node12 runtime, so it is the most likely thing to break the next release. gh is preinstalled on the runners and needs no third-party action. Also let download-artifact put the wheels straight into dist/ via merge-multiple instead of moving them out of per-artifact directories, build the sdist before creating the release, and drop twine, which nothing has used since the switch to trusted publishing. --- .github/workflows/ci.yml | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 834f884..c503aca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,18 +78,14 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install --upgrade build twine - - name: Download artifacts from build jobs + pip install --upgrade build + - name: Download wheels from build jobs uses: actions/download-artifact@v8 with: - path: wheelhouse/ - - name: Move wheels to dist/ directory - run: | - ls wheelhouse/* - mkdir -p dist/ - for wheel_dir in wheelhouse/unicodedata2*/; do - mv "${wheel_dir}"/*.whl dist/ - done + merge-multiple: true + path: dist/ + - name: Build sdist + run: python -m build --sdist - name: Extract release notes from annotated tag message id: release_notes run: | @@ -99,21 +95,18 @@ jobs: # strip leading 'refs/tags/' to get the tag name TAG_NAME="${GITHUB_REF##*/}" # Dump tag message to temporary .md file (excluding the PGP signature at the bottom) - TAG_MESSAGE=$(git tag -l --format='%(contents)' $TAG_NAME | sed -n '/-----BEGIN PGP SIGNATURE-----/q;p') - echo "$TAG_MESSAGE" > "${{ runner.temp }}/release_notes.md" + git tag -l --format='%(contents)' "$TAG_NAME" \ + | sed -n '/-----BEGIN PGP SIGNATURE-----/q;p' \ + > "${{ runner.temp }}/release_notes.md" + echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" - name: Create GitHub release - id: create_release - uses: actions/create-release@v1 env: # This token is provided by Actions, you do not need to create your own token - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - body_path: "${{ runner.temp }}/release_notes.md" - draft: false - prerelease: false - - name: Build sdist - run: python -m build --sdist + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: ${{ steps.release_notes.outputs.tag_name }} + run: | + ls -l dist/ + gh release create "$TAG_NAME" --title "$TAG_NAME" \ + --notes-file "${{ runner.temp }}/release_notes.md" - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 From 0cc1570cdf1bb1a71d58e3c6898d5b6af97158c6 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 18 Sep 2026 12:24:49 +0100 Subject: [PATCH 3/3] Refuse to publish when the tag and the package version disagree The version comes from pyproject.toml, not from the tag, so tagging 18.0.0rc1 on a tree that still says 18.0.0 would publish 18.0.0 to PyPI under the wrong release notes, and PyPI filenames cannot be reused. Fail the deploy job before anything is uploaded instead. Take the tag name from github.ref_name rather than re-deriving it from GITHUB_REF, and mark PEP 440 pre-release tags as prereleases on GitHub. --- .github/workflows/ci.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c503aca..6147bfe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,15 @@ jobs: run: | python -m pip install --upgrade pip pip install --upgrade build + - name: Check the tag matches the package version + env: + TAG_NAME: ${{ github.ref_name }} + run: | + VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + if [ "$TAG_NAME" != "$VERSION" ]; then + echo "::error::tag '$TAG_NAME' does not match project version '$VERSION'" + exit 1 + fi - name: Download wheels from build jobs uses: actions/download-artifact@v8 with: @@ -87,26 +96,28 @@ jobs: - name: Build sdist run: python -m build --sdist - name: Extract release notes from annotated tag message - id: release_notes + env: + TAG_NAME: ${{ github.ref_name }} run: | # GH checkout action doesn't preserve tag annotations, we must fetch them # https://github.com/actions/checkout/issues/290 git fetch --tags --force - # strip leading 'refs/tags/' to get the tag name - TAG_NAME="${GITHUB_REF##*/}" - # Dump tag message to temporary .md file (excluding the PGP signature at the bottom) + # Dump tag message to a temporary .md file, minus any PGP signature git tag -l --format='%(contents)' "$TAG_NAME" \ | sed -n '/-----BEGIN PGP SIGNATURE-----/q;p' \ > "${{ runner.temp }}/release_notes.md" - echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" - name: Create GitHub release env: # This token is provided by Actions, you do not need to create your own token GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: ${{ steps.release_notes.outputs.tag_name }} + TAG_NAME: ${{ github.ref_name }} run: | ls -l dist/ - gh release create "$TAG_NAME" --title "$TAG_NAME" \ + case "$TAG_NAME" in + *[ab][0-9]*|*rc[0-9]*|*dev[0-9]*) PRERELEASE=--prerelease ;; + *) PRERELEASE= ;; + esac + gh release create "$TAG_NAME" --title "$TAG_NAME" $PRERELEASE \ --notes-file "${{ runner.temp }}/release_notes.md" - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1