Skip to content

Releasing

Four distributions, one entrance: a version bump reaching main is the release.

Cutting one

cd funduq-contract && uv version --bump patch

Open that as a pull request, review it like any other diff, merge it. The workflow notices which pyproject.toml declares a new version, publishes that distribution, and writes the tag afterwards.

Nothing else to remember, and nothing to keep in sync: the version lives in exactly one place a human writes, and the tag is derived from it after the upload succeeds. A tag existing means that version is on PyPI.

If several move together, funduq-contract publishes first — the other two declare it as a dependency, and an install resolves nothing until it exists.

The gate is the environment

Merging publishes, and PyPI is irreversible: a filename cannot be reused, so a wrong version is not something a later commit fixes. Give each of the three environments a required reviewer and every upload stops for a human first. That is the pause; there is no second one.

There is no token

PyPI trusts this repository, this workflow file, and one environment per project; GitHub mints a short-lived OIDC token per run. Nothing long-lived is stored anywhere, and revoking a publisher is a click rather than a secret rotation.

Four things are load-bearing configuration and not free to change:

hukaichun/funduq the repository
release.yml the workflow filename
pypi-funduq, pypi-funduq-contract, pypi-funduq-provider-sdk one environment per project

Renaming any of them breaks publishing silently. The environments are separate because PyPI refuses to register one (owner, repo, workflow, environment) against more than one project name — a rule worth keeping rather than working around, since one configuration able to publish several names is one thing to compromise for all of them.

Two designs that look better and are not

Deriving the version from the tag (hatch-vcs and friends) would remove the number from pyproject.toml entirely, so nothing could disagree. It fails here, and silently: it works by git describe, which finds the nearest reachable tag whatever its prefix. With three interleaved tag series in one repository, building funduq-contract just after a funduq-provider-sdk release yields the provider-sdk's version — measured, not guessed — and PyPI accepts it. A per-package --match pattern repairs it, at the price of a misconfiguration publishing a wrong version instead of failing.

Tagging in one workflow and publishing on the tag in another cannot work at all: events created with GITHUB_TOKEN do not start workflow runs, so the release would simply never happen. Detection and publication are one workflow for that reason, and no personal access token is needed anywhere.

A name is not yours until you publish

A pending publisher does not reserve a project name. Until the first upload, anyone may register it — including through a pending publisher of their own.