Skip to content

feat(ci): PyPI publish workflow on tag via OIDC Trusted Publisher#21

Merged
cipher813 merged 1 commit into
mainfrom
feat/pypi-release-cd
May 13, 2026
Merged

feat(ci): PyPI publish workflow on tag via OIDC Trusted Publisher#21
cipher813 merged 1 commit into
mainfrom
feat/pypi-release-cd

Conversation

@cipher813
Copy link
Copy Markdown
Owner

Summary

Adds .github/workflows/release.yml so future PyPI releases happen automatically on tag push instead of needing manual twine upload.

Trigger: push of a tag matching v* (e.g. v0.5.0rc3, v0.5.0, v0.6.0). Manual workflow_dispatch with a ref input is included as a re-publish fallback.

Pipeline:

  1. build — checkout → setup Python 3.12 → install build + twinepython -m buildtwine check → verify tag version matches built wheel version (catches a forgotten __init__.py / pyproject.toml bump) → upload artifact.
  2. publish — download artifact → pypa/gh-action-pypi-publish@release/v1 with id-token: write. OIDC token exchange handles auth; no API token in repo secrets.

The publish job runs under a pypi GitHub environment so a manual approval gate can be added later (Settings → Environments → pypi → Required reviewers) without changing the workflow.

One-time PyPI-side setup (required before the workflow can publish)

  1. Visit https://pypi.org/manage/project/flow-doctor/settings/publishing/
  2. Add a new pending publisher:
    • PyPI Project Name: flow-doctor
    • Owner: cipher813
    • Repository name: flow-doctor
    • Workflow filename: release.yml
    • Environment name: pypi

After that, the release flow is just:

# bump version in __init__.py + pyproject.toml + CHANGELOG, commit, merge PR
git checkout main && git pull
git tag -a v0.5.0rc3 -m "..."
git push origin v0.5.0rc3
# → release.yml fires → wheel + sdist on PyPI

Test plan

  • YAML syntax + structure valid (commit + push succeeded; GitHub Actions parser will catch real errors on first dispatch).
  • First end-to-end exercise: next rc bump after merge.
  • Sanity: tag-vs-version mismatch path — push a tag that disagrees with __init__.py; the build job should fail loudly.

🤖 Generated with Claude Code

Fires on tag push matching v* (e.g. v0.5.0rc3, v0.5.0, v0.6.0). Two
jobs: build (sdist + wheel + twine check + tag-vs-version sanity
check) and publish (downloads the artifact + uploads via
pypa/gh-action-pypi-publish using PyPI's OIDC Trusted Publisher).

No API token lives in repo secrets — the action exchanges the
short-lived GitHub OIDC token for PyPI publish creds, scoped to
this exact workflow on this exact repo. Token rotation hygiene
becomes automatic.

`workflow_dispatch` with a `ref` input is included as a manual
re-publish fallback for cases where the tag-push event was missed
or the artifact needs to be rebuilt from a known ref.

The build job verifies that the tag (refs/tags/vX.Y.Z) matches the
built wheel's version metadata before handoff to publish, so a
forgotten flow_doctor/__init__.py / pyproject.toml bump fails the
release loud at the tag push instead of shipping a stale version.

The publish job runs under a `pypi` GitHub environment so an optional
manual approval gate can be added later (Settings → Environments →
pypi → Required reviewers) without changing the workflow.

One-time PyPI-side setup required before this workflow can publish:
  1. https://pypi.org/manage/project/flow-doctor/settings/publishing/
  2. Add a new pending publisher:
     - PyPI Project Name: flow-doctor
     - Owner: cipher813
     - Repository name: flow-doctor
     - Workflow filename: release.yml
     - Environment name: pypi

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cipher813 cipher813 merged commit 6f05409 into main May 13, 2026
1 check passed
@cipher813 cipher813 deleted the feat/pypi-release-cd branch May 13, 2026 20:52
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