Repository navigation
144 lines (139 loc) · 7.06 KB
/
Copy pathpython-package.yml
File metadata and controls
144 lines (139 loc) · 7.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
name: Build Python packages
# Three jobs. libinchi is per-OS and the wheel is per-OS AND per-interpreter: building the C library
# once per OS and downloading it into the twenty wheel jobs costs four cmake runs instead of twenty.
# `setup.py` calls the same builder and returns early when the binary is already present, so the
# download is all the wheel jobs need -- they never run cmake. The sdist is per-release, so it is
# built once and by itself.
on:
release:
types: [published]
workflow_dispatch:
jobs:
libinchi:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-24.04, ubuntu-24.04-arm]
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Set up CMake
uses: lukka/get-cmake@latest
- name: Set up Python 3.10
# THE OLDEST INTERPRETER IN THE WHEEL MATRIX, and pinned rather than left to the runner's default
# python because on macOS this choice sets the wheel's minimum OS version for every interpreter.
# `build_inchi.py` takes `CMAKE_OSX_DEPLOYMENT_TARGET` and `CMAKE_OSX_ARCHITECTURES` from
# `sysconfig.get_platform()`, and `wheel`'s `calculate_macosx_platform_tag` then RAISES the tag of
# any wheel carrying this dylib to cover it. A dylib stamped with the runner image's macOS
# version tags all five mac wheels with that version; one built for the oldest floor in the matrix
# cannot raise any of them.
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Build libinchi
run: python build_inchi.py
- name: Upload libinchi artifact
uses: actions/upload-artifact@v4
with:
name: libinchi-${{ matrix.os }}
# build/inchi/, not chython/core/: the binary is a build output and no longer enters the source
# tree. `if-no-files-found: error` is what makes a silent no-InChI release impossible -- the
# builder warns and skips when cmake or the submodule are missing, which is right for a
# developer's machine and wrong here.
path: build/inchi/libinchi.*
if-no-files-found: error
binary:
needs: libinchi
runs-on: ${{ matrix.os }}
strategy:
# Every row runs. `--skip-existing` below makes each upload idempotent, so the release is
# published incrementally and re-running the workflow completes it; cancelling nineteen rows over
# one row's failure only drops wheels that would have built, and hides which platforms are wrong.
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-24.04, ubuntu-24.04-arm]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Download libinchi artifact
uses: actions/download-artifact@v4
with:
name: libinchi-${{ matrix.os }}
path: build/inchi/
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip build twine
- name: Build wheel
# PEP 517, because the backend is setuptools: `poetry build` served the poetry backend this
# project used through 2.24 and would now build nothing at all. `build` reads
# `[build-system] requires` and installs Cython itself, so there is no separate install step
# for it.
run: |
python -m build --wheel
- name: Retag the Linux wheel for manylinux
if: runner.os == 'Linux'
# setuptools' `bdist_wheel` emits `linux_x86_64`, which PyPI rejects outright: without this step the
# release fails at upload on Linux only, and on Linux only because every other platform in the
# matrix is tagged acceptably by the build itself.
#
# THE TAG IS SELECTED BY PREFIX, NEVER BY POSITION. `sys_tags()` is ordered best-first and since
# packaging 26.3 the best Linux tag is the native `linux_x86_64` rather than a manylinux one
# (packaging #160), so `next(iter(sys_tags())).platform` yields the tag PyPI rejects while
# `wheel tags` reports success having renamed nothing. It is computed rather than written down so
# that it tracks the runner's glibc instead of freezing 2.39 into a file that lies the day the
# runner image is bumped; `grep -m1` takes the highest, and fails the step where there is no
# manylinux tag at all rather than letting a bare `linux_*` wheel reach twine.
# `chython/test/test_libinchi_staging.py` runs this computation, since a step that merely names
# `manylinux` need not produce one.
#
# `auditwheel repair` is the stronger tool, since it verifies the claim against the binaries rather
# than asserting it; it is not used here because a wheel built on glibc 2.39 and tagged 2.39 cannot
# be overstating its requirement, and because auditwheel would also rewrite libinchi.so, which
# nothing links against and which is loaded by path.
run: |
python -m pip install --upgrade wheel
supported=$(python -c 'from packaging.tags import sys_tags; print(*(t.platform for t in sys_tags()))')
tag=$(echo "$supported" | tr ' ' '\n' | grep -m1 '^manylinux')
echo "retagging as $tag"
python -m wheel tags --remove --platform-tag "$tag" dist/*.whl
- name: Publish package
run: |
twine upload -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} --non-interactive --skip-existing dist/*
sdist:
# WITHOUT THIS THE RELEASE IS WHEELS ONLY, and `pip install chython` outside the matrix above
# answers "no matching distribution": musl, FreeBSD, macOS x86_64, a CPython newer than the one
# this matrix was written for. A source distribution turns each of those into a build from source.
#
# Once, not per matrix cell: an sdist is a property of the source and every cell would produce the
# same file. `MANIFEST.in` is what makes it buildable -- the .pyx and .pxi layers and
# `build_inchi.py`, and not the generated .c, so an install from it cythonizes the sources it ships.
# It does NOT ship the INCHI submodule, so a build from the sdist warns and produces a wheel without
# InChI; `core/__init__.py` falls back silently and that surface raises at the call.
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip build twine
- name: Build sdist
run: |
python -m build --sdist
- name: Check metadata
# The sdist is what PyPI renders the project page from, and a README it cannot render is rejected
# at upload -- for the whole release, since `twine` uploads nothing when one file fails the check.
run: |
twine check dist/*
- name: Publish sdist
run: |
twine upload -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} --non-interactive --skip-existing dist/*