-
Notifications
You must be signed in to change notification settings - Fork 0
ReleaseProcess
Maintainer-facing reference for cutting a convert-python-sdk release. The
release pipeline is fully tag-driven: pushing v<X.Y.Z> to main triggers
the GitHub Actions workflow that re-runs CI, builds, publishes to PyPI via
OIDC Trusted Publishing, and creates a GitHub Release with the compiled
changelog.
.github/workflows/ci.yml enforces the following on every PR and push:
| Gate | Failure mode |
|---|---|
| Ruff lint | Blocks merge. |
mypy strict (src/) |
Blocks merge. |
| pytest matrix (15 cells) | Blocks merge per failing cell. |
Parity suite (tests/parity) |
Blocks merge — non-negotiable. |
Project coverage >= 85%
|
Blocks merge. |
evaluation/ coverage >= 95%
|
Blocks merge. |
| Bounds-check (lower & upper) | Blocks merge per failing extreme. |
| Changelog fragment (PRs only) | Blocks PR if no changes/<n>.<category>.md. |
| Build (wheel + sdist) | Blocks merge. |
The matrix targets Python 3.9 → 3.13 across ubuntu-latest, macos-latest,
and windows-latest. pytest is pinned to the 8.4.x line because pytest
9.x drops Python 3.9 support.
UV_CACHE_DIR=/tmp/uv-cache uv run python scripts/verify_release.pyThis reproduces every CI gate locally (lint, types, full test run with both
coverage gates, parity suite, uv build). It exits non-zero on the first
failure and prints the failing command.
-
Runtime deps in
pyproject.tomlalways carry compatible-release bounds (>=X,<Y). Currentlyhttpx>=0.28,<0.29is the only runtime dependency (the SDK ships a pure-Python MurmurHash3 — nommh3runtime dep). -
Exact lower-bound pins live only in
ci/lower-bounds-overrides.txt. The bounds-check CI job uses this file to install the oldest supported version of each runtime dep and re-runs the test suite. Both extremes must pass to merge. -
Widening bounds is a deliberate maintainer action. Land it in a
focused PR with a
changes/<n>.internal.md(orbreaking/bugfixif user-visible) fragment that explains the rationale.
Every PR with user-visible behavior must add a fragment to changes/
(see changes/README.md for the naming convention). Fragments are
compiled by towncrier build --yes --version <version> at release time;
do not hand-edit CHANGELOG.md above the towncrier marker.
Internal-only PRs (CI, refactors, dependency bumps without behavior
changes) should still add a changes/<n>.internal.md fragment to keep
the missing-fragment CI gate green.
The end-to-end release path:
-
Open a release PR from
dev-branchtomaincontaining only the version bump inpyproject.toml. CI must be green. -
Land the PR so
maincarries the bumped version. -
Tag and push:
git checkout main git pull git tag -a v0.1.0 -m "Release 0.1.0" git push origin v0.1.0 -
Watch the release workflow at
https://github.com/convertcom/python-sdk/actions/workflows/release.yml. It re-runs CI, runstowncrier buildto compile fragments intoCHANGELOG.md, builds wheel + sdist, publishes to PyPI through OIDC Trusted Publishing, and creates a GitHub Release with the extracted notes. - Verify the artifact on PyPI and the GitHub Releases page.
Tags matching vX.Y.Z(a|b|rc|dev)N (e.g., v0.2.0rc1) are published with
the GitHub --prerelease flag and are surfaced on PyPI as pre-releases.
Trusted Publishing on PyPI is configured per-project through the project
settings page on pypi.org. The publisher must reference:
| Field | Value |
|---|---|
| PyPI project | convert-python-sdk |
| Owner | convertcom |
| Repository | python-sdk |
| Workflow | release.yml |
| Environment | pypi |
No long-lived PyPI token is ever stored as a repository secret; the OIDC
exchange happens during the publish job (id-token: write).
Parity fixtures are checked in so CI does not require a Node.js runtime at test time. Regeneration is a maintainer action when the JavaScript SDK changes:
# From convert-python-sdk repo root, with the sibling javascript-sdk
# repo checked out at ../javascript-sdk on the desired commit.
UV_CACHE_DIR=/tmp/uv-cache uv run python scripts/generate_parity_fixtures.pyInspect the resulting git diff tests/parity/fixtures/ to confirm the
vector changes are intentional, then land them in a PR with a
changes/<n>.internal.md fragment recording the JS SDK commit they came
from.
-
OIDC
403from PyPI — The Trusted Publisher entry on PyPI does not match the repo/workflow. Confirm owner, repo, workflow filename, and environment. -
Coverage regressed below the gate — Inspect
term-missingoutput for the affected file and add direct unit tests. Theevaluation/gate is independent of the project gate; both must pass. -
Bounds-check failure on
lower— A new transitive constraint added upstream is incompatible with our declared minimum. Either pin a higher lower-bound inpyproject.toml(and add abreaking/internalfragment) or vendor a workaround. -
Changelog-fragment check fails on a PR that genuinely has no
user-visible effect — Add a
changes/<pr-number>.internal.mdwith one short sentence describing the maintenance.
Copyrights © 2025 All Rights Reserved by Convert Insights, Inc.
Getting Started
Python SDK
- Quickstart
- Installation
- Initialization
- Configuration
- Code Examples
- Type Hints
- Diagnostics
- Extending
- Testing
- Async & Frameworks
Migration
Core Concepts
- Experiences & Variations
- Feature Flags
- Bucketing Algorithm
- Rule Evaluation
- Segments
- Data Management
- Event System
- API Communication
How-To Guides
- Running Experiences
- Running Features
- Tracking Conversions
- Visitor Context
- Persistent DataStore
- Troubleshooting
Edge & Integrations
Maintainers