-
Notifications
You must be signed in to change notification settings - Fork 11
395 lines (383 loc) · 16.3 KB
/
build.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
name: Build discrete-optimization
env:
MAIN_REPO_NAME: 'airbus/discrete-optimization'
on:
push:
branches:
- "**"
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
linters:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: create requirements.txt so that pip cache with setup-python works
run: echo "pre-commit" > requirements_precommit.txt
- uses: actions/setup-python@v5
with:
python-version: "3.9"
cache: "pip"
cache-dependency-path: requirements_precommit.txt
- name: install pre-commit
run: python -m pip install pre-commit
- name: get cached pre-commit hooks
uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: pre-commit checks
run: pre-commit run --show-diff-on-failure --color=always --all-files
trigger:
# store trigger reason
runs-on: ubuntu-latest
outputs:
is_release: ${{ steps.reason.outputs.is_release }}
is_push_on_default_branch: ${{ steps.reason.outputs.is_push_on_default_branch }}
steps:
- id: reason
run: |
echo "is_release=${{ startsWith(github.ref, 'refs/tags/v') }}" >> $GITHUB_OUTPUT
echo "is_push_on_default_branch=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}" >> $GITHUB_OUTPUT
build:
runs-on: ubuntu-latest
needs: trigger
outputs:
do_version: ${{ steps.get_library_version.outputs.version }}
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: create requirements.txt so that pip cache with setup-python works
run:
echo "build" > requirements_build.txt
- uses: actions/setup-python@v5
with:
python-version: "3.9"
cache: "pip"
cache-dependency-path: requirements_build.txt
- name: Install build dependencies
run: pip install -U build
- name: Build discrete-optimization (wheel + source code)
run: python -m build
- name: Upload as build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist
- name: get library version and save it
id: get_library_version
run: |
wheelfile=$(ls ./dist/discrete_optimization*.whl)
version=$(python -c "print('$wheelfile'.split('-')[1])")
echo "version=$version"
echo "version=$version" >> $GITHUB_OUTPUT
test:
needs: build
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "macos-13", "macos-latest", "windows-latest"]
python-version: ["3.9", "3.12"]
wo_gurobi: ["", "without gurobi"]
include:
- os: "ubuntu-latest"
minizinc_config_cmdline: export PATH=$PATH:$(pwd)/bin/squashfs-root/usr/bin; export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/bin/squashfs-root/usr/lib
minizinc_cache_path: $(pwd)/bin/squashfs-root
minizinc_url: https://github.com/MiniZinc/MiniZincIDE/releases/download/2.8.5/MiniZincIDE-2.8.5-x86_64.AppImage
minizinc_downloaded_filepath: bin/minizinc.AppImage
minizinc_install_cmdline: cd bin; sudo chmod +x minizinc.AppImage; sudo ./minizinc.AppImage --appimage-extract; cd ..
minizinc_prerequisites_cmdline: sudo apt update && sudo apt install libegl1 -y
- os: "macos-13"
minizinc_config_cmdline: export PATH=$PATH:$(pwd)/bin/MiniZincIDE.app/Contents/Resources
minizinc_cache_path: $(pwd)/bin/MiniZincIDE.app
minizinc_url: https://github.com/MiniZinc/MiniZincIDE/releases/download/2.8.5/MiniZincIDE-2.8.5-bundled.dmg
minizinc_downloaded_filepath: bin/minizinc.dmg
minizinc_install_cmdline: sudo hdiutil attach bin/minizinc.dmg; sudo cp -R /Volumes/MiniZinc*/MiniZincIDE.app bin/.
minizinc_prerequisites_cmdline: ""
- os: "macos-latest"
minizinc_config_cmdline: export PATH=$PATH:$(pwd)/bin/MiniZincIDE.app/Contents/Resources
minizinc_cache_path: $(pwd)/bin/MiniZincIDE.app
minizinc_url: https://github.com/MiniZinc/MiniZincIDE/releases/download/2.8.5/MiniZincIDE-2.8.5-bundled.dmg
minizinc_downloaded_filepath: bin/minizinc.dmg
minizinc_install_cmdline: sudo hdiutil attach bin/minizinc.dmg; sudo cp -R /Volumes/MiniZinc*/MiniZincIDE.app bin/.
minizinc_prerequisites_cmdline: ""
- os: "windows-latest"
minizinc_config_cmdline: export PATH=$PATH:~/AppData/Local/Programs/MiniZinc
minizinc_cache_path: ~/AppData/Local/Programs/MiniZinc
minizinc_url: https://github.com/MiniZinc/MiniZincIDE/releases/download/2.8.5/MiniZincIDE-2.8.5-bundled-setup-win64.exe
minizinc_downloaded_filepath: minizinc_setup.exe
minizinc_install_cmdline: cmd //c "minizinc_setup.exe /verysilent /currentuser /norestart /suppressmsgboxes /sp"
minizinc_prerequisites_cmdline: ""
- coverage: false # generally no coverage to avoid multiple reports
- coverage: true # coverage only for one entry of the matrix
os: "ubuntu-latest"
python-version: "3.12"
wo_gurobi: ""
exclude:
- os: "windows-latest"
wo_gurobi: "without gurobi"
- os: "macos-13"
wo_gurobi: "without gurobi"
- os: "macos-latest"
wo_gurobi: "without gurobi"
- os: "ubuntu-latest"
wo_gurobi: "without gurobi"
python-version: "3.9"
- os: "macos-latest"
python-version: "3.9"
- os: "macos-13"
python-version: "3.12"
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: Checkout discrete-optimization source code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
cache-dependency-path: pyproject.toml
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Install only discrete-optimization
run: |
python -m pip install -U pip
wheelfile=$(ls ./dist/discrete_optimization*.whl)
pip install ${wheelfile}
- name: Check minizinc based solvers fails without minizinc binary
run: |
python -c "
try:
from discrete_optimization.generic_tools.cp_tools import find_right_minizinc_solver_name, CpSolverName
find_right_minizinc_solver_name(CpSolverName.CHUFFED)
except RuntimeError:
pass
else:
raise AssertionError('We should not be able to `find_right_minizinc_solver_name()` without minizinc being installed.')
"
- name: Create bin/
run: mkdir -p bin
- name: Minizinc prerequisites
run: |
${{ matrix.minizinc_prerequisites_cmdline }}
- name: get MiniZinc path to cache
id: get-mzn-cache-path
run: |
echo "path=${{ matrix.minizinc_cache_path }}" >> $GITHUB_OUTPUT # expands variables
- name: Restore MiniZinc cache
id: cache-minizinc
uses: actions/cache@v4
with:
path: ${{ steps.get-mzn-cache-path.outputs.path }}
key: ${{ matrix.minizinc_url }}
- name: Download MiniZinc
if: steps.cache-minizinc.outputs.cache-hit != 'true'
run: |
curl -o "${{ matrix.minizinc_downloaded_filepath }}" -L ${{ matrix.minizinc_url }}
- name: Install MiniZinc
if: steps.cache-minizinc.outputs.cache-hit != 'true'
run: |
${{ matrix.minizinc_install_cmdline }}
- name: Test minizinc install
run: |
${{ matrix.minizinc_config_cmdline }}
minizinc --version
- name: Check imports are working
run: |
# configure minizinc
${{ matrix.minizinc_config_cmdline }}
# check imports
python tests/test_import_all_submodules.py
- name: Install test dependencies
run: |
wheelfile=$(ls ./dist/discrete_optimization*.whl)
pip install "${wheelfile}[test, quantum]"
if [ "${{ startsWith(matrix.os, 'windows') || (startsWith(matrix.os, 'macos') && matrix.python-version == '3.12') }}" == false ]; then
echo "install toulbar2"
pip install "${wheelfile}[toulbar]"
python -c "import pytoulbar2"
fi
if [ "${{ matrix.wo_gurobi }}" != "without gurobi" ]; then
echo "install gurobi"
pip install gurobipy
python -c "import gurobipy"
fi
- name: Restore tests data cache
id: cache-data
uses: actions/cache@v4
with:
path: ~/discrete_optimization_data
key: data-${{ hashFiles('discrete_optimization/datasets.py') }}
- name: Fetch data for tests
if: steps.cache-data.outputs.cache-hit != 'true'
run: |
${{ matrix.minizinc_config_cmdline }}
python -m discrete_optimization.datasets
- name: Test with pytest (no coverage)
if: ${{ !matrix.coverage }}
run: |
# configure minizinc
${{ matrix.minizinc_config_cmdline }}
# show library path used
pytest -s tests/show_do_path.py
# run test suite
MPLBACKEND="agg" NUMBA_BOUNDSCHECK=1 pytest \
-v --durations=0 --durations-min=10 \
tests
- name: Test with pytest (with coverage)
if: ${{ matrix.coverage }}
run: |
# configure minizinc
${{ matrix.minizinc_config_cmdline }}
# create a tmp directory from which running the tests
# so that "--cov discrete_optimization" look for package installed via the wheel
# instead of the source directory in the repository (which would lead to a coverage of 0%)
mkdir -p tmp && cd tmp
# show library path used
pytest -s ../tests/show_do_path.py
# run test suite
MPLBACKEND="agg" NUMBA_BOUNDSCHECK=1 pytest \
--cov discrete_optimization \
--cov-report xml:coverage.xml \
--cov-report html:coverage_html \
--cov-report term \
-v --durations=0 --durations-min=10 \
../tests
cd ..
- name: Upload coverage report as artifact
if: ${{ matrix.coverage }}
uses: actions/upload-artifact@v4
with:
name: coverage
path: |
tmp/coverage.xml
tmp/coverage_html
update-notebooks-for-colab-and-binder:
runs-on: ubuntu-latest
needs: [trigger, build]
outputs:
notebooks-branch: ${{ steps.write-output.outputs.notebooks_branch }}
binder-full-ref: ${{ steps.write-output.outputs.binder_full_ref }}
steps:
- name: Set default notebooks branch and binder env full ref
run: |
# default value: current branch
echo 'notebooks_branch="${{ github.ref_name}}"' >> $GITHUB_ENV
echo 'binder_full_ref="${{ github.repository }}/${{ github.ref_name}}"' >> $GITHUB_ENV
- uses: actions/checkout@v4
if: needs.trigger.outputs.is_release == 'true'
- name: replace d-o version to install in colab notebooks
if: needs.trigger.outputs.is_release == 'true'
run: |
version=${{ needs.build.outputs.do_version }}
old_pip_spec_pattern="\(pip.*install.*\)git+https.*egg=discrete-optimization"
new_pip_spec_pattern="\1discrete-optimization==$version"
if ${{ github.repository != env.MAIN_REPO_NAME && secrets.TEST_PYPI_API_TOKEN != '' }} == 'true'; then
# install from TestPypi if on a fork
new_pip_spec_pattern="${new_pip_spec_pattern} --extra-index-url https://test.pypi.org/simple/"
fi
shopt -s globstar # enable **
sed -i -e "s|${old_pip_spec_pattern}|${new_pip_spec_pattern}|" notebooks/**/*.ipynb
- name: replace d-o version to install in binder environment
if: needs.trigger.outputs.is_release == 'true'
run: |
version=${{ needs.build.outputs.do_version }}
linefilter="/^name/!"
old_pip_spec_pattern="\(\s*\)-.*discrete-optimization.*$"
new_pip_spec_pattern="\1- discrete-optimization==$version"
if ${{ github.repository != env.MAIN_REPO_NAME && secrets.TEST_PYPI_API_TOKEN != '' }} == 'true'; then
# install from TestPypi if on a fork
new_pip_spec_pattern="${new_pip_spec_pattern}\n\1- --extra-index-url https://test.pypi.org/simple/"
fi
sed_command="${linefilter}s|${old_pip_spec_pattern}|${new_pip_spec_pattern}|"
echo sed -i -e ${sed_command} binder/environment.yml
sed -i -e "${sed_command}" binder/environment.yml
- name: push modifications on a dedicated tag
if: needs.trigger.outputs.is_release == 'true'
id: push-tuto-release-tag
run: |
current_tag_name=${GITHUB_REF/refs\/tags\//} # stripping refs/tags/
new_tag_name="notebooks-${current_tag_name}"
echo ${new_tag_name}
git config user.name "Actions"
git config user.email "[email protected]"
git commit binder/environment.yml notebooks -m "Install appropriate version of discrete-optimization"
git tag ${new_tag_name} -m "Use release ${current_tag_name} in binder and colab"
git push origin ${new_tag_name}
# store new tag name as notebooks branch
echo "notebooks_branch=${new_tag_name}" >> $GITHUB_ENV
echo "binder_full_ref=${{ github.repository }}/${new_tag_name}" >> $GITHUB_ENV
- name: write new notebooks branch in job outputs
id: write-output
run: |
echo "notebooks_branch=${notebooks_branch}" >> $GITHUB_OUTPUT
echo "binder_full_ref=${binder_full_ref}" >> $GITHUB_OUTPUT
build-doc:
needs: [update-notebooks-for-colab-and-binder]
uses: ./.github/workflows/build-doc.yml
with:
notebooks-branch: ${{ needs.update-notebooks-for-colab-and-binder.outputs.notebooks-branch }}
deploy:
# for release tags
runs-on: ubuntu-latest
needs: [trigger, test]
if: needs.trigger.outputs.is_release == 'true'
steps:
- name: Download wheels artifact
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Create the github release
uses: ncipollo/release-action@v1
with:
artifacts: dist
generateReleaseNotes: true
- name: Publish package to TestPyPI (only for forks)
env:
TEST_PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
if: github.repository != env.MAIN_REPO_NAME && env.TEST_PYPI_API_TOKEN != ''
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
- name: Publish package to PyPI (main repo)
env:
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
if: github.repository == env.MAIN_REPO_NAME && env.PYPI_API_TOKEN != ''
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
deploy-doc:
needs: [trigger, update-notebooks-for-colab-and-binder, build-doc, test, deploy]
# if: always()
# -> trigger even if one needed job was skipped (namely deploy)
# -> needed jobs successes must be checked explicitely
if: |
always()
&& (needs.build-doc.result == 'success')
&& (
(
(needs.trigger.outputs.is_push_on_default_branch == 'true')
&& (needs.test.result == 'success')
)
|| (
(needs.trigger.outputs.is_release == 'true')
&& (needs.deploy.result == 'success')
)
)
uses: ./.github/workflows/deploy-doc.yml
with:
doc-version: ${{ needs.build-doc.outputs.doc-version }}
binder-env-fullref: ${{ needs.update-notebooks-for-colab-and-binder.outputs.binder-full-ref }}