Skip to content

Publish to PyPI on v* tag push, not manual dispatch - #6

Merged
cbascom merged 1 commit into
mainfrom
chore/tag-based-publish
Oct 13, 2026
Merged

cbascom merged 1 commit into
mainfrom
chore/tag-based-publish

Conversation

@cbascom

@cbascom cbascom commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Moves PyPI releases to the tag-triggered model used by LuxorLabs/jardinero, so publishing is gated by who can push a v* tag rather than by an environment's required reviewers (which cap at 6).

Behavior

  • v* tag push → publishes. The only path that reaches PyPI.
  • workflow_dispatch → dry run. Builds, runs pytest, and twine checks the package, but never publishes. Runs on any branch.
  • A tag push must match the pyproject.toml version (v1.4.2 ⇒ 1.4.2) or it fails before publishing. The existing "already on PyPI" idempotency check is kept.

Why the job split

The job is split into build (no environment, runs on every trigger) and publish (if: push, bound to the pypi-publish environment / OIDC trusted publisher). An environment applies to a whole job, so a single-job version couldn't both restrict the environment to v* tags and let dry runs dispatch on a branch. Split, the dry run is environment-free and the publish is the only environment-bound job. No new actions or SHAs.

Settings to change after merge

  1. pypi-publish environment — remove required reviewers; set Deployment branches and tags to Selected → v*.
  2. New tag ruleset — Target Tags, pattern v*, rule Restrict creations, with the release managers (or a team) on the bypass list. That bypass list is who can publish, and it isn't limited to 6.

OIDC trusted publisher needs no change — it keys on the workflow filename + environment, both unchanged.

🤖 Generated with Claude Code

@tenki-reviewer

tenki-reviewer Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review complete. 🟡 1 medium

💬 Inline comments (1)


The change reworks .github/workflows/pypi-publish.yml so that publishing to PyPI is separated from building and gated on tag push events rather than running inline.

Files Change
.github/workflows/pypi-publish.yml Adds a dedicated publish job (tag-push only, id-token: write, pypi-publish environment) that re-checks out, rebuilds, and uploads via PyPI Trusted Publishing, while the build job retains the tag/version consistency check and twine check.

Reviewed commit: 21ddaff

@tenki-reviewer tenki-reviewer Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand to see previous review

This PR refactors the PyPI publish workflow to gate publishing on tag pushes only, with a rebuilt artifact in a separate publish job.

Key findings

Comment thread .github/workflows/pypi-publish.yml Outdated
@cbascom

cbascom commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

@tenki-reviewer please re-review.

Since the last review:

  • Addressed the medium finding — the publish job no longer rebuilds. The build job uploads the tested, twine checked dist/ as an artifact (and exposes the already-published check as a job output); the publish job, the only one with id-token: write and the pypi-publish environment, now runs only download-artifact + publish — no checkout, pip install, or build. So the published bytes are the tested bytes and no freshly resolved code runs before the trusted-publisher step. Artifact actions pinned to SHAs.
  • Added docs/release-process.md documenting the tag-triggered release flow, the dry-run path, and the tag-ruleset / trusted-publishing gating.

@tenki-reviewer

tenki-reviewer Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review complete. No blocking issues — approved ✅; 1 nitpick below.

🧹 Nitpicks (1) — 🟢 1 low
  • 🟢 Test workflow table omits workflow_dispatch trigger (release-process.md:73) — The newly added workflows table lists the Test workflow's triggers as "every PR; push to test paths", but .github/workflows/test.yml also declares workflow_dispatch (test.yml:14).

The change introduces an OIDC-based PyPI publish pipeline and documents the release flow. The build job builds and validates distributions with twine check and reports whether the version is already published; the publish job runs only on v* tags when the version is unpublished, using a pypa/gh-action-pypi-publish action with id-token: write and an environment for Trusted Publishing.

Files Change
.github/workflows/pypi-publish.yml New tag-triggered workflow that builds dists, checks them with twine, and publishes to PyPI via OIDC Trusted Publishing on v* tags only.
docs/release-process.md New release-process guide covering versioning, the tag ruleset, the workflow's build/publish jobs, and a mermaid diagram of the flow.

Reviewed commit: e055d21

tenki-reviewer[bot]
tenki-reviewer Bot previously approved these changes Oct 13, 2026
Move releases to the jardinero model: pushing a v* tag is the only path
that publishes, and workflow_dispatch becomes a dry run that builds,
tests, and twine-checks without publishing. Who can release is then
exactly who can push a v* tag, which a tag ruleset gates for more than
the six approvers an environment's required-reviewer rule allows. A tag
push must also match the pyproject version before it publishes.

The workflow is two jobs. build tests, builds, and twine-checks the
package and uploads dist/ as an artifact; publish runs only on a tag
push and is the one job bound to the pypi-publish environment / OIDC
trusted publisher. It downloads that exact artifact and publishes it,
running no checkout, pip install, or build, so no freshly resolved code
executes in the job holding id-token: write and the published bytes are
the bytes that were tested.

Also document the flow in docs/release-process.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cbascom

cbascom commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

@tenki-reviewer please re-review — squashed to a single commit, which dismissed the prior approval.

Changes since approval: squashed the three commits into one (no content change from the approved diff) and fixed the nit — docs/release-process.md now lists manual dispatch as a Test workflow trigger.

@tenki-reviewer

tenki-reviewer Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review complete. No issues found — approved ✅.


This PR introduces a GitHub Actions workflow that separates building wheels/sdist from publishing them via PyPI trusted publishing, and documents the release process to match.

Files Change
.github/workflows/pypi-publish.yml New CI workflow: build job produces dist/ artifacts, tag/version consistency gate, publish job gated on v* tags with OIDC id-token and SHA-pinned actions
docs/release-process.md New documentation of the release flow (tagging, version checks, publishing)

The workflow's provenance, permission scoping, and tag-gating are all sound; no issues met the reporting threshold.

Reviewed commit: 315456b

@cbascom
cbascom merged commit fbbbca1 into main Oct 13, 2026
4 checks passed
@cbascom
cbascom deleted the chore/tag-based-publish branch October 13, 2026 18:21
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