ci(release): publish to PyPI via trusted publishing (#45) - #46
Merged
fsecada01 merged 1 commit intoJul 31, 2026
Merged
Conversation
A `v*` tag now builds, validates, and publishes. Trusted publishing means no API token is created, stored, or rotated: the job mints a short-lived OIDC token that PyPI exchanges for an upload token scoped to this project alone. There is no repo secret that could leak. The upload is a separate job gated on a `pypi` environment, so protection rules can require a review before anything reaches PyPI. Three assertions run before the artifact is handed to the publish job, all of them because a bad upload can only be yanked, never replaced: * `twine check` on both distributions — a malformed README renders as a broken PyPI page that cannot be fixed in place. * The tag must equal `pyproject.toml`'s version, or the wheel would publish under a name the tag does not describe. * The wheel must actually contain the templates. This is the one worth spelling out: cf-ui *is* templates, and they ship inside the package so hatchling includes them with no explicit config — which is exactly what makes a regression here quiet. A packaging change that dropped them would produce a wheel that installs cleanly, imports cleanly, and renders nothing. The check counts JinjaX templates, cotton wrappers, and cotton theme partials against the 14-per-set invariant the unit suite already asserts, then requires `assets.jinja` (a sibling of `templates/jinja/`, so a theme-shaped glob would miss it — see #42) and the four static assets. Verified against a real wheel: 70 jinja, 14 wrappers, 70 partials, assets present. Verified it can fail: a wheel rebuilt with `cf_ui/templates/` stripped exits 1 naming all four missing groups. Closes #45 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhqNRBg83czKfr8L6FF5xf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #45.
A
v*tag now builds, validates, and publishes cf-ui to PyPI. Matches the workflowcomponent-frameworkgot in fsecada01/component-framework#48, so both projects release the same way.Trusted publishing, not a token
The publish job mints a short-lived OIDC token that PyPI exchanges for an upload token scoped to this project alone. No API token is created, stored, or rotated — there is no repo secret that could leak, and nothing to revoke if the repo is ever compromised. The upload is a separate job gated on a
pypiGitHub environment, so protection rules can require a review before anything reaches PyPI.Three gates before anything is uploaded
All three exist for the same reason: a bad upload can only be yanked, never replaced. PyPI does not let you re-upload a filename.
twine check dist/*pyproject.tomlversionWhy the template check earns its place
cf-ui is templates. They live inside the package specifically so hatchling includes them with no explicit
[tool.hatch.build]config — which is exactly what makes a regression here silent. A packaging change that droppedtemplates/produces a wheel that installs cleanly, imports cleanly, and renders nothing. No test in this repo would see it, because every test runs against the source tree, not the built artifact.The check counts against the same 14-per-set invariant the unit suite already asserts, then requires the individual files a count would miss:
templates/cf_ui/assets.jinja— a sibling oftemplates/jinja/, so any theme-shaped glob skips it. That is precisely the mistake Litestar cannot render components — the installer never builds a JinjaX catalog #42 was: the Litestar installer globbed the theme folder and the asset macros vanished.cf_ui_alpine.js,cf_ui_axes.css,cf_ui_axes.json,cf_ui_tailwind_plugin.mjs— the last three are build products ofaxes.py, and the.mjsis vendored.Verification
Run against a real wheel built from this branch:
And proven able to fail — a wheel rebuilt with every
cf_ui/templates/entry stripped:The step's heredoc framing is also checked: the YAML block scalar dedents to a
PYterminator at column 0, so the script reaches bash intact.Not in this PR
Cutting the 0.2.0 tag. Two things gate it:
component-frameworkhas to publish first. cf-ui's wheel metadata carriesRequires-Dist: component-framework>=0.4with no git source —[tool.uv.sources]pins do not travel into wheel metadata — so pip cannot resolve cf-ui until that name exists on PyPI.fsecada01, repocomponent-framework-ui, workflowrelease.yml, environmentpypi.🤖 Generated with Claude Code
https://claude.ai/code/session_01NhqNRBg83czKfr8L6FF5xf