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
76 changes: 75 additions & 1 deletion .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
# library and the standalone driver. The x86-64 wheels target x86-64-v3 (AVX2), as the static
# Windows release does. Each wheel is installed into a clean environment and
# smoke-tested (release/smoke_test_wheel.py) on its own platform. The distributions are workflow
# artifacts; publishing them is a separate, explicit step.
# artifacts. They are uploaded only by the two publish jobs, through PyPI Trusted Publishing
# (no stored token): to TestPyPI when a manual run asks for it, and to PyPI when a GitHub
# release is published, after checking that the distributions carry the release tag's version.
name: wheels

on:
workflow_dispatch:
inputs:
upload:
description: Upload the built distributions (TestPyPI only; PyPI uploads come from releases)
type: choice
options: [none, testpypi]
default: none
release:
types: [published]
pull_request:
Expand Down Expand Up @@ -189,3 +197,69 @@ jobs:
name: cabledyn-wheel-windows-x86_64
path: wheelhouse/*.whl
if-no-files-found: error

publish-testpypi:
# manual runs that ask for it; a version already on TestPyPI is skipped, not an error
if: github.event_name == 'workflow_dispatch' && inputs.upload == 'testpypi'
needs: [sdist, linux, macos, windows]
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/cabledyn
permissions:
id-token: write
steps:
- name: Download the distributions
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: cabledyn-*
merge-multiple: true
path: dist

- name: Check the distribution set
run: |
ls -l dist
test "$(ls dist/*.tar.gz | wc -l)" -eq 1
test "$(ls dist/*.whl | wc -l)" -eq 3

- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
with:
repository-url: https://test.pypi.org/legacy/
skip-existing: true

publish-pypi:
# published GitHub releases only, behind the pypi environment
if: github.event_name == 'release'
needs: [sdist, linux, macos, windows]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/cabledyn
permissions:
id-token: write
steps:
- name: Download the distributions
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: cabledyn-*
merge-multiple: true
path: dist

- name: Check the distributions against the release tag
env:
TAG: ${{ github.event.release.tag_name }}
run: |
ls -l dist
version="${TAG#v}"
test "$(ls dist/*.tar.gz | wc -l)" -eq 1
test "$(ls dist/*.whl | wc -l)" -eq 3
for f in dist/*; do
case "$(basename "$f")" in
"cabledyn-${version}.tar.gz"|"cabledyn-${version}-py3-none-"*.whl) ;;
*) echo "::error::$(basename "$f") is not version ${version} of release ${TAG}"; exit 1 ;;
esac
done

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ All notable changes to CableDyn are recorded in this file. The format follows
- Release wheels built in CI (`.github/workflows/wheels.yml`): Linux x86-64 (`manylinux_2_28`,
x86-64-v3), macOS arm64 and Windows x86-64 (IFX, static, the release's reference LAPACK), plus
the sdist. Each wheel is installed into a clean environment and smoke-tested
(`release/smoke_test_wheel.py`); publishing is a separate step.
(`release/smoke_test_wheel.py`). The same workflow publishes them through PyPI Trusted
Publishing: to TestPyPI on request from a manual run, and to PyPI when a GitHub release is
published (after checking the release tag's version and approval of the `pypi` environment).

### Changed

Expand Down
7 changes: 5 additions & 2 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,11 @@ A wheel built this way still needs the toolchain's runtime and LAPACK libraries
search path; the release wheels bundle them (or link them statically on Windows).

The release wheels come from `.github/workflows/wheels.yml` (manual runs, published
releases, and pull requests that touch the packaging), as workflow artifacts; publishing
them is a separate step. It builds the sdist (checked with `twine`) and three wheels:
releases, and pull requests that touch the packaging), as workflow artifacts, which only
its publish jobs upload (TestPyPI from a manual run with **upload = testpypi**, PyPI from a
published release; see
[RELEASE.md](https://github.com/SMI-Lab-Inha/CableDyn/blob/main/RELEASE.md)). It builds
the sdist (checked with `twine`) and three wheels:

| Wheel | Toolchain | Runtime and LAPACK | Build |
|---|---|---|---|
Expand Down
28 changes: 27 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ manually (`workflow_dispatch`) as a preflight. Download its
`CableDyn-release-assets` artifact, verify every checksum (on a second machine
where practical), and keep the workflow URL with the release record.

The PyPI distribution (the sdist and the Linux, macOS and Windows wheels that carry
the library and the driver) comes from `.github/workflows/wheels.yml`. As its
preflight, run that workflow manually with **upload = testpypi**: it builds and
smoke-tests the four distributions and uploads them to TestPyPI (a version already
there is skipped). Then, in a clean environment on each platform you can reach:

```powershell
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "cabledyn==MAJOR.MINOR.PATCH"
python release/smoke_test_wheel.py examples/dynamic_chain_current.dat --expect-build x86-64-v3
```

(`--expect-build baseline` on macOS arm64.)

## 5. Tag and publish

Tag only the commit that passed every check:
Expand All @@ -141,9 +154,16 @@ partial release or invalid manifest fails without replacing any file. If assets
not match, withdraw the release and investigate; never replace a file without a
documented correction.

Publishing also starts `.github/workflows/wheels.yml`, which rebuilds and smoke-tests
the PyPI distributions from the tag, checks that every file carries the tag's
version, and then waits for approval of the `pypi` environment. Approve it only
after the release assets above check out: a version uploaded to PyPI can be yanked
but never replaced.

## 6. Close the release

Confirm that the release page lists all five assets, the documentation builds from
Confirm that the release page lists all five assets, <https://pypi.org/project/cabledyn/>
lists the sdist and the three wheels of the release, the documentation builds from
the tag, the GitHub source archives contain no credentials or run output, and a
fresh clone passes the fast test tier. Then add an empty `[Unreleased]` section to
the changelog.
Expand All @@ -162,6 +182,12 @@ Before the first release is published from a new GitHub repository:
- [ ] **Actions**: allow GitHub-hosted runners; the workflows need no secrets
beyond the automatic `GITHUB_TOKEN`, and only the release job writes
(`contents: write`).
- [ ] **PyPI**: on <https://pypi.org> and <https://test.pypi.org>, register a
(pending) Trusted Publisher for the project `cabledyn`: this repository, the
workflow `wheels.yml`, and the environments `pypi` and `testpypi`
respectively. Create both environments in the repository settings and make
a maintainer a required reviewer of `pypi`. The publish jobs then need no
token (`id-token: write` only).
- [ ] **Branch protection** on `main` and `dev`: require pull requests, the `fortran` and
`python` workflow checks, and linear history.
- [ ] **Read the Docs**: import the repository as project `cabledyn`, build
Expand Down
Loading