Skip to content

chore(release): 0.3.0, and make the release guards able to fail (#63) - #64

Merged
fsecada01 merged 1 commit into
masterfrom
chore/component-framework-ui-63-release-0-3-0
Aug 1, 2026
Merged

chore(release): 0.3.0, and make the release guards able to fail (#63)#64
fsecada01 merged 1 commit into
masterfrom
chore/component-framework-ui-63-release-0-3-0

Conversation

@fsecada01

Copy link
Copy Markdown
Owner

Closes #63.

Cuts 0.3.0 — and fixes two latent defects in the release plumbing first, because a PyPI upload can only be yanked, never replaced.

The release

0.2.0 shipped 14 structural components and nothing underneath them. 0.3.0 is the primitives layer: seven components (button, badge, heading, label, icon, box, prose) across five themes in both template sets, on one shared vocabulary, plus the daisy CDN fix (#56), the grid decision (#55) and djLint across both template trees.

The changelog carries one upgrade note above the section list, because it is the only behaviour change a 0.2.0 consumer can be surprised by: on the daisy theme, cf_ui_head now emits Tailwind's Play CDN script alongside the daisyUI stylesheet. If you already have a real Tailwind build, CF_UI_DAISY_CDN = "off" restores the previous single-tag output. Every other theme is untouched.

The wheel guard could not catch what it exists to catch

release.yml asserted >= 14 JinjaX templates, >= 14 cotton wrappers and >= 14 cotton theme partials. Those are 0.1.0 figures, from when 14 components in one template set was the entire package. The real counts are now 21 wrappers, 105 jinja, 105 partials — so a packaging regression that dropped all seven primitives from all five themes would have left exactly 14 per set and passed. The required-assets list had also never picked up cf_ui_primitives.json, added in #52, so a wheel shipping without it published clean.

Bumping the numbers would move the staleness to the next component, so the mechanism is replaced rather than patched. The guard now derives its expectation from the source tree — every .jinja/.html/.js/.mjs/.css/.json under templates/ or static/ — and asserts the wheel contains all of them. Wheel entries are already src-relative, so the paths compare directly with no rewriting. It cannot drift, and it fails on one missing file rather than only on a missing fourteen.

That swap reintroduces the same hazard in a new place, which is worth naming rather than leaving for someone to find: if the source layout ever moves, the expectation set silently becomes empty and every wheel passes. So the guard checks its own scan found something before trusting a pass, with a floor (100) far below the real figure (237) — low enough never to need editing, high enough that an empty scan is caught.

The version was declared twice with nothing comparing them

pyproject.toml and src/cf_ui/_version.py both carry it, and release.yml's tag-match step reads only the former. A bump that missed _version.py would publish a correctly-named wheel whose cf_ui.__version__ reported the previous version — indefinitely, with every gate green.

tests/unit/test_version.py now asserts they agree. It lives in the unit suite rather than in release.yml deliberately: the drift is introduced when the bump commit is written, and that is when it should fail, not at the tag when the commit is already on master.

Both guards were mutation-tested, not assumed

Per this repo's rule that a test has to be able to fail:

Mutation Expected Result
nothing (baseline) pass pass
_version.py left at 0.2.0 fail fail — agreement
both declarations set to 0.3.0.dev0 fail agreement passes, PEP 440 check fails
wheel intact pass pass, 237/237
7 daisy primitive templates removed from the wheel fail fail — this is the case the old thresholds allowed
cf_ui_primitives.json removed from the wheel fail fail — never checked before at all
src/ present but emptied of templates fail fail, refuses to pass vacuously

The third row is why it is two tests. A first pass mutated only _version.py, which tripped the agreement assertion before the shape check ever ran — so the PEP 440 guard was passing untested. Mutating both declarations to the same suffixed value isolates it: agreement holds, and the shape check is then the only thing that can fire. It did.

The wheel cases run the guard script extracted verbatim from release.yml, not a paraphrase, against a wheel from uv build --wheel — the same builder CI uses.

Acceptance criteria

  • Version bumped in both pyproject.toml and src/cf_ui/_version.py.
  • [Unreleased] rolled into [0.3.0] — 2026-07-31.
  • Wheel guard derives expectations from the source tree, and fails if its own scan finds nothing.
  • cf_ui_primitives.json covered — automatically, by being in static/, rather than by being added to a list that will go stale again.
  • Version agreement asserted in the normal CI suite.
  • Every new guard proven non-vacuous by mutation.
  • Tag v0.3.0 — after this merges. Publish still waits on the pypi environment gate.

Gate

2313 passed, 13 skipped · ruff check src tests clean · ruff format --check src tests clean · mkdocs build --strict clean · wheel builds and passes the new guard at 237/237.

Note on merging

Do not tag until this is on masterrelease.yml's tag-match step compares the tag against pyproject.toml at the tagged commit, so a tag pushed before the merge would either miss the bump or point at a commit that is not what shipped.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NhqNRBg83czKfr8L6FF5xf

Bumps both version declarations and rolls [Unreleased] into [0.3.0].

Two latent defects in the release plumbing are fixed first, because a
PyPI upload can only be yanked, never replaced.

The wheel guard could not catch what it exists to catch. It asserted
>= 14 JinjaX templates, cotton wrappers and cotton partials — figures
from 0.1.0, when 14 components in one set was the whole package. The
real counts are now 21 wrappers and 105 partials, so a packaging
regression that dropped all seven primitives from all five themes
would have left exactly 14 and passed. Its required-assets list had
also never picked up cf_ui_primitives.json from #52.

Bumping the numbers would only move the staleness to the next
component, so the mechanism is replaced: the guard derives its
expectation from the source tree and asserts the wheel contains every
shipped template and asset. It cannot drift, and it fails on one
missing file rather than only on a missing fourteen.

That swap carries the same hazard in a new place — if the source
layout moves, the expectation set silently becomes empty and every
wheel passes. So the guard asserts its own scan found something
first, with a floor far below the real figure.

The version was declared twice with nothing comparing them.
pyproject.toml and _version.py both carry it and the tag-match step
reads only the former, so a bump missing _version.py would publish a
correctly-named wheel whose cf_ui.__version__ reported the previous
version, permanently, with every gate green. Now asserted in the unit
suite rather than at release time, because the drift is introduced
when the bump commit is written.

Both new guards were mutation-tested rather than assumed:

  version agreement  clean pass; fails with _version.py left at 0.2.0
  PEP 440 shape      isolated by mutating both declarations to
                     0.3.0.dev0, so agreement holds and only the shape
                     check can fire — it did
  wheel, intact      pass, 237/237
  wheel, 7 daisy primitives removed   fail (the old guard's blind spot)
  wheel, primitives.json removed      fail (never in the old list)
  wheel, src/ emptied of templates    fail, refusing to pass vacuously

Gate: 2313 passed, 13 skipped · ruff check and format clean ·
mkdocs build --strict clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NhqNRBg83czKfr8L6FF5xf
@fsecada01 fsecada01 added the enhancement New feature or request label Aug 1, 2026
@fsecada01
fsecada01 merged commit 3d40909 into master Aug 1, 2026
7 checks passed
@fsecada01
fsecada01 deleted the chore/component-framework-ui-63-release-0-3-0 branch August 1, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release 0.3.0: the primitives layer — and fix the wheel guard and version drift first

1 participant