Releasing¶
tau ships as two PyPI distributions, tau-core and tau-tui, released together with the same
version. A release is one tag away: push vX.Y.Z and the release workflow builds both
packages, smoke-tests the wheels, publishes them to PyPI through trusted publishing and creates
the GitHub release with the files attached. The docs site deploys itself on every push to
main. This is the "installable by anyone" half of
ADR 0006.
Versioning¶
- Both packages carry the same version.
tau-tuidepends ontau-coreand is released with it, even when only one of them changed. - The tag is
vplus the version inpackages/tau-core/pyproject.toml, for examplev0.3.0. The workflow refuses a tag that does not match it, and atau-tuiversion that differs fromtau-core. - Versions follow PEP 440. A pre-release such as
0.3.0rc1is taggedv0.3.0rc1and becomes a GitHub pre-release.
Cut a release¶
Before you start
main is green in CI and everything you want to ship is merged. Tags are cut from main.
-
Bump both versions (this also updates
uv.lock): -
Commit and tag:
-
Watch the run under Actions → release. The
buildjob checks the tag against the version, builds the sdists and wheels, installs them into a fresh virtualenv and runstau --help,tau version,tau init --yes,tau doctor,tau components, a scriptedtau chatandtau tui --help, then repeats the documenteduv tool install.publishuploads each package (one job per package) andgithub-releasecreates the release. -
Write the changelog. The release is created with notes generated from the pull requests and commits since the previous tag. Open it under Releases, edit the notes into a few lines a user can read (what changed for someone running
tau, what to do after upgrading) and save. -
Verify from a clean machine, or a scratch tool directory:
Something went wrong halfway
Rerun the workflow from the Actions tab: publishing skips files PyPI already has
(skip-existing) and the GitHub release is updated rather than duplicated. If the code
itself was wrong, fix it on main, bump to the next patch version and tag again; a
version that reached PyPI can never be uploaded twice.
Dry run¶
The same workflow without the upload. Under Actions → release → Run workflow pick main,
or the tag you are about to publish, and leave dry run ticked. From a shell:
gh workflow run release.yml --ref main # build, smoke test, artifacts
gh workflow run release.yml --ref v0.3.0 -f dry_run=false # publish an existing tag by hand
The built files are attached to the run as the dist artifact. The smoke test also runs
locally, against wheels built into a scratch directory:
uv build --package tau-core --out-dir /tmp/tau-dist
uv build --package tau-tui --out-dir /tmp/tau-dist
uv venv --python 3.12 /tmp/tau-venv
uv pip install --python /tmp/tau-venv/bin/python /tmp/tau-dist/*.whl
/tmp/tau-venv/bin/tau init --yes --home /tmp/tau-home --provider fake --language en
/tmp/tau-venv/bin/tau --home /tmp/tau-home doctor
printf '/status\nhello\n/quit\n' | /tmp/tau-venv/bin/tau --home /tmp/tau-home chat
The fake provider answers "Okay." and needs no credentials.
One-time setup¶
PyPI: trusted publishing¶
No API token is stored on GitHub. The publish job presents its GitHub OIDC token to PyPI,
which answers with a short-lived upload token for the projects that trust this workflow
(trusted publishers). Configure it once per
project:
- Sign in to PyPI and open Publishing.
-
Under Add a new pending publisher (a project that does not exist yet is created by its first upload) fill in:
Field Value PyPI project name tau-coreOwner fportRepository name tauWorkflow name release.ymlEnvironment name pypi -
Repeat with the project name
tau-tuiand the environment namepypi-tui. PyPI accepts only one pending publisher per (repository, workflow, environment) tuple, so each package publishes from its own GitHub environment; the workflow's matrix already mapstau-core → pypiandtau-tui → pypi-tui. - On GitHub, Settings → Environments → New environment →
pypi, thenpypi-tui(orgh api -X PUT repos/fport/tau/environments/pypiand the same forpypi-tui). Optional: add yourself as a required reviewer, so every tag waits for a click before anything is uploaded. The first run creates a missing environment; the reviewer rule is the reason to create it by hand.
Names must match exactly
PyPI compares the owner, repository name, workflow file name and environment name with
the claims in the OIDC token. A typo shows up as invalid-publisher in the publish job.
Fix the publisher on PyPI and rerun; no code change is needed.
The docs site on Cloudflare¶
.github/workflows/docs.yml builds the site with mkdocs build --strict on every push that
touches docs/** or mkdocs.yml, and deploys it as Cloudflare Workers static assets
(the successor of Cloudflare Pages) with wrangler deploy -c infra/docs/wrangler.jsonc. No
code runs at the edge; the Worker tau-docs only serves the uploaded files.
-
The first deploy creates the Worker. From a shell where
wrangleris logged in (npx wrangler login):The site is live at
https://tau-docs.<your-subdomain>.workers.devright away. -
For the automated deploy, create an API token (My Profile → API Tokens → Create Token → "Edit Cloudflare Workers" template, or a custom token with Account · Workers Scripts · Edit) and note the account id (dashboard sidebar). Store both as repository secrets:
Without them the workflow only builds the site and keeps it as an artifact.
-
Once the domain's DNS is on Cloudflare (roadmap issue 022): Worker → Settings → Domains & Routes → add
docs.tau.<your-domain>. Cloudflare creates the DNS record and the certificate;site_urlinmkdocs.ymlalready points there.
Dry run
Actions → docs → Run workflow with deploy unticked builds the site without touching
Cloudflare; the built site/ is attached to the run as an artifact.
What the workflows check¶
| Check | Where | Fails when |
|---|---|---|
ruff check, ruff format --check, pytest, uv build |
ci.yml, every push and pull request |
lint, formatting or a test fails, or a package does not build |
mkdocs build --strict |
ci.yml and docs.yml |
a page listed in nav is missing or a link points nowhere |
| tag ↔ version | release.yml |
the tag is not v<tau-core version>, or tau-tui has another version |
| wheel smoke test | release.yml |
the tau command, its tui subcommand, the templates tau init writes or the entry points are missing from the wheels |