Skip to content

Publish to PyPI from CI on GitHub release (ADR-0007) - #11

Merged
fidecastro merged 1 commit into
mainfrom
ci/publish-on-release
Nov 5, 2026
Merged

fidecastro merged 1 commit into
mainfrom
ci/publish-on-release

Conversation

@fidecastro

Copy link
Copy Markdown
Owner

Publishing a GitHub release now publishes it to PyPI. There's no token anywhere: the upload uses PyPI trusted publishing from the GitHub environment pypi, which is already created and limited to v* tags.

What publish.yml does

  • build (contents: read): checks out the release tag, fails unless the tag is v + the version in pyproject.toml, runs uv build and twine check --strict, installs the wheel with uv tool install, and checks jevify --version.
    • If the release already has a wheel and sdist attached, the CI build must be byte-identical to them or nothing is uploaded.
    • If the release has none, the build is marked for attaching.
  • publish (id-token: write, environment pypi): runs pypa/gh-action-pypi-publish, which also uploads attestations by default.
  • attach (contents: write): runs only when the release had no files. It uploads the CI build plus a SHA256SUMS to the release.
  • Dry run: workflow_dispatch with tag runs the build job only and never publishes.

ADR-0007 records the decision and has a row in the ADR index.

Before the next release

On PyPI, add a trusted publisher under jevify → Settings → Publishing with these values:

  • owner fidecastro
  • repository jevify
  • workflow publish.yml
  • environment pypi

Without it, the publish job fails.

Checks

  • actionlint is clean.
  • The guard, build, twine, smoke and compare steps were run locally against v0.2.0. A fresh clone's build is byte-identical to the files attached to the 0.2.0 release.
  • After merge: gh workflow run publish.yml -f tag=v0.2.0 should pass the compare and skip publishing.

🤖 Generated with Claude Code

publish.yml builds the release tag with uv build, refuses a tag that is not
v + the pyproject version, runs twine check --strict and a tool-install smoke
test, then uploads by PyPI trusted publishing from the `pypi` environment; no
token is stored. If the release already has a wheel and sdist attached, the
CI build must be byte-identical to them or nothing is uploaded; if it has
none, the CI build is attached with a SHA256SUMS after publishing.
workflow_dispatch with a tag is a dry run that never publishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fidecastro
fidecastro merged commit 1ab8988 into main Nov 5, 2026
5 of 6 checks passed
@fidecastro fidecastro mentioned this pull request Nov 6, 2026
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