-
-
Notifications
You must be signed in to change notification settings - Fork 0
148 lines (137 loc) · 6.01 KB
/
Copy pathpython.yml
File metadata and controls
148 lines (137 loc) · 6.01 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
# Clean-install gate for the Python model and its pinned anatomical inputs.
name: python
# DEFAULT ON, WITH AN OFF-SWITCH. The jobs were paused behind CI_ENABLED while this
# repository was private and its metered Actions minutes were exhausted (the long note in
# viewer.yml tells that story). The repository is public now, standard runners are free,
# and the gate's polarity is flipped: jobs run unless the repository variable CI_ENABLED
# is set to the literal string 'false'. The off-switch stays because the pause was a good
# tool -- skipped-not-red kept the checks honest while they could not run -- and losing
# it would mean re-deriving it the next time it is needed.
on:
push:
branches: [main]
paths: ['worm/**', 'tests/**', 'tools/*.py', 'tools/check_all.mjs', 'data/**',
'pyproject.toml', 'requirements*.txt', 'web/worm.model', 'web/worm.wasm',
'wasm/assembly/**', 'wasm/asconfig.json', 'wasm/package*.json',
'.github/workflows/python.yml']
pull_request:
paths: ['worm/**', 'tests/**', 'tools/*.py', 'tools/check_all.mjs', 'data/**',
'pyproject.toml', 'requirements*.txt', 'web/worm.model', 'web/worm.wasm',
'wasm/assembly/**', 'wasm/asconfig.json', 'wasm/package*.json',
'.github/workflows/python.yml']
workflow_dispatch:
# One run per branch at a time; a superseded push cancels the run it replaced instead of
# both running to completion. Runs on main itself always finish, so every merge keeps a
# complete record (#144).
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
discover-tests:
name: discover Python test targets
if: ${{ vars.CI_ENABLED != 'false' }}
runs-on: ubuntu-latest
outputs:
targets: ${{ steps.targets.outputs.targets }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- id: targets
name: build the cost-aware test matrix
shell: bash
run: |
targets="$(python tools/ci_test_targets.py --json)"
echo "targets=$targets" >> "$GITHUB_OUTPUT"
tests:
name: Python 3.10 - ${{ matrix.test_target }}
if: ${{ vars.CI_ENABLED != 'false' }}
needs: discover-tests
runs-on: ubuntu-latest
timeout-minutes: 30
# These simulations solve small dense systems repeatedly. Letting BLAS create a
# runner-sized thread team for each solve is dramatically slower. The matrix helper
# also keeps shared fixtures together, shards only the dense files, and combines the
# sub-minute files so parallel wall time does not come at needless runner/setup cost.
env:
OMP_NUM_THREADS: '1'
OPENBLAS_NUM_THREADS: '1'
MKL_NUM_THREADS: '1'
VECLIB_MAXIMUM_THREADS: '1'
NUMEXPR_NUM_THREADS: '1'
strategy:
fail-fast: false
matrix:
test_target: ${{ fromJSON(needs.discover-tests.outputs.targets) }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: pip
- run: python -m pip install -e '.[test]'
- run: python tools/ci_test_targets.py --run "${{ matrix.test_target }}"
dataset:
name: pinned anatomy rebuilds the committed dataset and runtime pair
if: ${{ vars.CI_ENABLED != 'false' }}
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
cache: pip
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: wasm/package-lock.json
- run: python -m pip install -e .
- run: npm ci --no-audit --no-fund
working-directory: wasm
- run: python tools/fetch_raw.py
- run: python tools/build_dataset.py
- run: git diff --exit-code -- data/celegans.json
# The committed .model and .wasm are a single artifact set: model_gen.ts compiles
# the model payload's offsets and scalar constants into the runtime. Snapshot all
# three before either generator runs, then prove the committed runtime was built from
# the committed layout.
- name: snapshot the committed model/runtime set
run: |
mkdir -p "$RUNNER_TEMP/celegans-artifacts"
cp web/worm.model "$RUNNER_TEMP/celegans-artifacts/worm.model"
cp web/worm.wasm "$RUNNER_TEMP/celegans-artifacts/worm.wasm"
cp wasm/assembly/model_gen.ts "$RUNNER_TEMP/celegans-artifacts/model_gen.ts"
- name: committed WASM matches its committed generated layout
run: |
cd wasm
npx asc assembly/index.ts --target release
cmp "$RUNNER_TEMP/celegans-artifacts/worm.wasm" ../web/worm.wasm
- name: rebuild the model and runtime as one set
run: |
python tools/export_model.py
cd wasm
npx asc assembly/index.ts --target release
env:
PYTHONPATH: .
# Raw model bytes include a linear solve and libm-derived setup values, so exact
# cross-platform comparison would make last-bit BLAS differences a flaky gate. The
# checker compares every layout field and payload array, exactly for discrete data
# and within 5e-13 for floats. If model_gen is byte-identical, the WASM must be too.
- name: regenerated model/runtime set matches the committed set
run: >-
python tools/check_model_artifacts.py
--expected-model "$RUNNER_TEMP/celegans-artifacts/worm.model"
--actual-model web/worm.model
--expected-layout "$RUNNER_TEMP/celegans-artifacts/model_gen.ts"
--actual-layout wasm/assembly/model_gen.ts
--expected-wasm "$RUNNER_TEMP/celegans-artifacts/worm.wasm"
--actual-wasm web/worm.wasm
- name: regenerated pair conforms to the Python model
run: |
python tools/conform.py
node wasm/conform.mjs
env:
PYTHONPATH: .