Skip to content

Release workflow

This document describes how pymqrest versions are managed and published to PyPI.

Version management

The version is stored statically in pyproject.toml under [project].version. It follows semantic versioning (MAJOR.MINOR.PATCH).

After each release, the publish workflow automatically opens a PR to bump the patch version on develop. This default can be overridden at any time by changing the version to a minor or major bump instead.

Release flow

  1. Develop — All feature work merges into develop. Ensure the version in pyproject.toml is set to the desired release version.
  2. Prepare release — Run the release preparation script from develop:
uv run python3 scripts/dev/prepare_release.py

The script automates everything needed to get a release PR open: - Validates preconditions (on develop, clean tree, tools available) - Creates a release/X.Y.Z branch from develop - Generates the changelog via git-cliff - Commits the changelog update on the release branch - Pushes the branch and creates a PR to main - Enables auto-merge (regular merge, delete branch)

  1. Merge to main — The PR merges automatically once CI passes using a regular merge commit (not squash). This preserves shared ancestry between main and develop, avoiding history divergence. If auto-merge is not enabled on the repository, merge manually with --merge.
  2. Automatic publish — The publish.yml workflow fires on push to main and:
  3. Extracts the version from pyproject.toml
  4. Skips if the version is already on PyPI (idempotent)
  5. Builds sdist and wheel with uv build
  6. Publishes to PyPI via OIDC trusted publishing
  7. Creates an annotated git tag (vX.Y.Z)
  8. Creates a develop-vX.Y.Z lightweight tag on develop for git-cliff boundary tracking
  9. Creates a GitHub Release with install instructions and dist artifacts
  10. Opens a PR against develop to bump the patch version (e.g. 1.0.01.0.1), assuming the next release is a patch

Automatic version bump

After each successful publish, the workflow creates a PR to increment the patch version on develop. This keeps the working version ahead of the last release and ready for the next patch. The bump PR also refreshes all dependencies to their latest compatible versions via uv lock --upgrade and re-exports requirements.txt and requirements-dev.txt.

If the next release should be a minor or major bump instead, simply change the version in pyproject.toml at any point during the development cycle — the automated PR is just a default starting point.

The bump PR is skipped if develop already has the expected next version (e.g. if someone bumped it manually first).

Changelog

The project changelog is maintained in CHANGELOG.md using git-cliff, configured via cliff.toml at the repository root.

git-cliff is a local developer tool (not required in CI). Install it with Homebrew:

brew install git-cliff

How it works

git-cliff uses develop-vX.Y.Z lightweight tags as version boundary markers. These tags point to commits on develop and are created automatically by the publish workflow after each release. They allow git-cliff to determine which commits belong to each version when run on develop or a release branch.

To regenerate the changelog from scratch:

git-cliff -o CHANGELOG.md

To generate the changelog with a new version heading (used during the release flow):

git-cliff --tag develop-vX.Y.Z -o CHANGELOG.md

CI validation

The release-gates CI job validates that CHANGELOG.md contains an entry matching the version in pyproject.toml for PRs targeting main. This ensures the changelog is always updated before a release.

CI version gates

Pull requests trigger additional version checks:

  • PRs targeting main: Version must not already exist on PyPI, must be greater than the latest published version, and CHANGELOG.md must contain an entry for the version.
  • PRs targeting develop: Version must differ from the version on main (prevents accidental no-op releases).

PyPI trusted publisher setup (one-time)

Before the first release, the repository owner must configure OIDC trusted publishing on PyPI:

  1. Go to https://pypi.org/manage/account/publishing/.
  2. Add a pending publisher:
  3. Project name: pymqrest
  4. Owner: wphillipmoore
  5. Repository: pymqrest
  6. Workflow name: publish.yml
  7. Environment: (leave blank)
  8. The first publish will claim the package name.

See the PyPI trusted publisher documentation for details.

Troubleshooting

Version already exists on PyPI

The publish workflow skips publishing if the version already exists. To release a new version, bump the version in pyproject.toml and go through the release flow again.

Tag already exists

The publish workflow skips tag creation if the tag already exists. This is expected when re-running a failed workflow.

Publish fails

Check the workflow logs for OIDC authentication errors. Ensure the trusted publisher is configured correctly on PyPI. The workflow only triggers on push to main, so the id-token: write permission is scoped to that branch.