Skip to content

Publish to PyPI on a tag, via Trusted Publishing#9

Merged
Nik7A merged 1 commit into
mainfrom
release-workflow
Jul 16, 2026
Merged

Publish to PyPI on a tag, via Trusted Publishing#9
Nik7A merged 1 commit into
mainfrom
release-workflow

Conversation

@Nik7A

@Nik7A Nik7A commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Publishing needs no API token at all. PyPI verifies over OIDC that the upload came from this repo and this workflow, and mints a short-lived credential for that one request — nothing to store in a secret, nothing to sit in ~/.pypirc in plaintext, nothing to rotate or leak. It also means PyPI attaches PEP 740 attestations by default, so the package carries evidence of where it was built. For this library in particular, shipping without that would be odd.

Two guards, because a tag can point at any commit:

  • The tag must match version in pyproject.toml. A tag saying v0.2.0 over a tree saying 0.3.0 would publish a version nobody can trace back to a commit — it fails instead of guessing.
  • Tests, mypy and ruff re-run in the release job. ci.yml covers main and PRs, but a tag can point at a commit neither ever saw, so the gate is repeated rather than assumed.

Build and publish are split so the job holding id-token: write does nothing but download an artifact and upload it. It has no other permission.

One-time setup on PyPI, needed before the first release: Publishing → add a pending publisher for owner Nik7A, repo chiplog, workflow release.yml, environment pypi. After the first upload it stops being pending and becomes the project's publisher.

Then releasing is: bump version, land it, git tag -s vX.Y.Z -m "..." && git push origin vX.Y.Z.

Note that v0.2.0 is already tagged and pushed, from before this workflow existed. Once this lands, that tag can be deleted and re-pushed to trigger a release, or 0.2.1 can be the first tag that publishes itself.

No API token — not in a secret, not in a file, not in ~/.pypirc. PyPI verifies
over OIDC that the upload came from this repo and this workflow, and mints a
short-lived credential for that one request. Nothing to store, leak or rotate.
It also means the package gets PEP 740 attestations by default, so it carries
evidence of where it was built, which this library has no business shipping
without.

Two guards, because a tag can point at anything:

- the tag must match `version` in pyproject. A tag saying v0.2.0 over a tree
  saying 0.3.0 would publish a version nobody can trace to a commit.
- tests, mypy and ruff re-run here. ci.yml runs on main and on PRs, but a tag
  can point at a commit neither ever saw, so the gate is repeated rather than
  assumed.

Split into build and publish jobs so the job holding `id-token: write` does
nothing but download an artifact and upload it. It has no other permission.

One-time setup on PyPI, before the first release: Publishing -> add a pending
publisher for owner Nik7A, repo chiplog, workflow release.yml, environment pypi.

Releasing is then: bump version, land it, `git tag -s vX.Y.Z && git push --tags`.
@Nik7A
Nik7A merged commit bb06f46 into main Jul 16, 2026
1 check passed
@Nik7A
Nik7A deleted the release-workflow branch July 16, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant