Files
python-sdk/RELEASE.md
T
zeekay 0f5676c200 one release path, and the docs say the one that runs
RELEASE.md described GitHub Actions, GitHub secrets and three bin/ scripts
that each took a PyPI token off the environment. None of that publishes this
repo — .hanzo/workflows/publish-pypi.yml does, reading the token from KMS —
so the scripts are gone and the page describes the lane, its tag mapping, and
what to check before a tag makes a version permanent.

LLM.md carries the published state: hanzoai 3.2.13 with get_keys/get_tools,
and the KMS route that a 404 will otherwise misreport as a missing secret.
2026-08-14 17:51:02 -07:00

54 lines
2.0 KiB
Markdown

# Releasing
One lane: `.hanzo/workflows/publish-pypi.yml`, on the git.hanzo.ai runner fleet.
It reads the PyPI token from KMS at publish time — nothing else in this repo
uploads, and no token is ever handed to a shell here.
Bump the version, commit, push a tag:
```bash
# pkg/hanzo-mcp/pyproject.toml -> version = "0.15.15"
git commit -am "hanzo-mcp 0.15.15"
git push origin main
git tag hanzo-mcp-0.15.15 && git push origin hanzo-mcp-0.15.15
```
The tag names the packages:
| tag | publishes |
|---|---|
| `v3.2.13` | every workspace package |
| `hanzoai-v3.2.13` | the root `hanzoai` client alone |
| `hanzo-tools-0.3.5` | every `pkg/hanzo-tools-*` |
| `hanzo-mcp-0.15.15` | the matching `pkg/<name>`, else `hanzo` |
A `v*` tag is not as broad as it looks: uploads pass `--skip-existing`, so a
package whose version is already on PyPI is a no-op. What it does publish is
every package whose in-tree version is new — which is the point, and also the
reason to look before you tag.
Watch the run under Actions on git.hanzo.ai. Versions are semver, and PyPI never
lets a version be replaced: a bad release is fixed by a higher one.
## Before you tag
Build what the lane will build and read the metadata you are about to publish —
the registry page is the first thing a reader sees, and it is permanent.
```bash
python -m build . --outdir dist # or pkg/<name>
python -m twine check dist/*
```
`twine check` catches a README that will not render. It does not catch a missing
one: a package with no `readme`, `license` or `authors` in its `pyproject.toml`
uploads happily and lands as a blank page.
## If the lane fails
Read the job log — every failure mode it knows about prints a line saying which
credential or path it wanted. The KMS read is the one worth naming: KMS serves
`GET /v1/kms/secrets/<path>/<name>?env=<env>` and takes the org from the caller's
token, so a request in any other shape 404s exactly like an unseeded path. Prove
a route against a secret you know exists before concluding a secret is missing.