Every package releases independently. The default path is a coordinated release-all workflow; per-package workflows are for urgent or partial releases.
Normal releases are tag-from-main: each publish creates a tag
(<pkg>==<version>) pointing at the release commit on main. Patches are the
exception — they are cut from package-release/<pkg>/v* branches created lazily
from a release tag, and their tags point at the backport branch, not main.
Releases are driven by GitHub Actions workflows. They handle version bumps,
changelog generation (via towncrier),
tagging, PyPI publishing, GitHub releases, and changelog updates on main.
Packages use the OpenTelemetry beta versioning format MAJOR.MINORbN
(e.g. 1.0b0). version.py carries a .dev suffix during development
(e.g. 1.0b0.dev); the prepare workflow drops it at release time.
Version cadence on main:
- The post-release bump advances the minor automatically. After each
successful release of
X.YbN, the release workflow opens a PR bumpingmainto the next unreleased minorX.(Y+1)b0.dev.maintherefore always sits on an unreleased minor line, and the next release frommainis that minor. - Patches come from release branch. To patch an already-released version you cut a backport branch from its tag (see Backport patch) — this is the same procedure whether the release is the latest or an older one.
- Major bumps are maintainer-led. Trigger the
Bump package major versionworkflow (documented below) when you're ready to move a package to the next major line. TheBump package minor versionworkflow exists to jump the minor ahead of a release without shipping; it happens only when a maintainer explicitly asks for it.
Unlike opentelemetry-python-contrib, we do not maintain a long-lived release
branch for every minor. Normal releases tag main directly; patch branches
are created on demand from a release tag when patching an already-released
version.
| opentelemetry-python-contrib | This repo | |
|---|---|---|
| Normal release | Long-lived package-release/<pkg>/v* branch |
Tag on main |
| Tag target | Commit on the release branch | Commit on main |
| Patch a released version | Commits + tags on the release branch | Branch from the tag (lazy) |
| Branch sprawl | One branch per package per minor | Branches only for patches |
For releasing every package that has towncrier changelog fragments:
- Run the
Prepare releaseworkflow againstmain. Leave thepackageinput empty for the bulk case.- Finds packages with fragments under
.changelog/. - Opens one combined PR on
mainthat drops.devsuffixes and runstowncrier buildfor each eligible package. - Labels the PR
release.
- Finds packages with fragments under
- Review and merge the prepare PR.
- The
Release allworkflow runs automatically when a labelled prepare PR merges (or trigger it manually againstmain).- Publishes each ready package to PyPI.
- Creates a GitHub release tag (
<pkg>==<version>) onmainfor each. - Opens a PR bumping released packages to the next minor
.devversion.
Packages without changelog fragments are skipped during prepare and logged in the workflow output.
Use when only one package needs to ship, or the rest of the workspace is not
ready for a bulk release. The version type (patch vs minor vs major) depends
on what's currently in version.py on main — see Version model.
- Run
Prepare releaseagainstmainand set thepackageinput to the target package. The workflow opens a PR that drops the.devsuffix and runstowncrier buildfor just that package (still labelledrelease). - Review and merge the prepare PR.
- Either wait for
Release allto fire on the merged prepare PR, or runRelease packageagainstmainfor that one package.
The minor advances automatically as the last step of every release (see Version model). Jumping the minor ahead of a release, or moving to the next major line, is a maintainer decision:
Bump package minor versionopens a PR that editsversion.pyfromX.YbN(.dev)toX.(Y+1)b0.dev.Bump package major versionopens a PR that editsversion.pyfromX.YbN(.dev)to(X+1).0b0.dev.
Merge the bump PR first, then follow the normal
bulk or
single-package release flow. Prepare release
picks up the new dev version verbatim.
Patching any already-released version — the latest or an older one — comes from a branch cut from that version's tag.
- Create
package-release/<pkg>/v<X>.<Y>bxfrom the<pkg>==<X>.<Y>b<N>tag if it does not exist yet. - Cherry-pick or develop the fix on the branch.
- Run
Prepare backport patchagainst the backport branch. Bumps the patch version and runstowncrier build. - Review and merge the prepare PR into the backport branch.
- Run
Release packageagainst the backport branch.- Tags the backport branch and opens a PR copying changelog updates to
main.
- Tags the backport branch and opens a PR copying changelog updates to
Several packages carry CHANGELOG entries that pre-date towncrier (added
before the towncrier marker was inserted). towncrier build does not
fold them into the generated release section. Before the first towncrier
release of a given package, fold those entries by hand into the new
release section produced by towncrier build (or convert them into
fragments first). The do-not-edit comment in each CHANGELOG.md flags
this.
When a new package is ready to ship:
- Add its name to the
packages=list under[release_packages]ineachdist.ini. Packages not listed here are skipped by the release workflows. - Add the package to the dropdown options in the workflow files that offer
a package selector:
release-package.yml,prepare-release.yml,prepare-backport-patch.yml,bump-package-minor.yml, andbump-package-major.yml. - Create the PyPI project and register two trusted publishers (Manage → Publishing → Add a new pending publisher), one for each workflow that publishes. For detailed instructions, refer to PyPI's documentation on Creating a PyPI project with a Trusted Publisher or Adding a Trusted Publisher to an existing PyPI project. Note that creating a pending publisher does not reserve the project name on PyPI:
| Field | Entry 1 | Entry 2 |
|---|---|---|
| PyPI project name | e.g. opentelemetry-util-genai |
same |
| Owner | open-telemetry |
open-telemetry |
| Repository name | opentelemetry-python-genai |
opentelemetry-python-genai |
| Workflow name | release-package.yml |
release-all.yml |
| Environment name | pypi |
pypi |
- Optionally reserve the package name to prevent name-squatting shortly after
the introductory PR lands on
mainby navigating to https://pypi.org/manage/organization/opentelemetry/projects/, scrolling to the bottom (Add project to organization), and using the form.
All packages share the same environment. The first upload from CI activates each publisher.
At least one publishable package needs a towncrier fragment under
.changelog/ (any file other than .gitkeep / .gitignore).
Re-run the release workflow (Release package or Release all). Trusted
Publishing only works from GitHub Actions, there is no repo-stored PyPI token
for manual twine upload.
If the wheel was built but upload failed, fix the underlying issue (PyPI
project missing, trusted publisher misconfigured, environment approval pending)
and re-run. The workflow uses skip-existing, so a partial upload is safe to
retry.
After a successful PyPI upload, re-running picks up remaining steps (GitHub release tag + follow-up PRs) if those failed.
Merge the prepare PR first. Release workflows require a non-.dev version in
version.py.
- A
backportworkflow (create backport branches manually from release tags when needed).