diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 81301649..40f5adb8 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -88,6 +88,18 @@ jobs: manylinux: auto sccache: true + # `twine check --strict` validates the wheel's metadata against + # the same rules PyPI's upload validator applies (README + # rendering, License-File presence, Description-Content-Type, + # etc.). Running it on every PR catches packaging-metadata + # regressions at PR time instead of at the next `release: + # published` event. + - name: Validate wheel metadata + shell: bash + run: | + python -m pip install --quiet twine + twine check --strict target/wheels/*.whl + # Only upload artifacts when the caller actually needs them # (release.yml downloads them in its publish job). PR smoke # builds just need the build to succeed — keeping the @@ -103,16 +115,18 @@ jobs: name: build sdist timeout-minutes: 10 runs-on: ubuntu-24.04 - # The sdist is only useful for the release publish step. PR - # smoke runs would just throw it away — skip the whole job. - if: inputs.stamp-version steps: - uses: actions/checkout@v6.0.2 with: fetch-depth: 0 fetch-tags: true + - uses: actions/setup-python@v6.2.0 + with: + python-version: 3.14 + - name: Stamp wheel version from git tag + if: inputs.stamp-version shell: bash run: | set -eu @@ -125,7 +139,18 @@ jobs: command: sdist args: --out target/wheels + # See `Validate wheel metadata` above — twine's strict check + # exercises the metadata rules PyPI applies. Running it on PR + # would have caught the missing `License-File LICENSE` + # bundling that broke release 2026.5.5.1. + - name: Validate sdist metadata + shell: bash + run: | + python -m pip install --quiet twine + twine check --strict target/wheels/*.tar.gz + - uses: actions/upload-artifact@v7 + if: inputs.stamp-version with: name: wheel-sdist path: target/wheels/*.tar.gz diff --git a/pyproject.toml b/pyproject.toml index 4807f654..f96ce860 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,6 +81,13 @@ manifest-path = "crates/mergify-cli/Cargo.toml" python-source = "." module-name = "mergify_cli" strip = true +# Bundle LICENSE in both the wheel and sdist. PEP 639 metadata +# (auto-derived by maturin from `license = "Apache-2.0"`) emits a +# `License-File: LICENSE` field, and PyPI's upload validator rejects +# the artifact with `400 License-File LICENSE does not exist in +# distribution file` if the file isn't actually bundled. Listing it +# here keeps wheel + sdist in sync with the metadata. +include = ["LICENSE"] [tool.pytest.ini_options] asyncio_mode = "auto"