Release Process¶
This repository uses release-please in manifest mode to independently version three packages from a single, aggregated release PR:
- statuspro-openapi-client (component
client) — the Python API client, at the repo root - statuspro-mcp-server (component
mcp) — the Model Context Protocol server, instatuspro_mcp_server/ - statuspro-client (component
ts) — the TypeScript client, inpackages/statuspro-client/
Configuration lives in release-please-config.json and .release-please-manifest.json
at the repo root. (Not linked: they sit outside the docs tree, and mkdocs runs in strict
mode.)
How releases work¶
1. Every push to main updates one Release PR¶
.github/workflows/release-please.yml runs on every push to main. It never pushes to
main itself — it only opens or updates one aggregated pull request covering every
package that has releasable commits since its last release
(separate-pull-requests: false). That PR:
- bumps
versionin each changed package's manifest (pyproject.toml/package.json) - updates each package's changelog
- updates
.release-please-manifest.json
Because change detection is path-based (which files a commit touches), not
scope-based (a (client)/(mcp)/(ts) prefix in the commit message), a commit
that touches both statuspro_mcp_server/ and the repo root will bump both packages.
Commit scopes are still worth using for readability and changelog grouping, but they are
no longer load-bearing for version-bump decisions the way they were under
python-semantic-release.
2. A second workflow keeps the release PR internally consistent¶
.github/workflows/release-pr-prepare.yml runs on the release PR branch only (never on
main). It:
- resyncs
uv.lockto whatever versions release-please just bumped - keeps
statuspro_mcp_server/pyproject.toml'sstatuspro-openapi-client>=Xfloor equal to the client version this PR proposes, so the MCP package's declared dependency is always installable
Both changes are pushed as an extra commit on the release PR branch, so they land
atomically with the version bump when the PR is merged — never as a follow-up push
to main.
3. Merging the release PR creates tags and draft GitHub Releases¶
When the release PR merges, release-please.yml runs once more (still triggered by the
push to main the merge produces), notices the PR was just merged, and creates a tag +
draft GitHub Release for every package that changed, all at that single merge commit:
client-vX.Y.Zmcp-vX.Y.Zts-vX.Y.Z
Releases are created as drafts ("draft": true in the config). Draft releases can
still accept asset uploads; once a release is published it becomes
immutable
and permanently rejects new assets. See step 4.
4. Each tag triggers a build-and-publish job¶
.github/workflows/publish.yml triggers only on client-v* / mcp-v* / ts-v* tag
pushes — never on a main push. For the matching component it:
- builds the package/tarball (the MCP job also builds the
.mcpbbundle) - publishes to the registry (PyPI or npm) via OIDC — no stored tokens
- uploads the build artifact(s) to the still-draft GitHub Release
- flips the release to published (
gh release edit --draft=false)
The MCP job additionally builds and pushes a multi-arch Docker image to
ghcr.io/dougborg/statuspro-mcp-server after its PyPI publish succeeds.
This ordering — build, publish to registry, attach assets, then finalize the release — means the release is never finalized before its assets exist, so nothing is ever lost to the immutability rule.
Trusted Publisher environments (deviation from other repos in this migration)¶
publish.yml's jobs deliberately declare no environment:. statuspro's existing
PyPI Trusted Publishers for statuspro-openapi-client and statuspro-mcp-server were
registered with the environment field blank, matching the old release.yml's
publish-client-pypi/publish-mcp-pypi jobs, which never set environment: either.
PyPI includes the environment name in the OIDC claim it checks against the registered
Trusted Publisher, so adding environment: here — without first re-registering the
Trusted Publishers on PyPI to expect it — would make the already-working publishers stop
matching and break every future release. If dedicated environments are wanted later,
register them as Trusted Publishers on PyPI first, then add environment: to the
corresponding jobs.
Manual prerequisites before the first TS release¶
No npm Trusted Publisher exists yet for statuspro-client — the TS client has never
been published (no ts-v* tag exists). Until one is registered, publish-ts will fail
its registry-publish step. Configure, before merging a release PR that bumps the ts
component:
- npm Trusted Publisher for
statuspro-client, workflowpublish.yml, jobpublish-ts
The PyPI Trusted Publishers for the client and MCP packages already exist and require no changes.
Commit message format¶
Conventional Commits still drive what kind of bump happens (feat → minor,
fix/perf → patch, !/BREAKING CHANGE → major); scopes remain useful for changelog
readability. Which package(s) get bumped is now determined by which paths a commit
touches, not by its scope:
git commit -m "feat(client): add helper for archived orders" # bumps client
git commit -m "fix(mcp): correct stock level calculation" # bumps mcp
git commit -m "feat(ts): export pagination helpers" # bumps ts
git commit -m "docs: update contributing guide" # bumps nothing
Tag format¶
Tags carry an explicit component prefix, matching the format used before this migration
(include-component-in-tag: true), so existing tag history is continuous:
client-v0.1.0,client-v0.2.0, …mcp-v0.1.0, …ts-v0.1.0, …
Troubleshooting¶
No release PR appears after merging a PR to main¶
Check that at least one commit since the last release touches a path release-please
watches (., statuspro_mcp_server/, or packages/statuspro-client/) with a
feat/fix/perf/breaking-change commit. docs:/chore:/test: commits do not
trigger a release-worthy change on their own.
The release PR's uv.lock or MCP pin looks stale¶
Check the release-pr-prepare.yml run for that PR — it runs on every push to the PR
branch (including release-please's own force-pushes) and should show a
chore(release): sync uv.lock and MCP client pin commit if anything needed resyncing.
Publish job failed with a PyPI/npm auth error¶
For npm: almost certainly the Trusted Publisher prerequisite above hasn't been
configured yet. For PyPI: check that the Trusted Publisher on PyPI still has no
environment set — if one was added there without a matching environment: here (or
vice versa), the OIDC claim will stop matching.
Release created but no assets attached¶
The publish job for that component failed before the "attach assets" step (usually the
registry publish itself). Fix the underlying failure and re-run — the release stays in
draft state (and therefore mutable) until the workflow successfully reaches
gh release edit --draft=false.