Skip to content

feat: PyPI-ready metadata and a tag-triggered release workflow - #436

Merged
KCNyu merged 3 commits into
masterfrom
claude/pypi-release
Aug 9, 2026
Merged

feat: PyPI-ready metadata and a tag-triggered release workflow#436
KCNyu merged 3 commits into
masterfrom
claude/pypi-release

Conversation

@KCNyu

@KCNyu KCNyu commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Toward #379. The acceptance criterion turned out to already pass — I ran it before
writing anything:

$ python -m build --wheel
$ python -m venv clean && clean/bin/pip install clawock-0.1.0-py3-none-any.whl
$ cd /tmp/newuser
$ env -i PATH=/usr/bin:/bin HOME=/tmp clean/bin/clawock init mybook
$ env -i … CLAWOCK_WORKSPACE=…/mybook clawock run prepare  > .clawock/work/request.json
$ env -i … clawock run publish --request … --artifact answer=…
{"status": "published", "generation_id": "3b3b66c8…", "artifacts": [answer, manifest.json]}

No checkout, no OpenClaw, no KCNyu workspace, HOME pointed away from /root.
So what is missing is not the lifecycle — it is everything around shipping it.

Metadata

pyproject.toml had no authors, no classifiers, no keywords and no
[project.urls]. Trove classifiers are how PyPI's own search and category
filters see a package; without them it is unlisted in every category a
prospective user would browse. This is invisible locally forever, because
pip install -e . never renders a project page.

test_the_wheel_carries_the_metadata_pypi_indexes_on reads the built wheel's
METADATA and asserts version parity with pyproject.toml, a license, trove
classifiers, the three URLs PyPI puts in the sidebar, and a
Description-Content-Type (without it the README renders as plain text). It
reuses the build the existing wheel test already does. Mutation-verified by
deleting the classifiers block.

Release workflow

Tag-triggered (v*) plus a manual TestPyPI rehearsal. Nothing publishes on
merge
— a version number, once on PyPI, is burned even if the release is
yanked, so the tag is the human decision and the pypi environment lets that
decision carry a required reviewer. Trusted publishing via OIDC, so there is no
long-lived token in repository secrets.

Before it publishes it runs twine check, refuses a tag that disagrees with the
packaged version, and executes the isolated-install run above against the exact
artifact about to ship. One-time PyPI-side setup is in
docs/operations/release.md; it needs kcn's account and cannot be done from here.

One rough edge fixed on the way

clawock run publish --request req.json answered:

'/x/req.json' is not in the subpath of '/x/.clawock/work'

A raw Path.relative_to ValueError. The boundary is deliberate — a request from
elsewhere has no provenance — but that message tells a first-time user nothing
about what to do, and run prepare prints to stdout, so putting the file in the
current directory is the natural first move. It now names the path prepare
actually wrote, and --help says so too. Same treatment for the artifact
boundary check.

Not done here

The actual publish. That is kcn's call and needs the PyPI-side publisher
configured first; #379 stays open until a release exists and pip install clawock
from the real index is verified.

KCNyu added 3 commits August 9, 2026 22:15
The no-hand-written-package-list guard was right to fire: build and twine were
pinned in a workflow line, a second source of truth. They are a `release` extra
now. Installing the wheel the same workflow just built is not a dependency list,
so the guard names that exception explicitly.
@KCNyu
KCNyu merged commit 9e8721a into master Aug 9, 2026
10 checks passed
@KCNyu
KCNyu deleted the claude/pypi-release branch August 9, 2026 14:25
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