From a714b63b7f6a85812bc678143eb4e865ac54383e Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sat, 4 Jul 2026 16:36:41 -0400 Subject: [PATCH] docs(releasing): describe the process as it stands after this PR RELEASING.md documents the pipeline this PR ships (scheduled/dispatch CalVer releases, OIDC publishing, retry runbook), with a footer noting the planned Phase 2 changes tracked in #4463. The doc gets updated when that work merges, not before. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG --- RELEASING.md | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index f7a4fb1..51e6a52 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,30 +1,22 @@ # Releasing -How the packages in this repository are versioned and published, and what to do when a publish fails. - -> Implementation of this process is tracked in [#4463](https://github.com/modelcontextprotocol/servers/issues/4463). - -## Versioning - -- **TypeScript packages** use **semver**, managed by [changesets](https://github.com/changesets/changesets). A PR that changes a TypeScript package includes a changeset file declaring the bump (patch/minor/major) and a changelog line — the changeset bot flags PRs that are missing one. A rolling **"Version Packages" PR** accumulates pending changesets; merging it applies the version bumps and updates each package's `CHANGELOG.md`. - - patch = fixes · minor = new tools/prompts/resources/options · major = breaking changes (tool removed or renamed, schema changes that break clients, protocol or Node floor bump) -- **Python packages** use **CalVer** (`YYYY.M.D`). A prepare-release step stamps the date onto Python packages that changed since their last published version and opens a normal PR. -- **Versions live on `main` and are only changed by pull requests.** No workflow computes a version or pushes a tag. +How the packages in this repository are published, and what to do when a publish fails. ## How publishing works -Publishing is triggered by **creating a GitHub Release** on the [releases page](https://github.com/modelcontextprotocol/servers/releases) (with auto-generated notes). That fires the [`release.yml`](.github/workflows/release.yml) workflow, which is the **only** publish path: - -1. Every package is a matrix job, run independently (`fail-fast: false` — one package's failure never blocks another). -2. Each job: checkout at the release tag → install → **skip if the package's version already exists on its registry** → run the package's tests → build → publish. -3. Publish jobs are gated by the `release` environment — a required reviewer must approve the deployment. - -Because of the version-exists guard, a release publishes exactly the packages whose versions were bumped since the last release, and re-running is always safe. +All packages publish exclusively from the [`release.yml`](.github/workflows/release.yml) GitHub Actions workflow, gated by the `release` environment (a required reviewer must approve each deployment). Runs are triggered on a daily schedule or manually via **workflow_dispatch**. **Authentication is OIDC trusted publishing on both registries — there are no registry tokens.** -- **npm**: each `@modelcontextprotocol/*` package is registered on npmjs.com with a [trusted publisher](https://docs.npmjs.com/trusted-publishers) bound to this repository, workflow filename `release.yml`, and environment `release` (the binding is case-sensitive). Packages publish with [provenance attestations](https://docs.npmjs.com/generating-provenance-statements). -- **PyPI**: published via [PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/) using `pypa/gh-action-pypi-publish`. +- **npm** (TypeScript servers): each `@modelcontextprotocol/*` package is registered on npmjs.com with a [trusted publisher](https://docs.npmjs.com/trusted-publishers) bound to this repository, workflow filename `release.yml`, and environment `release` (the binding is case-sensitive). Packages publish with [provenance attestations](https://docs.npmjs.com/generating-provenance-statements). +- **PyPI** (Python servers): published via [PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/) using `pypa/gh-action-pypi-publish`. + +A release run: + +1. **Detects changed packages** since the last release tag — a package counts as changed if any `.py`, `.ts`, or `.md` file in its directory changed (READMEs ship inside the published artifacts). +2. **Stamps versions and pushes the release tag** — versions are date-based (CalVer, e.g. `2026.7.4`). +3. **Publishes each changed package as an independent matrix job** (`fail-fast: false` — one package's failure never blocks another). Each job: checkout at the release tag → install → abort if this version already exists on the registry (double-publish guard) → **run the package's tests** → build → publish. +4. **Creates the GitHub release** with generated notes. ## When a publish fails @@ -37,17 +29,21 @@ gh run rerun --failed --repo modelcontextprotocol/servers ``` - A re-run is still a `release.yml` run in the `release` environment, so it satisfies the trusted-publisher binding. -- It re-runs only the failed legs, checked out at the release tag — it publishes exactly the tagged code, and the version-exists guard makes it idempotent. +- It re-runs only the failed legs, checked out at the original release tag — it publishes exactly the tagged code, and the double-publish guard keeps already-published packages safe. - It needs a fresh `release` environment approval, and the run must be complete first (approve or reject any pending deployments). - GitHub's re-run window is ~30 days from the original run, and re-runs execute the *original* workflow snapshot — workflow fixes on `main` don't apply to a re-run. -**Otherwise: the next release picks it up.** The unpublished version is still on `main`, so the version-exists guard publishes it on the next release automatically. A version skipped this way simply never exists on that registry — that's benign; npm and PyPI version histories don't need to match. +**Otherwise: let the next release pick it up.** If the re-run window has closed (or the fix required a workflow change), the failed version simply never exists on that registry — that's benign; npm and PyPI version histories don't need to match. The package publishes at the next version, provided it has a qualifying change (`.py`, `.ts`, or `.md`) since the last release tag. **Never:** - Publish manually with an npm token or from a laptop — there are no registry tokens, and manual publishes would break the provenance/trust chain. -- Edit versions outside a reviewed PR to force a publish. +- Re-run or re-dispatch `release.yml` expecting a fresh run to retry a failed version — a fresh run mints a *new* version. ## Environment approvals The `release` environment's required-reviewer list is configured in the repository settings (Settings → Environments → `release`). Reviewer rights come only from that list — repository admin does not confer deployment approval. + +--- + +Planned changes to this process — semver via changesets for the TypeScript packages, publishing triggered by manually-created GitHub Releases — are tracked in [#4463](https://github.com/modelcontextprotocol/servers/issues/4463). This document will be updated when that work merges.