|
| 1 | +# Making a release |
| 2 | + |
| 3 | +pyzeo ships a Cython-based C++ extension. Following [Cython's |
| 4 | +recommendation](https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#distributing-cython-modules), |
| 5 | +the **pre-generated `src/pyzeo/extension.cpp` is committed and shipped** so end |
| 6 | +users do not need Cython to build from source (`setup.py` sets |
| 7 | +`USE_CYTHON = False`). |
| 8 | + |
| 9 | +Releases are published **manually with `twine`**: bump the version, regenerate |
| 10 | +the C++ from the `.pyx`, build wheels via the `wheels` GitHub Actions workflow, |
| 11 | +then upload the sdist + wheels to PyPI. |
| 12 | + |
| 13 | +## Prerequisites |
| 14 | + |
| 15 | +```sh |
| 16 | +python -m pip install --upgrade pip |
| 17 | +pip install --upgrade setuptools wheel cython build twine |
| 18 | +``` |
| 19 | + |
| 20 | +`setuptools`, `wheel` and `cython` are needed to regenerate the C++ source and |
| 21 | +compile (step 1); `build` and `twine` are used to package and upload (steps 3, |
| 22 | +5). On Python 3.12+ `distutils` was removed from the standard library, so an |
| 23 | +up-to-date `setuptools` (which vendors a `distutils` shim) is required. |
| 24 | + |
| 25 | +- A C++ compiler and the Python development headers (step 1 compiles the |
| 26 | + extension after regenerating the C++). On Debian/Ubuntu, for the Python |
| 27 | + version you build with: `sudo apt install pythonX.Y-dev` (e.g. |
| 28 | + `python3.13-dev`). Without them the build fails with |
| 29 | + `fatal error: Python.h: No such file or directory`. |
| 30 | +- A PyPI account with upload rights to `pyzeo`, configured via `~/.pypirc` or a |
| 31 | + token passed to `twine`. |
| 32 | +- The [`gh`](https://cli.github.com/) CLI authenticated to this repo (used to |
| 33 | + trigger the workflow and download artifacts). |
| 34 | + |
| 35 | +## 1. Regenerate the Cython C++ source |
| 36 | + |
| 37 | +This keeps the shipped `extension.cpp` in sync with the current `extension.pyx` |
| 38 | +and a current Cython. |
| 39 | + |
| 40 | +```sh |
| 41 | +# 1. In setup.py set: USE_CYTHON = True |
| 42 | +python setup.py build_ext --inplace --force |
| 43 | +# 2. Revert setup.py back to: USE_CYTHON = False |
| 44 | +``` |
| 45 | + |
| 46 | +Commit the regenerated `src/pyzeo/extension.cpp`. The `.so` produced is |
| 47 | +gitignored — do **not** commit it. |
| 48 | + |
| 49 | +## 2. Bump the version |
| 50 | + |
| 51 | +Edit `version` in `pyproject.toml`. Update the `requires-python` value and the |
| 52 | +`Programming Language :: Python :: 3.x` classifiers if the supported Python |
| 53 | +range changed (also update the matrix in `.github/workflows/test.yml` and |
| 54 | +`CIBW_SKIP` in `.github/workflows/wheels.yml` accordingly). |
| 55 | + |
| 56 | +## 3. Build & sanity-check the sdist locally |
| 57 | + |
| 58 | +```sh |
| 59 | +python -m build --sdist |
| 60 | +tar tzf dist/pyzeo-<version>.tar.gz | grep extension.cpp # must be present |
| 61 | +``` |
| 62 | + |
| 63 | +Optional but recommended — prove a from-source install compiles without Cython: |
| 64 | + |
| 65 | +```sh |
| 66 | +python -m venv /tmp/pyzeo-test && source /tmp/pyzeo-test/bin/activate |
| 67 | +pip install dist/pyzeo-<version>.tar.gz |
| 68 | +cd tests && pytest |
| 69 | +deactivate |
| 70 | +``` |
| 71 | + |
| 72 | +> Note: only `extension.cpp` (not `.pyx`/`.pxd`) ships in the sdist. The bundled |
| 73 | +> `.cc`/`.h`/`.hh`/Eigen files come from the `setup.py` Extension sources + |
| 74 | +> `MANIFEST.in`. |
| 75 | +
|
| 76 | +## 4. Build the wheels (CI) |
| 77 | + |
| 78 | +Push the version bump + regenerated `.cpp` to `main` (or a release branch and |
| 79 | +merge it). Then trigger the **wheels** workflow — it is `workflow_dispatch`-only: |
| 80 | + |
| 81 | +```sh |
| 82 | +gh workflow run wheels.yml --ref main |
| 83 | +``` |
| 84 | + |
| 85 | +(Or via the GitHub Actions UI → "wheels" → Run workflow.) |
| 86 | + |
| 87 | +When the run finishes, download the built wheels into `dist/` (one artifact per |
| 88 | +OS: ubuntu, macos-13, macos-14): |
| 89 | + |
| 90 | +```sh |
| 91 | +gh run download <run-id> -n cibw-wheels-* -D dist/ |
| 92 | +``` |
| 93 | + |
| 94 | +## 5. Upload to PyPI |
| 95 | + |
| 96 | +```sh |
| 97 | +twine check dist/pyzeo-<version>* # validate sdist + wheels |
| 98 | +twine upload dist/pyzeo-<version>.tar.gz dist/pyzeo-<version>-*.whl |
| 99 | +``` |
| 100 | + |
| 101 | +Upload the sdist and all wheels together. To rehearse first, upload to TestPyPI: |
| 102 | +`twine upload --repository testpypi dist/pyzeo-<version>*`. |
| 103 | + |
| 104 | +## 6. Tag the release |
| 105 | + |
| 106 | +```sh |
| 107 | +git tag v<version> |
| 108 | +git push origin v<version> |
| 109 | +gh release create v<version> --generate-notes # optional GitHub Release |
| 110 | +``` |
| 111 | + |
| 112 | +## Checklist |
| 113 | + |
| 114 | +- [ ] Regenerated `extension.cpp` (USE_CYTHON True → build → False) and committed it |
| 115 | +- [ ] Bumped `version` in `pyproject.toml` (+ classifiers / Python range if changed) |
| 116 | +- [ ] `python -m build --sdist`; confirmed `extension.cpp` is in the tarball |
| 117 | +- [ ] (Optional) clean-venv install of the sdist + `pytest` passes |
| 118 | +- [ ] Pushed to `main`, ran `gh workflow run wheels.yml`, downloaded artifacts |
| 119 | +- [ ] `twine check` + `twine upload` of sdist and all wheels |
| 120 | +- [ ] Tagged `v<version>` and pushed the tag |
0 commit comments