Skip to content

Release Pipeline

A CUTIP release produces a versioned Python wheel published to a GitHub Release and tagged with a git tag. Release branches are temporary — the git tag is the durable version marker.


Trigger

Push to release/v{X.Y.Z} — triggers release.yml.


GitHub Actions: release.yml

Push to release/v{X.Y.Z}
  Build wheel (uv build --wheel)
  Create GitHub Release
      │   - Tag: v{X.Y.Z}
      │   - Uploads wheel as asset
  Release published

The git tag (v{X.Y.Z}) is created by softprops/action-gh-release as part of the release step. Do not tag manually.


Release branch lifecycle

Release branches (release/v{X.Y.Z}) are short-lived:

  1. Created from integration
  2. Pushed to trigger release.yml
  3. Deleted after the GitHub Release is published

Versioned code is preserved via the git tag (v{X.Y.Z}), not via a permanent branch.


Full release sequence

1. Bump version in pyproject.toml
2. Update docs/patch-notes.md
3. PR → staging → merge
4. Forward staging → integration
5. git checkout -b release/v{X.Y.Z} origin/integration
6. git push -u origin release/v{X.Y.Z}
7. Watch release.yml until complete
8. Edit GitHub Release notes (Features + Patch Notes)
9. Remove non-wheel assets from release
10. git branch -d release/v{X.Y.Z}  (tag persists)

See the complete playbook: .claude/workflows/release.md


GitHub Release structure

Release notes use the format from .claude/templates/github-release-notes.md:

## Features
- **cap{N} — {title}**: {user-facing description}

## Patch Notes
- **cap{N} — {title}**: {user-facing description}

## Installation
pip install cutip=={X.Y.Z}
  • ## Featuresfeat/* caps shipped in this release
  • ## Patch Notesbug/* caps shipped in this release
  • No PR numbers, no authors, no GitHub-generated "What's Changed"

Dev Release Track (Test PyPI)

Every merge to integration automatically publishes a dev build to Test PyPI. Issue fixes accumulate in a single installable pre-release version.

Workflow: .github/workflows/integration.yml (the publish-dev job)

Version format: {major}.{minor}.{patch+1}.dev{RUN_NUMBER} — e.g. 0.1.7.dev42

Install:

pip install --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  "cutip>=0.1.7.dev0"

The --extra-index-url flag lets pip resolve dependencies from the main PyPI registry.

Requires secret: TEST_PYPI_TOKEN — add once in repo settings → Secrets → Actions.


Patch notes

docs/patch-notes.md is updated as part of every release. Each entry follows the same structure:

## v{X.Y.Z} ({YYYY-MM-DD})

### Features
- **cap{N}** — {title}: {description}

### Patch Notes
- **cap{N}** — {title}: {description}