ci(release): publish PEP 740 attestations to PyPI for hf-xet (#848)

## Summary

Switches the `release-pypi` job from `PyO3/maturin-action`'s upload
command to `pypa/gh-action-pypi-publish` with `attestations: true`, so
that every `hf-xet` artifact on PyPI ships with **PEP 740 / Sigstore
provenance**.

Today, `hf-xet 1.5.0` on PyPI shows `attestations: None` and
`https://pypi.org/integrity/hf-xet/1.5.0/<wheel>/provenance` returns
404. The existing `actions/attest-build-provenance` step does the work,
but its output never reaches PyPI: `gh attestation verify` works for
wheels people downloaded from GitHub releases, but `pip download
--verify-attestations` (and the equivalent uv / SLSA verifier flows)
have nothing to chain to.

## What changes

In `.github/workflows/release.yml`, `release-pypi` job:

- Add a step that collects every `wheels-*/<file>` artifact into a
single `dist/` directory. Debug-symbol artifacts (`dbg-*`) are
explicitly excluded from this directory and therefore from the upload.
- Point `actions/attest-build-provenance` at `dist/*` so the GitHub
attestation covers exactly the same bytes that get uploaded.
- Replace `PyO3/maturin-action@... command: upload` with
`pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b`
(v1.14.0, SHA-pinned) and `attestations: true`, `skip-existing: true`,
`print-hash: true`.

## Why two attestation steps?

They are complementary, not redundant:

| Step | Verifier | Where it lives |
|---|---|---|
| `actions/attest-build-provenance` | `gh attestation verify`,
sigstore-go | github.com attestations endpoint |
| `pypa/gh-action-pypi-publish` with `attestations: true` | `pip
download --verify-attestations`, uv, slsa-verifier | PyPI
`/integrity/.../provenance` |

The first one verifies wheels distributed anywhere; the second one is
what PyPI clients actually look at.

## Verification after merge

On the first release that runs this workflow:

```bash
$ pip download --no-deps hf-xet==<new-version>
$ python -c "from pypi_attestations import Distribution; print(Distribution.from_file('hf_xet-*.whl').identity)"
# should print the workflow / environment / repo identity
```

And on PyPI's web UI: each wheel should show a "Provenance" link instead
of "no attestations".

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the release publishing path and artifact selection for PyPI
uploads; mistakes could block releases or upload the wrong artifacts
despite being confined to CI.
> 
> **Overview**
> Switches the `release-pypi` job to **collect all wheel/sdist artifacts
into a single `dist/` directory** (excluding `dbg-*`) and generate the
GitHub build provenance attestation over exactly those files.
> 
> Replaces the PyPI upload step from `PyO3/maturin-action` to
`pypa/gh-action-pypi-publish` using **Trusted Publishing + PEP 740
attestations** (`attestations: true`, `skip-existing: true`,
`print-hash: true`), and clarifies required workflow permissions.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4f0c7f8db9. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Adrien
2026-06-02 08:20:34 +02:00
committed by GitHub
parent 5a75a71727
commit 21aab77be4

View File

@@ -379,24 +379,32 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [github-release]
permissions:
# Use to sign the release artifacts
# Used by both attest-build-provenance and the PyPI Trusted Publisher (OIDC).
id-token: write
# Used to upload release artifacts
# Used to upload release artifacts and to generate artifact attestations.
contents: write
# Used to generate artifact attestation
attestations: write
environment: release
steps:
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
- name: Generate artifact attestation
- name: Collect wheels and sdist into dist/
run: |
set -euo pipefail
mkdir -p dist
# Each `wheels-*` artifact directory holds one or more wheels/sdists.
# Debug-symbol artifacts (`dbg-*`) must NOT be uploaded to PyPI.
cp wheels-*/* dist/
ls -l dist
- name: Generate GitHub build provenance attestation
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
with:
subject-path: 'wheels-*/*'
- name: Publish to PyPI
uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1
subject-path: 'dist/*'
- name: Publish to PyPI (Trusted Publishing + PEP 740 attestations)
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
with:
command: upload
args: --non-interactive --skip-existing wheels-*/*
attestations: true
skip-existing: true
print-hash: true
github-release:
name: Create GitHub release