diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 0000000..dff8e31 --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,61 @@ +name: PyPI + +# Publish a Wheels run to PyPI. The workflow is registered as the +# project's trusted publisher on pypi.org, so the job mints a short-lived +# OIDC token and stores no secrets. The release ritual around it is +# documented in docs/ReleaseProcess.md. +# +# Two triggers: +# - A finished Wheels run for a v* tag push publishes automatically: the +# tag is the release act, tag runs always build the full python list, +# and the pypi environment carries any reviewer approval configured +# for it. +# - workflow_dispatch with an explicit run id: the manual fallback for +# recovery and re-publishing. +on: + workflow_run: + workflows: [Wheels] + types: [completed] + workflow_dispatch: + inputs: + run-id: + description: Run id of a green full Wheels run + required: true + +permissions: + contents: read + +jobs: + publish: + # Wheels push runs come only from main and v* tags, so the branch + # prefix test selects exactly the tag runs. + if: >- + ${{ github.event_name == 'workflow_dispatch' + || (github.event.workflow_run.conclusion == 'success' + && github.event.workflow_run.event == 'push' + && startsWith(github.event.workflow_run.head_branch, 'v')) }} + runs-on: ubuntu-24.04 + environment: pypi + permissions: + id-token: write + actions: read + steps: + - uses: actions/download-artifact@v8 + with: + pattern: wheels-* + merge-multiple: true + path: dist + run-id: ${{ inputs.run-id || github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - uses: actions/download-artifact@v8 + with: + name: sdist + path: dist + run-id: ${{ inputs.run-id || github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - name: List the distributions before upload + run: ls -l dist/ + + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md new file mode 100644 index 0000000..b8b7e22 --- /dev/null +++ b/docs/ReleaseNotes.md @@ -0,0 +1,43 @@ +# cppjit 0.1.0a1 Release Notes + +This document contains the release notes for cppjit 0.1.0a1, an +automatic Python-C++ interoperability and bindings package built on +[CppInterOp](https://github.com/compiler-research/CppInterOp) and the +[LLVM](https://llvm.org) compiler infrastructure. If you are reading +this file from a git checkout, it describes the *next* release, not +the current one. The notes of previously released versions are +archived on the +[releases page](https://github.com/compiler-research/cppjit/releases). + +This release supports Python 3.12-3.14 and LLVM 21-22. + +## Highlights + +- ... + +## New features + +- ... + +## Deprecated features + +- ... + +## Backwards incompatible changes + +- ... + +## Other changes + +- ... + +## Contributors + +Special thanks to everyone who contributed to this release: + +- ... + + diff --git a/docs/ReleaseProcess.md b/docs/ReleaseProcess.md new file mode 100644 index 0000000..a324f5c --- /dev/null +++ b/docs/ReleaseProcess.md @@ -0,0 +1,78 @@ +# Release Process + +A cppjit release is a git tag on main. The tag push builds the wheels, +and the PyPI workflow uploads what that build produced. Future releases +will extend this to more distribution channels like conda. + +## Procedure + +1. Open the release PR: + - Set `__version__` in `python/cppjit/_version.py` to the release + version. + - Fill in `docs/ReleaseNotes.md`. + - Add the `build-wheels` label to build the complete set of wheels + on the PR. + +2. Squash-merge the PR and tag the resulting commit on main: + + ```bash + git tag -a vX.Y.Z -m "cppjit X.Y.Z" + git push origin vX.Y.Z + ``` + + The tag push runs the Wheels workflow with every supported Python + on every platform. + +3. When that run succeeds, the PyPI workflow uploads its wheels and + sdist. If the `pypi` environment has required reviewers, the + upload waits for their approval. A failed run uploads nothing: fix + the problem and re-tag, or, if only the upload failed, re-run it + through the manual fallback below. + +4. Create the GitHub release for the tag. The body is the finalized + `docs/ReleaseNotes.md`. The attached artifacts are the uploaded + wheels and sdist. + +5. Announce the release on the compiler-research website and the + relevant mailing lists. + +6. Open the post-release PR. It sets `_version.py` to the next + version with a `.dev0` suffix (e.g., `0.1.0a2.dev0` follows + `0.1.0a1`) and resets `docs/ReleaseNotes.md`. + +## Version and metadata + +`python/cppjit/_version.py` is the source of truth for the version. +scikit-build-core reads it at build time through the +`[[tool.dynamic-metadata]]` entry in `pyproject.toml`. PyPI uses the +information in `pyproject.toml` to build the project page. The +versioning scheme follows PEP 440, and between releases the version +carries a `.dev0` suffix. + +## Release notes + +`docs/ReleaseNotes.md` collects notes for the release under +development. The release PR completes them and the post-release PR +resets them, so a git checkout always describes the next release. +The notes of a released version live on in its GitHub release. + +PyPI shows no release notes. It renders the README that was built +into each version, and the `Changelog` URL in `[project.urls]` leads +from there to the GitHub releases page. + +## The PyPI workflow + +`.github/workflows/pypi.yml` runs when the Wheels run for a `v` tag +completes. On success it downloads that run's wheels and sdist and +uploads them to PyPI, so what lands there is exactly what CI built +and tested. The upload uses trusted publishing: the job authenticates +with a short-lived OIDC token, and the repository stores no +credential. GitHub runs the copy of `pypi.yml` on main, so a change +to the workflow takes effect once it merges. + +The manual fallback is a `workflow_dispatch` of the same workflow +with the run id of a successful full Wheels run. + +PyPI accepts each version once, and deleting an upload does not free +its number. A broken release is replaced by the next patch or +prerelease number. diff --git a/pyproject.toml b/pyproject.toml index 123e305..0dd5fb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "scikit_build_core.build" [project] name = "cppjit" dynamic = ["version"] -description = "CppJIT: fast and automatic Python-C++ interoperability" +description = "Automatic Python-C++ interoperability layer and bindings generator" readme = "README.md" license = "BSD-3-Clause-LBNL" requires-python = ">=3.12" @@ -26,6 +26,13 @@ classifiers = [ "Topic :: Software Development :: Interpreters", "Topic :: Software Development :: Libraries :: Python Modules", ] +keywords = [ + "cplusplus", + "interoperability", + "bindings-generator", + "llvm-jit", + "scientific-computing", +] authors = [ {name = "Wim Lavrijsen"}, {name = "Vassil Vassilev"}, @@ -93,6 +100,10 @@ xfail_strict = true [tool.ruff] show-fixes = true +[tool.ruff.format] +# the README's code fences hand-align their annotation comments +exclude = ["*.md"] + [tool.ruff.lint] # pyflakes/pycodestyle errors plus import sorting; wider rule sets are a # post-squash cleanup