This repository releases workspace packages independently. A release may include one package or several packages, depending on handwritten news fragments and workspace dependency cascades.
Package discovery and release/build ordering come from uv workspace metadata,
not from a hard-coded package list.
Useful commands:
python scripts/workspace.py list --names --topological
python scripts/workspace.py version vercel-cache
python scripts/workspace.py version-file vercel-cacheUser-visible changes need a news fragment under:
changes/<package>/<id>.<type>.md
Valid types are:
breakingfor major changes, or minor changes while the package is0.xfeaturefor minor changesbugfixfor patch fixesdocsfor patch documentation changesinternalfor patch internal changes
Example:
changes/vercel-cache/123.bugfix.md
News fragment content should be one or more concise changelog bullets. The
release script adds - when a line does not already start with a bullet.
Dependency-only cascade releases do not need handwritten news fragments. The
release script writes Update dependencies. for those changelog entries.
Run:
uv run poe release-status
uv run poe releaserelease-status prints the pending release set without editing files.
release does all release PR file updates:
- requires a clean Git working tree
- creates a release branch named
<gh-username>/release-<timestamp> - finds packages with news fragments
- computes the semver bump for each package
- walks reverse workspace dependencies
- adds dependency-only patch releases for dependents whose workspace dependency lower bounds need to move
- updates package
version.pyfiles - prepends per-package
CHANGELOG.mdentries - removes consumed news fragments
- runs
uv lock - stages the release diff
- opens
git commit -vwith a prepopulatedRelease Packagescommit message - pushes the current branch
- opens a pull request with
gh pr createnon-interactively
News-fragment-backed changelog bullets include PR numbers when Git history for
the news fragment contains GitHub squash subjects like
pkg: fix cache cleanup (#123) or merge subjects like
Merge pull request #123 from user/branch.
Dependency-only cascade entries still render as Update dependencies. without a
PR suffix.
Review the staged verbose diff in the commit editor before saving the release commit. After a successful commit, the script pushes the current branch and opens the release PR.
Initial bump mapping:
breaking->majorfeature->minorbugfix,docs,internal->patch
For 0.x packages, breaking bumps minor instead of major.
If a package has multiple news fragments, the largest bump wins. If a package is also pulled in by a dependency cascade, its handwritten news fragment bump still wins over the dependency-only patch bump.
Package pyproject.toml files keep publishable dependency declarations in:
[tool.vercel.release.dependencies]
dependencies = [
"vercel-headers>=0.6.0",
]project.dependencies is dynamic. The Hatch metadata hook rewrites workspace
dependencies at build time to use the current sibling package versions, while
preserving markers, extras, and upper bounds. Third-party dependencies pass
through unchanged.
Local development still uses [tool.uv.sources] workspace links.
There is a tiny hatch_build.py loader in each package root because Hatch's
custom metadata hook path is resolved relative to the package both during local
builds and during wheel builds from sdists. The shared implementation lives in
scripts/hatch_build.py, and sdists include it as _vercel_hatch_build.py so
sdist rebuilds are self-contained.
Publishing is done by GitHub Actions through PyPI trusted publishing. Do not publish packages from a maintainer machine.
When the release PR is merged to main, .github/workflows/publish.yml:
- detects packages whose version file or changelog changed in the merge commit
- publishes changed packages in dependency order
- builds each package with
uv build --package <name> --no-sourcesinto an isolated workflow directory - publishes only that package's artifacts with
uv publishand OIDC trusted publishing - creates and pushes
<package>-v<version>tags after successful publish - creates matching GitHub releases
The workflow skips the PyPI upload when the exact version already exists, but still creates the tag and GitHub release idempotently. This lets a failed tag or release step be retried after a successful upload.
Before merging a release PR, run at least:
uv run poe lint-towncrier
uv run pytest tests/unit/test_release_system.py
uv build --package vercel-cache --no-sourcesFor broader confidence, run:
./scripts/lint.sh
./scripts/typecheck.sh
./scripts/test.shlint-towncrier validates news fragment package names, news fragment filename
types, and non-empty news fragment content. The Git pre-push-news-fragments
hook generated from scripts/githooks/pre-push.news-fragments.sh runs uv run poe check-news-fragments to require news fragments for changed package code
without maintaining a hard-coded package registry. sync-githooks installs
pre-push hooks with WORKSPACE_POE_GIT_SCOPE=commit, so this check runs against
the commit tree being pushed. The news fragment format is Towncrier-style, but
enforcement is repo-local because stock Towncrier does not support
changes/<package>/ discovery without enumerating every package.