-
Notifications
You must be signed in to change notification settings - Fork 18
/
.gitlab-ci.yml
523 lines (474 loc) · 14.9 KB
/
.gitlab-ci.yml
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
variables:
LIGO_SKYMAP_USE_SYSTEM_CHEALPIX: 1
OMP_NUM_THREADS: 4
include:
- project: computing/gitlab-ci-templates
file: python.yml
stages:
- deps
- dist
- test
- deploy
#
# Read all requirements for the package and all extras from setup.cfg.
#
requirements:
stage: deps
image: python:slim
script:
- |
python >requirements.txt <<EOF
import tomllib
with open('pyproject.toml', 'rb') as f:
conf = tomllib.load(f)
for dep in conf['project']['dependencies']:
print(dep)
for deps in conf['project']['optional-dependencies'].values():
for dep in deps:
print(dep)
EOF
artifacts:
paths:
- requirements.txt
expire_in: 1 day
#
# Build Python source package.
#
sdist:
image: python:3.10
stage: dist
script:
- pip install build
- python -m build -s -o .
needs: []
artifacts:
paths:
- '*.tar.*'
expire_in: 1 day
#
# Build binary wheels for Linux and macOS.
#
.wheel-linux: &wheel-linux
stage: dist
script:
# Build and install LALSuite
- PYPREFIX=/opt/python/cp310-cp310
- ${PYPREFIX}/bin/pip install build
- ${PYPREFIX}/bin/python -m build -w
- auditwheel repair dist/*.whl
- rm dist/*
- mv wheelhouse/* .
needs: []
artifacts:
paths:
- '*.whl'
expire_in: 1 day
wheel/linux/x86_64:
<<: *wheel-linux
# This container is derived from the official manylinux image provided by
# python.org (see PEP 513), and includes all of the LALSuite
# build-dependencies.
image: containers.ligo.org/lscsoft/lalsuite-manylinux/manylinux_2_28_x86_64:icc
wheel/linux/aarch64:
<<: *wheel-linux
variables:
CFLAGS: -Ofast -fno-finite-math-only -flto
# This container is derived from the official manylinux image provided by
# python.org (see PEP 513), and includes all of the LALSuite
# build-dependencies.
image: containers.ligo.org/lscsoft/lalsuite-manylinux/manylinux_2_28_aarch64
tags:
- aarch64
.wheel-macos: &wheel-macos
variables:
CC: gcc-mp-14
CXX: g++-mp-14
CFLAGS: -Ofast -fno-finite-math-only -flto
stage: dist
script:
- . /opt/local/share/macports/setupenv.bash
- PYVERS=3.10
# Enter virtualenv so that we have a controlled version of Numpy
- python${PYVERS} -m venv env
- source env/bin/activate
- pip install build delocate
# Build and audit wheel
- python -m build -w .
- delocate-wheel -v -w wheelhouse dist/*.whl
- rm -f *.whl
- mv wheelhouse/* .
needs: []
artifacts:
paths:
- '*.whl'
expire_in: 1 day
wheel/macos/x86_64:
<<: *wheel-macos
tags:
- macos_ventura_x86_64
wheel/macos/arm64:
<<: *wheel-macos
tags:
- macos_monterey_arm64
#
# Build Docker containers for dependencies listed in requirements.txt,
# plus dependencies for running the unit tests, collecting coverage data,
# and generating the docs.
#
.in-tmpdir: &in-tmpdir
before_script:
- WORKING_DIRECTORY="$(mktemp -d)"
- cd "${WORKING_DIRECTORY}"
after_script:
- cd "${CI_PROJECT_DIR}"
- rm -rf "${WORKING_DIRECTORY}"
.deps: &deps
stage: dist
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE/$CI_JOB_NAME:$CI_COMMIT_REF_SLUG
GIT_STRATEGY: none
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- |
cat <<EOF > Dockerfile
FROM python:${CI_JOB_NAME#*python}
RUN apt-get update && apt-get -y install --no-install-recommends libchealpix-dev libgsl-dev pkg-config && rm -rf /var/lib/apt/lists/*
RUN pip --no-cache-dir install pytest-cov gcovr\<8 pycobertura flake8 coverage 'pip>=24'
COPY requirements.txt .
# FIXME: temporarily install numpy<2 so that we test Numpy 1.x here and cover newer versions in the dev deps test. Remove when we require numpy>=2.
RUN pip --no-cache-dir install numpy\<2 -r requirements.txt && rm -f requirements.txt
EOF
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
needs:
- requirements
tags:
- executor-docker
deps-aarch64/python3.10:
<<: *deps
tags:
- aarch64
# FIXME: Uncomment after the following issue is fixed.
# https://git.ligo.org/computing/helpdesk/-/issues/5775
# - executor-docker
deps-x86_64/python3.10:
<<: *deps
# Pin Python patch version due to bug affecting doctests that has been fixed
# but will not be backported to Python 3.11.
# See https://github.com/python/cpython/issues/117692#issuecomment-2047800533.
deps-x86_64/python3.11.8:
<<: *deps
deps-x86_64/python3.12:
<<: *deps
#
# Generate documentation.
#
docs:
image: $CI_REGISTRY_IMAGE/deps-x86_64/python3.10:$CI_COMMIT_REF_SLUG
stage: test
<<: *in-tmpdir
script:
- tar --strip-components 1 -xf ${CI_PROJECT_DIR}/*.tar.*
- pip install -e .
- make -C docs html
- mv docs/_build/html ${CI_PROJECT_DIR}/
needs:
- deps-x86_64/python3.10
- sdist
artifacts:
paths:
- html/
expire_in: 1 day
#
# Test the wheels.
#
.test: &test
<<: *in-tmpdir
stage: test
script:
- pip install $(echo ${CI_PROJECT_DIR}/*.whl)[test]
- python -c 'import sys; from ligo.skymap import test; sys.exit(test(args="--doctest-plus --doctest-ufunc --mpl --mpl-results-path '${CI_PROJECT_DIR}'/pytest-mpl-results --mpl-generate-summary=html --omp-get-num-threads --durations=10"))'
artifacts:
paths:
- pytest-mpl-results/
when: always
test/linux/aarch64/python3.10:
<<: *test
image: $CI_REGISTRY_IMAGE/deps-aarch64/python3.10:$CI_COMMIT_REF_SLUG
needs:
- deps-aarch64/python3.10
- wheel/linux/aarch64
tags:
- aarch64
test/linux/x86_64/python3.10:
<<: *test
image: $CI_REGISTRY_IMAGE/deps-x86_64/python3.10:$CI_COMMIT_REF_SLUG
needs:
- deps-x86_64/python3.10
- wheel/linux/x86_64
test/linux/x86_64/python3.11.8:
<<: *test
image: $CI_REGISTRY_IMAGE/deps-x86_64/python3.11.8:$CI_COMMIT_REF_SLUG
needs:
- deps-x86_64/python3.11.8
- wheel/linux/x86_64
test/linux/x86_64/python3.12:
<<: *test
image: $CI_REGISTRY_IMAGE/deps-x86_64/python3.12:$CI_COMMIT_REF_SLUG
needs:
- deps-x86_64/python3.12
- wheel/linux/x86_64
.test/macos: &test-macos
<<: *in-tmpdir
stage: test
script:
- . /opt/local/share/macports/setupenv.bash
- PYVERS=3.10
# Enter virtualenv so that we have a controlled version of Numpy
- python${PYVERS} -m venv env
- source env/bin/activate
- pip install $(echo ${CI_PROJECT_DIR}/*.whl)[test]
- python -c 'import sys; from ligo.skymap import test; sys.exit(test(args="--doctest-plus --doctest-ufunc --mpl --mpl-results-path '${CI_PROJECT_DIR}'/pytest-mpl-results --mpl-generate-summary=html --omp-get-num-threads --durations=10"))'
artifacts:
paths:
- pytest-mpl-results/
when: always
test/macos/x86_64:
<<: *test-macos
tags:
- macos_ventura_x86_64
needs:
- wheel/macos/x86_64
test/macos/arm64:
<<: *test-macos
tags:
- macos_sequoia_arm64
needs:
- wheel/macos/arm64
# Test against development versions of certain upstream dependencies.
test/dev-deps:
<<: *test
image: python:3.12
script:
# Qhull is needed to build Matplotlib from source
- apt-get update && apt-get -y install --no-install-recommends libqhull-dev
- pip install $(echo ${CI_PROJECT_DIR}/*.whl)[test]
- >
pip install --upgrade --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
'numpy>=0.0.0.dev0'
'matplotlib>=0.0.0.dev0'
'lalsuite>=0.0.0.dev0'
git+https://github.com/astropy/astropy
git+https://git.ligo.org/kipp.cannon/python-ligo-lw
- python -c 'import sys; from ligo.skymap import test; sys.exit(test(args="--doctest-plus --doctest-ufunc --mpl --mpl-results-path '${CI_PROJECT_DIR}'/pytest-mpl-results --mpl-generate-summary=html --omp-get-num-threads --durations=10"))'
needs:
- wheel/linux/x86_64
allow_failure: true
#
# Measure test coverage:
# - coverage.py for Python code
# - gcov/gcovr for C code
#
# Export the results from both to Cobertura format because it's an XML format
# that both coverage.py and gcovr can write, merge them by hand, and then
# write HTML and text summaries.
#
# This would be a lot prettier if we could use coveralls or codecov.io,
# which support multilingual test coverage. However, those products don't
# integrate with git.ligo.org (or at least, they don't integrate for free).
#
test/coverage:
stage: test
image: $CI_REGISTRY_IMAGE/deps-x86_64/python3.10:$CI_COMMIT_REF_SLUG
variables:
# -UNDEBUG
# enable C assertions
# -coverage
# instrument C code for coverage measurement
# -fsanitize=undefined
# enable GCC UndefinedBehaviorSanitizer
# -fopenmp
# The -coverage breaks OpenMP detection in extension-helpers.
# See https://github.com/astropy/extension-helpers/issues/1
CC: gcc -coverage
LDSHARED: gcc -pthread -shared -coverage
CFLAGS: -UNDEBUG -fsanitize=undefined -fopenmp
coverage: '/^TOTAL\s+.*\s+(\d+\.?\d*)%/'
<<: *in-tmpdir
script:
- tar --strip-components 1 -xf ${CI_PROJECT_DIR}/*.tar.*
# Build and install package with pip, but save the intermediate files which
# include gcov's .gcno data files.
- mkdir obj
- pip install --no-deps -C--global-option=build_ext -C--global-option=--build-temp=$(pwd)/obj -ve .
# Run tests.
- pytest --capture=sys --doctest-plus --doctest-ufunc --mpl --mpl-results-path ${CI_PROJECT_DIR}/pytest-mpl-results --mpl-generate-summary=html --durations=10 --cov ligo/skymap --junit-xml=${CI_PROJECT_DIR}/junit.xml || FAILED=true
# Write coverage reports in Cobertura format.
- gcovr --gcov-ignore-errors no_working_dir_found obj -r . -x -o c-coverage.xml
- coverage xml -o py-coverage.xml
# Merge coverage reports.
- ${CI_PROJECT_DIR}/.gitlab/combine-coverage.py py-coverage.xml c-coverage.xml coverage.xml
# Write human-readable report.
- pycobertura show coverage.xml -f html -o coverage.html
- pycobertura show coverage.xml
- cp coverage.html coverage.xml ${CI_PROJECT_DIR}
- if [[ "$FAILED" ]]; then false; fi
needs:
- deps-x86_64/python3.10
- sdist
artifacts:
paths:
- coverage.html
- pytest-mpl-results/
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
junit: junit.xml
expire_in: 1 day
when: always
timeout: 2 hours
#
# Run flake8 linter to enforce code style.
#
lint:
extends:
- .python:flake8
image: $CI_REGISTRY_IMAGE/deps-x86_64/python3.10:$CI_COMMIT_REF_SLUG
stage: test
needs:
- deps-x86_64/python3.10
#
# Acceptance tests.
#
tests/review:
stage: test
image: containers.ligo.org/emfollow/ssh-kerberos
when: manual
needs:
- wheel/linux/x86_64
variables:
GIT_STRATEGY: none
REMOTE_HOST: [email protected]
script:
- echo -e "\e[0Ksection_start:`date +%s`:remote_clone\r\e[0KCloning HTCondor workflow on cluster"
- ssh -T $REMOTE_HOST "mkdir -p public_html && git clone --depth=1 --recurse-submodules --shallow-submodules https://git.ligo.org/leo-singer/ligo-skymap-acceptance-tests-public public_html/$CI_JOB_ID"
- echo -e "\e[0Ksection_end:`date +%s`:remote_clone\r\e[0K"
- echo -e "\e[0Ksection_start:`date +%s`:remote_install\r\e[0KInstalling Python packages on cluster"
- scp *.whl $REMOTE_HOST:public_html/$CI_JOB_ID
- |
ssh -T $REMOTE_HOST "bash -e" <<EOF
cd public_html/$CI_JOB_ID
/cvmfs/oasis.opensciencegrid.org/ligo/sw/conda/bin/python -m venv env
source env/bin/activate
pip install *.whl
pip install -r requirements.txt
EOF
- echo -e "\e[0Ksection_end:`date +%s`:remote_install\r\e[0K"
- echo -e "\e[0Ksection_start:`date +%s`:remote_submit\r\e[0KRunning HTCondor workflow on cluster"
- |
ssh -T $REMOTE_HOST "bash -e" <<EOF
cd public_html/$CI_JOB_ID
source env/bin/activate
condor_submit_dag -include_env LAL_DATA_PATH -append accounting_group_user=leo.singer -batch-name pipeline/$CI_JOB_ID htcondor/acceptance-tests.dag
tail --follow=descriptor --retry htcondor/acceptance-tests.dag.dagman.out &
condor_wait htcondor/acceptance-tests.dag.dagman.log
kill %
EOF
- echo -e "\e[0Ksection_end:`date +%s`:remote_submit\r\e[0K"
- echo -e "\e[0Ksection_start:`date +%s`:remote_retrieve\r\e[0KRetrieving artifacts from cluster"
- scp -r $REMOTE_HOST:public_html/$CI_JOB_ID/site site
- echo -e "\e[0Ksection_end:`date +%s`:remote_retrieve\r\e[0K"
- echo -e "\e[0Ksection_start:`date +%s`:remote_cleanup\r\e[0KCleaning up files on cluster"
- ssh -T $REMOTE_HOST "rm -rf public_html/$CI_JOB_ID"
- echo -e "\e[0Ksection_end:`date +%s`:remote_cleanup\r\e[0K"
artifacts:
paths:
- site
- site/index.html
expose_as: acceptance tests
expire_in: 1 week
#
# Run benchmark.
#
tests/benchmark:
stage: test
image: containers.ligo.org/emfollow/ssh-kerberos
when: manual
needs:
- wheel/linux/x86_64
variables:
GIT_STRATEGY: none
REMOTE_HOST: [email protected]
script:
- |
ssh -T $REMOTE_HOST "bash -e" <<EOF
mkdir -p public_html
git clone $CI_REPOSITORY_URL public_html/$CI_JOB_ID
cd public_html/$CI_JOB_ID
git checkout $CI_COMMIT_REF_NAME
EOF
- scp *.whl $REMOTE_HOST:public_html/$CI_JOB_ID
- |
ssh -T $REMOTE_HOST "bash -e" <<EOF
cd public_html/$CI_JOB_ID
CI_SERVER_PROTOCOL="$CI_SERVER_PROTOCOL" CI_JOB_TOKEN="$CI_JOB_TOKEN" \
CI_SERVER_FQDN="$CI_SERVER_FQDN" condor_run \
-a request_disk=4G \
-a request_memory=8G \
-a request_cpus=48 \
-a accounting_group=ligo.dev.o4.cbc.pe.bayestar \
-a accounting_group_user=leo.singer \
-a Requirements=TARGET.machine==\"node2100.cluster.ldas.cit\" \
-a MY.Benchmarking_Mix=True \
./.gitlab/benchmark.sh *.whl
EOF
- scp $REMOTE_HOST:public_html/$CI_JOB_ID/{benchmark.svg,roofline.html,perf.svg} ./
after_script:
- echo -e "\e[0Ksection_start:`date +%s`:remote_cleanup\r\e[0KCleaning up files on cluster"
- ssh -T $REMOTE_HOST "rm -rf public_html/$CI_JOB_ID"
- echo -e "\e[0Ksection_end:`date +%s`:remote_cleanup\r\e[0K"
artifacts:
paths:
- benchmark.svg
- roofline.html
- perf.svg
expose_as: benchmark
expire_in: 1 week
#
# Gather coverage reports and docs for GitLab pages and build badges.
#
pages:
stage: deploy
script:
- mv html public
- mv coverage.html public/coverage.html
needs:
- docs
- test/coverage
artifacts:
paths:
- public
expire_in: 30 days
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
#
# Upload Python packages.
#
deploy/pypi:
stage: deploy
image: python:slim
before_script:
- pip install twine
script:
# TWINE_USERNAME and TWINE_PASSWORD are provided by CI secret variables
- twine upload --skip-existing *.whl *.tar.*
needs:
- sdist
- wheel/linux/x86_64
- wheel/linux/aarch64
- wheel/macos/x86_64
- wheel/macos/arm64
rules:
- if: '$CI_COMMIT_TAG'
- if: '$CI_PIPELINE_SOURCE == "schedule"'