forked from applied-material-modeling/neml2
-
Notifications
You must be signed in to change notification settings - Fork 0
312 lines (301 loc) · 11.7 KB
/
Copy pathpython.yaml
File metadata and controls
312 lines (301 loc) · 11.7 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
name: Python package
on:
push:
branches: [main]
pull_request:
branches: [main]
release:
types:
- published
# Newer commits should cancel old runs
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
# dependencies: python.version_min
python-version: "3.10"
- name: Install nmhit (provides the nmhit-format CLI used by pre-commit)
# dependencies: nmhit.version_min
run: pip install 'nmhit>=0.2.1'
- name: Run ruff lint
uses: pre-commit/action@v3.0.1
with:
extra_args: ruff --all-files
- name: Run ruff format check
uses: pre-commit/action@v3.0.1
with:
extra_args: ruff-format --all-files
- name: Run nmhit-format check
uses: pre-commit/action@v3.0.1
with:
extra_args: nmhit-format --all-files
typecheck:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/neml2-ci
with:
# dependencies: python.version_min
python-version: "3.10"
# dependencies: cmake.version_min
cmake-version: "3.26.1"
# dependencies: torch.version
torch-version: "2.12.1"
cpu-torch: true
- name: Install (editable)
# Editable install is load-bearing for this job: pyright type-checks
# the in-tree ``./neml2/`` (per ``[tool.pyright].include``), and
# ``neml2-stub`` writes ``_aoti.pyi`` next to the .so it discovers.
# A non-editable install would place both the .so and the generated
# .pyi under ``site-packages/neml2/aoti/`` -- outside pyright's
# include list -- so the ``from ._aoti import Model`` line in
# ``neml2/aoti/__init__.py`` would fail with "could not be
# resolved". Editable install lets scikit-build-core place the .so
# into ``./neml2/aoti/`` directly; the stub then lands beside it
# where pyright sees it.
run: pip install -e ".[dev]" -v
- name: Regenerate pybind11 stubs
# pybind11 extensions ship as .so; pyright can't introspect them
# without a .pyi sibling. The `neml2-stub` console script (added
# to [project.scripts]) wraps pybind11-stubgen and writes the
# stubs next to each .so. Run AFTER `pip install` so the
# package is fully importable.
run: neml2-stub
- name: Run pyright
run: pyright
vuln-scan:
# ``pip-audit`` resolves installed packages against the PyPI advisory
# database (a mirror of OSV.dev). It flags both direct and transitive
# dependencies; running it against the installed editable env catches
# any CVE in the pinned torch, pyzag, sphinx, etc. before merge.
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/neml2-ci
with:
# dependencies: python.version_min
python-version: "3.10"
# dependencies: cmake.version_min
cmake-version: "3.26.1"
# dependencies: torch.version
torch-version: "2.12.1"
cpu-torch: true
- name: Install
run: pip install ".[dev]" -v
- name: Install pip-audit
run: pip install pip-audit
- name: Scan installed env for known CVEs
# ``--vulnerability-service osv`` queries OSV.dev rather than the
# PyPI advisory mirror; OSV resolves packages installed from non-PyPI
# indexes (torch's ``+cpu`` / ``+cu126`` local-version specifiers)
# which the PyPI service rejects with "dependency not found".
# ``--strict`` exits non-zero on any vulnerability *or* dependency
# resolution skip; ``--ignore-vuln`` slots in here as needed when a
# flagged CVE has no realistic exploit path for this codebase.
run: pip-audit --vulnerability-service osv --strict
coverage:
# Python branch + line coverage. Runs the fast lane only -- the
# regression and verification suites pin gold outputs rather than
# exercise code paths, so including them would only inflate the
# coverage number with end-to-end exercises (and triple the CI
# wall time). The xdist ``-n auto`` matches what the AOTI sweep
# benchmark showed scales best on a multi-core box; ``parallel =
# true`` in pyproject.toml's [tool.coverage.run] keeps per-worker
# .coverage files distinct and merges them cleanly.
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/neml2-ci
with:
# dependencies: python.version_min
python-version: "3.10"
# dependencies: cmake.version_min
cmake-version: "3.26.1"
# dependencies: torch.version
torch-version: "2.12.1"
cpu-torch: true
- name: Install
run: pip install ".[dev]" -v
- name: Run tests with coverage
run: |
pytest -n auto --cov --cov-report=xml --cov-report=term \
--ignore=tests/regression --ignore=tests/verification \
tests/
- name: Upload coverage to Codecov
# Codecov rejects tokenless OIDC uploads on protected branches,
# so the workflow always passes ``CODECOV_TOKEN`` (a repository
# secret, generated from the project's Codecov dashboard). For
# same-repo PRs and pushes to ``main`` the secret resolves and
# upload proceeds; for fork PRs the secret is unavailable and
# the action falls back to tokenless OIDC (still allowed for
# non-protected branches under the org policy).
uses: codecov/codecov-action@v7
with:
files: ./coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false # Codecov outage shouldn't break PRs
flags: unittests # groups runs in the trend view
- name: Upload raw coverage as workflow artifact
# Belt-and-suspenders for the first few weeks after Codecov goes
# live -- a self-hosted fallback in case the Codecov upload
# silently breaks. Drop this step once Codecov is reliably
# picking up uploads.
uses: actions/upload-artifact@v7
with:
name: coverage-xml
path: coverage.xml
if-no-files-found: error
retention-days: 30
test:
needs: lint
strategy:
fail-fast: false
matrix:
runner: [ubuntu-latest, macos-26, gpu_runner]
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/neml2-ci
with:
# dependencies: python.version_min
python-version: "3.10"
# dependencies: cmake.version_min
cmake-version: "3.26.1"
# dependencies: torch.version
torch-version: "2.12.1"
cpu-torch: ${{ matrix.runner != 'gpu_runner' }}
- name: Install
run: pip install ".[dev]" -v
- name: Run tests
run: pytest -v --junitxml=pytest.xml tests
- name: Test results
uses: EnricoMi/publish-unit-test-result-action/linux@v2
if: always()
with:
files: pytest.xml
check_name: Test Results
check_run: false
action_fail: true
comment_mode: off
# cibuildwheel build identifiers (cp310, ...) to build wheels for, sourced
# from compatibility.yaml so the wheel matrix tracks the supported-python set
# automatically. Full set on a release; just the min+max python on PRs/pushes
# (a packaging smoke -- the full set is for the published artifacts). Emitted
# as a JSON list so `wheels` fans out one (os, python) cell per wheel.
wheel-matrix:
needs: lint
runs-on: ubuntu-latest
outputs:
cibw: ${{ steps.set.outputs.cibw }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
# dependencies: python.version_min
python-version: "3.10"
- run: pip install pyyaml
- id: set
env:
MODE: ${{ github.event_name == 'release' && 'full' || 'pr' }}
run: |
echo "cibw=$(python scripts/compat_matrix.py expand --kind cibuildwheel --mode "$MODE")" >> "$GITHUB_OUTPUT"
wheels:
needs: [lint, wheel-matrix]
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-26]
python: ${{ fromJson(needs.wheel-matrix.outputs.cibw) }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Free disk space
if: runner.os == 'Linux'
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
swap-storage: true
- name: Install cibuildwheel
run: pip install cibuildwheel
- name: Build wheel
# One python per matrix cell (matrix.python is a cibuildwheel build tag
# like cp310) so the per-python builds run in PARALLEL instead of
# serially within an OS job. Each cibuildwheel wheel reinstalls the
# build-time torch inside the manylinux container (~24 min/wheel on
# Linux), so the serial build was the dominant CI cost; wall time now
# caps at one wheel regardless of python count.
run: cibuildwheel --output-dir wheelhouse
env:
CIBW_BUILD: ${{ matrix.python }}-*
- name: Resolve python version for the smoke test
id: pyver
shell: bash
run: echo "version=3.${TAG#cp3}" >> "$GITHUB_OUTPUT" # cp310 -> 3.10
env:
TAG: ${{ matrix.python }}
- name: Set up Python for the wheel smoke test
uses: actions/setup-python@v6
with:
python-version: ${{ steps.pyver.outputs.version }}
- name: Smoke-test the built wheel
# Install + import the wheel this cell just built. cibuildwheel's own
# test-command only runs on tag releases, so this gives every
# (os, python) cell a basic packaging check on PRs too. pip resolves the
# runtime deps (torch / pyzag / nmhit) straight from pyproject.toml.
shell: bash
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install wheelhouse/*.whl
python -c "import neml2; print(f'neml2 {neml2.__version__} loads from {neml2.__file__}')"
- uses: actions/upload-artifact@v7
if: github.event_name == 'release' && github.event.action == 'published'
with:
name: packages-wheels-${{ matrix.os }}-${{ matrix.python }}
path: wheelhouse/*.whl
sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Build SDist
run: pipx run build --sdist
- name: Upload SDist
uses: actions/upload-artifact@v7
if: github.event_name == 'release' && github.event.action == 'published'
with:
name: package-sdist
path: dist/*.tar.gz
PyPI:
needs: [sdist, wheels]
environment: pypi
permissions:
id-token: write
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/download-artifact@v8
with:
pattern: package-sdist
path: dist
merge-multiple: true
- uses: actions/download-artifact@v8
with:
pattern: packages-wheels-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1