The release is driven by .github/workflows/publish.yml, which runs on every
v* tag push. The workflow supports two independent authentication paths
and picks one at runtime based on whether the PYPI_API_TOKEN repository
secret is set:
| Path | Trigger | Requires operator action on PyPI? | Requires repo secret? |
|---|---|---|---|
| A. API token | secrets.PYPI_API_TOKEN is set |
No (after the project exists) | Yes |
| B. Trusted Publishing (OIDC) | secrets.PYPI_API_TOKEN is empty |
Yes (one-time form on pypi.org) | No |
The workflow emits a step-summary line announcing the selected path so a maintainer can see, after the fact, which one ran.
Use this if you want the next tag to succeed without creating a Trusted Publisher on PyPI.
- The PyPI project
benchdeckmust already exist. If it does not, create it once at https://pypi.org/project/benchdeck/ (or viapyproject.toml'sname = "benchdeck"+ an initial manual upload). - Mint a project-scoped API token at
https://pypi.org/manage/account/token/. Scope: "Project: benchdeck",
"Upload packages". Copy the token (
pypi-...) — PyPI shows it only once. - Add it as a repository secret:
- Repo → Settings → Secrets and variables → Actions → New repository secret
- Name:
PYPI_API_TOKEN(must match the workflow literal) - Value: paste the token
- Push the next tag (e.g.
git tag v0.1.3 && git push origin v0.1.3).
The workflow will run the Publish to PyPI (API token) step and exit 0.
Use this long term. The workflow proves its identity to PyPI via a short-lived OIDC token; no long-lived secret is stored in GitHub.
-
Add a pending publisher (or, for an already-existing project, a publisher for that project) with exactly these fields:
Field Value Owner MerverliPyRepository BenchDeckWorkflow filename publish.ymlEnvironment name pypiThe environment name must match the
environment: pypiblock inpublish.yml:14-16. The workflow filename must match exactly (lowercase, no path prefix). -
(Optional but recommended) Set a release manager for the project so trusted-publisher additions still require human approval: https://docs.pypi.org/trusted-publishers/adding-a-publisher/#_3-optional-set-the-project-to-require-manual-approval.
For debugging, GitHub includes the rendered claims in the action's error
output. The values below are an illustrative snapshot captured from a
real run (v0.1.2); the only fields that need to match exactly on PyPI
are owner, repository, the workflow filename, and the environment name.
The numeric IDs and the tag-specific ref fields will differ on each run
and across repos.
sub:repo:MerverliPy/BenchDeck:environment:pypirepository:MerverliPy/BenchDeckrepository_owner:MerverliPyrepository_owner_id:194641652workflow_ref:MerverliPy/BenchDeck/.github/workflows/publish.yml@refs/tags/v0.1.2job_workflow_ref:MerverliPy/BenchDeck/.github/workflows/publish.yml@refs/tags/v0.1.2ref:refs/tags/v0.1.2environment:pypi
If a tag push fails with invalid-publisher: Publisher with matching claims was not found, the most common causes are:
- The form field "Workflow filename" is
Publish to PyPI(the workflow's display name) instead ofpublish.yml(the file name). - The form field "Environment name" is empty or
Pypi(capital P); it must be exactlypypi. - The PyPI project does not exist yet; Trusted Publishing requires an already-registered project. Create the project (Path A step 1) first, then add the publisher.
You can confirm the build will succeed without a tag push:
python -m pip install --upgrade build twine
python -m build
twine check dist/*Expected: dist/benchdeck-<version>-py3-none-any.whl and
dist/benchdeck-<version>.tar.gz are produced and twine check reports
PASSED for both.
Once a version tag is pushed and the package is published to PyPI, the version
must never be deleted, re-tagged, or re-published. If a publish step fails,
fix the workflow and bump the version in pyproject.toml (following the
NEXT_VERSION gate comment at the top of that file). Do not delete the tag
and re-push.
A unified build workflow (.github/workflows/_build.yml) builds the wheel and
sdist exactly once, runs the full test suite, and uploads the artifacts as an
immutable workflow artifact. The publish.yml and release.yml workflows
consume this single artifact — no rebuilding occurs per destination. See each
workflow file for the exact steps.
The two paths are independent. Setting PYPI_API_TOKEN does not invalidate
the Trusted Publisher; clearing the secret re-enables the OIDC path. You can
run one tag with the token and the next with OIDC without any other change.