Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Publishing and Deployment Workflow

xmris strictly separates CI (testing) from CD (publishing) to avoid the “bump version → push → CI fails → bump again” cycle. Never bump the version until all tests pass.

We use uv for dependency management and GitHub Actions for testing across Python 3.10–3.13 on Ubuntu, Windows, and macOS.

Note: Workflow diagram at the end of the page.


① Daily Development

Work on a branch and land it through a pull request — main takes no direct pushes, and six checks gate every merge. That path has its own page: Open a pull request.

Documentation is republished on every merge to main, independently of releases — see .


② Release Preparation

When main is stable, create a release branch:

git checkout -b release/v0.2.0
git push origin release/v0.2.0

This triggers the Full CD Pipeline (ci-publish.yml) — a 12-job matrix covering all OS and Python combinations.

If tests fail

Do not bump the version. Fix directly on the release branch and push:

git commit -am "fix: windows path issue" && git push

The full matrix re-runs automatically. Repeat until green — all twelve legs, macOS included.


②b The Changelog

The matrix in ② takes roughly fifteen minutes and nothing else depends on it. That is when the changelog entry gets written, so it is ready to ride the bump commit in ③ — one pull request carries both the new version and the description of what is in it. How an entry is curated, and why nothing generates it, is its own page: Write a changelog entry.

Two properties of the entry are load-bearing later. Its heading says unreleased until the release, and ③ refuses to tag while that word is there — the only guard against a tag whose version the changelog never described. And its cross-links are MyST targets rather than URLs, so the build reports any that no longer resolve.


③ Bump, Merge, Then Tag

Once the matrix is fully green, bump on the release branch and land it through a pull request:

uv version --bump minor                  # bump version in pyproject.toml
git commit -am "chore: bump version to 0.2.0"
git push origin release/v0.2.0
gh pr create --base main --title "chore: bump version to 0.2.0"

Merge that pull request, then tag the merged commit on main — the tag is what ships:

git checkout main && git pull
git tag v0.2.0
git push origin v0.2.0                   # triggers the publish job

Tag after the merge, never before. The tag is the permanent release marker, so it has to sit on a commit that is actually in main’s history.

Why the order matters

Tagging the bump commit on the release branch and merging afterwards used to work because the merge-back was a direct git push origin main, which preserved that commit. main now takes no direct pushes, and pull requests here are squash-merged — so the merge would create a new commit and the tagged one would never enter main’s history. Tagging after the merge keeps git describe on main meaningful, and costs nothing: the tag triggers the full matrix again before anything reaches PyPI.


④ After the Release

The merge in ③ already put the bump on main, so what is left is:

The v* tag stays as the permanent release marker.


⑤ Documentation Deployment

Nothing to do here — and nothing release-specific. The documentation is rebuilt and republished on every merge to main, so the site tracks main rather than the latest release. The mechanics, including the per-pull-request preview sites and the manual redeploy button, belong to the pull-request lifecycle: How the documentation reaches the web.


Workflow Diagram