Skip to content

Commit 0cca060

Browse files
committed
Pypi release pipeline: design documents
design-docs/adr/0006-codetracer-python-recorder-pypi-release.md: design-docs/codetracer-python-recorder-pypi-release-implementation-plan.md: Signed-off-by: Tzanko Matev <[email protected]>
1 parent 0e6c7fd commit 0cca060

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# ADR 0006: PyPI Release Strategy for `codetracer-python-recorder`
2+
3+
- **Status:** Proposed
4+
- **Date:** 2025-10-13
5+
- **Deciders:** Codetracer Runtime & Tooling Leads
6+
- **Consulted:** Release Engineering, Developer Experience, Platform Reliability
7+
- **Informed:** Product Management, Support, Security
8+
9+
## Context
10+
11+
We are ready to publish the first public release of the Rust-backed `codetracer-python-recorder`
12+
module to PyPI. The business goal is to distribute an officially supported recording module for
13+
Python 3.12 and 3.13 across Linux, macOS, and Windows, with both binary wheels and a source
14+
distribution that downstream teams can audit and rebuild.
15+
16+
A repository review shows that:
17+
18+
- Packaging metadata in `codetracer-python-recorder/pyproject.toml` still targets Python 3.8+,
19+
omits Trove classifiers for supported versions and platforms, and does not expose project URLs
20+
or the README as the long description.
21+
- The `Justfile` and CI only support developer builds (`maturin develop`) and Linux test jobs;
22+
there is no automated release workflow and no multi-platform wheel builds.
23+
- `tool.maturin` configuration does not yet declare source-distribution rules, exclude build
24+
artefacts, or ensure that `Cargo.toml` / `pyproject.toml` versions stay synchronized.
25+
- There is no documented path for staging releases on TestPyPI or for securely authenticating a CI
26+
workflow to PyPI.
27+
28+
We consulted the Python Packaging User Guide packaging flow
29+
([packaging.python.org](https://packaging.python.org/en/latest/flow/)) and the maturin distribution
30+
guide ([maturin.rs](https://www.maturin.rs/distribution.html)). Key takeaways include:
31+
32+
- Every release must produce both wheels and an sdist, validate metadata with tooling, and verify
33+
installability before uploading.
34+
- PyPI now recommends OpenID Connect “Trusted Publishing” for CI-driven uploads instead of storing
35+
long-lived API tokens.
36+
- maturin provides first-class support for building manylinux, macOS universal2, and Windows wheels,
37+
plus TestPyPI/PyPI uploads from CI runners.
38+
39+
## Decision
40+
41+
1. **Metadata hardening:** Update `pyproject.toml` to `requires-python = ">=3.12,<3.14"`, include
42+
the project README as the long description, add platform-specific Trove classifiers (Linux,
43+
macOS, Windows; CPython 3.12/3.13; Rust), and publish canonical URLs (homepage, repository,
44+
issues). Mirror the version in `Cargo.toml` and add a guard that compares Rust and Python
45+
versions during CI.
46+
2. **Distribution artefacts:** Continue to use maturin as the build backend. For the initial public
47+
release, build per-version wheels (`cp312` and `cp313`) for
48+
- manylinux2014 `x86_64` and `aarch64`,
49+
- macOS universal2 (`x86_64` + `arm64`),
50+
- Windows `amd64`.
51+
Produce a source distribution via `maturin sdist`, ensuring `Cargo.lock`, Rust sources, and Python
52+
shim modules are included while excluding wheel artefacts (`target/`, compiled `.so` files).
53+
Evaluate enabling the `pyo3/abi3-py312` feature after the first release to collapse per-version
54+
wheels.
55+
3. **Pre-release verification:** Extend the release pipeline to run unit tests against the built
56+
artefacts, execute smoke installs (`pip install` from the local wheel and sdist), and run the CLI
57+
(`python -m codetracer_python_recorder --help`) before any upload step.
58+
4. **Trusted publishing workflow:** Create a dedicated GitHub Actions workflow triggered by
59+
annotated tags (e.g., `recorder-v*`). The workflow will:
60+
- Build and test wheels on each platform matrix job.
61+
- Upload artefacts to a staging job that performs TestPyPI publishing via maturin.
62+
- Require a manual approval (environment protection) before promoting the same artefacts to the
63+
production PyPI repository.
64+
Configure the PyPI project as a Trusted Publisher for the repository so uploads rely on OIDC
65+
tokens instead of stored secrets.
66+
5. **Versioning & change management:** Adopt a documented version-bump process that updates both
67+
`pyproject.toml` and `Cargo.toml`, updates the changelog/release notes, and tags releases using
68+
`recorder-vMAJOR.MINOR.PATCH`. Enforce semantic-versioning semantics via review, and block
69+
accidental reuse of versions by asserting that the requested tag matches the metadata.
70+
6. **Documentation & support:** Add a release checklist to the repository (under `design-docs`) that
71+
references PyPI’s packaging flow steps (metadata validation, TestPyPI verification, final
72+
release), and document how downstream consumers install the wheel (platform notes, supported
73+
Python versions).
74+
75+
## Alternatives Considered
76+
77+
- **Manual, developer-driven uploads:** Rejected because manual wheel builds are error-prone,
78+
difficult to reproduce across platforms, and conflict with PyPI’s recommendation to use trusted
79+
CI publishing.
80+
- **cibuildwheel-backed workflow:** Considered but rejected for this release; maturin already owns
81+
the build backend, integrates with PyO3, and provides the required cross-platform coverage with
82+
less indirection.
83+
- **Limiting support to Linux and source distributions:** Rejected because product requirements call
84+
for macOS and Windows parity from day one, and maturin’s cross-platform build support removes the
85+
main barrier to doing so.
86+
87+
## Consequences
88+
89+
- **Positive:** Reproducible, tested release artefacts; reduced risk of shipping mismatched metadata;
90+
streamlined release process that matches the official packaging flow; improved supply-chain
91+
posture through OIDC trusted publishing.
92+
- **Negative:** Longer CI pipelines (multi-platform builds) and the operational overhead of
93+
configuring PyPI Trusted Publishing. macOS universal2 builds increase macOS runner usage, and
94+
cross-compiling for `aarch64` may extend Linux build times.
95+
- **Risks & Mitigations:** maturin cross-build failures are possible—mitigate by caching Rust
96+
dependencies and adding smoke tests that catch platform-specific regressions early. If Trusted
97+
Publishing is unavailable during initial setup, fall back temporarily to a scoped PyPI API token
98+
stored in GitHub environments while tracking completion of the OIDC configuration.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# `codetracer-python-recorder` PyPI Release – Implementation Plan
2+
3+
This plan captures the work required to implement ADR 0006 (“PyPI Release Strategy for
4+
`codetracer-python-recorder`”) and prepare the project for automated publishing.
5+
6+
---
7+
8+
## Workstream 1 – Package metadata & repository hygiene
9+
10+
1. **Tighten project metadata**
11+
- Update `codetracer-python-recorder/pyproject.toml` with `requires-python = ">=3.12,<3.14"`,
12+
`readme = "README.md"`, Trove classifiers for CPython 3.12/3.13 and the three target
13+
platforms, and `project.urls` entries (`Homepage`, `Repository`, `Issues`, `Changelog`).
14+
- Ensure licensing information references the repository MIT license file.
15+
2. **Synchronize Rust/Python versions**
16+
- Add a repository script (e.g., `scripts/check_recorder_version.py`) that verifies the version in
17+
`pyproject.toml` matches `Cargo.toml`.
18+
- Wire the check into CI (pull requests and release workflow) so mismatches fail fast.
19+
3. **Curate source-distribution contents**
20+
- Expand `[tool.maturin.sdist]` to include Rust sources, `Cargo.lock`, and Python shim modules
21+
while excluding build artefacts (`target/`, cached wheels, compiled `.so` files).
22+
- Add a `.gitignore` entry for `codetracer_python_recorder/*.so` if not already covered.
23+
4. **Document supported environments**
24+
- Update `codetracer-python-recorder/README.md` with explicit notes on supported Python versions,
25+
OS targets, and a short install section showing `pip install codetracer-python-recorder`.
26+
- Add a release checklist under `design-docs/` describing version bumps, tagging, and the TestPyPI
27+
verification gate.
28+
29+
## Workstream 2 – Build & test enhancements
30+
31+
1. **Augment local build tooling**
32+
- Extend `Justfile` targets (`build`, `build-all`) to invoke `maturin build --release --sdist`
33+
for the configured Python interpreters (3.12, 3.13) and to drop artefacts under
34+
`codetracer-python-recorder/target`.
35+
- Provide a `just smoke-wheel` recipe that creates a virtual environment, installs the freshly
36+
built wheel/sdist, and runs `python -m codetracer_python_recorder --help`.
37+
2. **Strengthen automated tests**
38+
- Ensure existing Rust `cargo nextest` and Python `pytest` suites run as part of every release
39+
build.
40+
- Add an integration smoke test that exercises `start_tracing` and `stop_tracing` through the
41+
Python facade with temporary directories to guard against obvious regressions.
42+
3. **Cache dependencies**
43+
- Configure `maturin` to use `--locked` builds and enable Rust/Python dependency caching (UV,
44+
Cargo) in CI to keep build times predictable across platforms.
45+
46+
## Workstream 3 – Cross-platform build & publish automation
47+
48+
1. **Create release workflow**
49+
- Add `.github/workflows/recorder-release.yml` triggered by annotated tags (`recorder-v*`) and a
50+
manual `workflow_dispatch`.
51+
- Define a matrix for:
52+
- Linux: `ubuntu-latest` using `messense/maturin-action` to build manylinux2014 `x86_64` and
53+
`aarch64` wheels.
54+
- macOS: `macos-13` to build universal2 wheels via `maturin build --universal2`.
55+
- Windows: `windows-latest` to build `win_amd64` wheels.
56+
- Run `just test` (or explicit Rust/Python test commands) before building artefacts on every job.
57+
2. **Stage artefacts**
58+
- Upload all wheels and the sdist as GitHub Actions artefacts.
59+
- Add a downstream job that collects the artefacts, performs `pip install` smoke tests on Linux,
60+
and publishes to TestPyPI using `maturin upload --repository testpypi`.
61+
3. **Trusted publishing setup**
62+
- Register the GitHub repository as a PyPI Trusted Publisher, mapping the release workflow to the
63+
PyPI project.
64+
- Configure GitHub environments to require manual approval (“Promote to PyPI”) and run
65+
`maturin upload --repository pypi` only after TestPyPI verification succeeds.
66+
- Document fallback instructions for using scoped API tokens if Trusted Publishing cannot be
67+
completed before the first release.
68+
69+
## Workstream 4 – Operational readiness & documentation
70+
71+
1. **Release management**
72+
- Prepare a CHANGELOG entry for v0.1.0 (or the first publicly published version) following
73+
Conventional Commits.
74+
- Document how to create and push annotated tags (`git tag -a recorder-vX.Y.Z`).
75+
2. **Post-publish verification**
76+
- Describe the post-release validation steps (download from PyPI on each platform, run CLI smoke
77+
tests, monitor PyPI stats).
78+
- Create an issue template for future release tracking that references the checklist.
79+
80+
---
81+
82+
**Exit criteria:** The new release workflow successfully publishes a TestPyPI build from CI, a human
83+
approves promotion to PyPI via the protected environment, and the published package installs and
84+
passes smoke tests on Linux, macOS, and Windows for Python 3.12 and 3.13.

0 commit comments

Comments
 (0)