Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -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:

- ...

<!-- Contributors since the previous release, with commit counts, in
alphabetical order:
git log --pretty='%an' <previous-tag>..HEAD | sort | uniq -c

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here will be replaced with the last tag after the first release

-->
78 changes: 78 additions & 0 deletions docs/ReleaseProcess.md
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does cmake know what is the current version? Does it need to parse that? We should probably have some notion of a simple VERSION file.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The single source is python/cppjit/_version.py, which the build system reads with the regex declared in pyproject.toml when building the wheel/sdist, and provides cppjit.__version__ at runtime. This is the recommended practice for Python packages: https://packaging.python.org/en/latest/discussions/single-source-version/

cppyy also followed the same practice, setup.py regex-reads python/cppyy/_version.py and cites the same PyPA guide: https://github.com/wlav/cppyy/blob/2d28e09af72dd7f951c341d74266a0c31599d8a8/setup.py#L36

If we need the info in CMake (a build stamp etc.) we can have it read the same file with a one-line string(REGEX MATCH), so I don't think we need a separate VERSION file, that would just be a second copy to keep in sync.

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" <commit>
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.
13 changes: 12 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider:

Suggested change
description = "Automatic Python-C++ interoperability layer and bindings generator"
description = "An automatic PythonC++ interoperability layer and bindings generator built on LLVM's Clang-REPL"

Consider also adding '"exascale", "scientific-computing",' to the keywords.

Consider adding the following topics to the github repo: python, cpp, llvm, clang, clang-repl, jit, jit-compilation, python-bindings, bindings, interoperability, ffi, cppyy, cppinterop

readme = "README.md"
license = "BSD-3-Clause-LBNL"
requires-python = ">=3.12"
Expand All @@ -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"},
Expand Down Expand Up @@ -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
Expand Down
Loading