-
Notifications
You must be signed in to change notification settings - Fork 11
Add PyPI publishing job and release process #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9d79e3b
acbb578
3b2527c
fba9b76
648029f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| 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 | ||
| --> | ||
| 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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. | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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" | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider:
Suggested change
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" | ||||||
|
|
@@ -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 | ||||||
|
|
||||||
There was a problem hiding this comment.
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