🔨 Build scikit-decide #1425
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: 🔨 Build scikit-decide | |
on: | |
push: | |
branches: | |
- "**" | |
pull_request: | |
workflow_dispatch: | |
schedule: | |
- cron: '45 1 * * *' | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: true | |
env: | |
BOOST_DIR: 3rdparty/boost | |
BOOST_VERSION: "1.76.0" | |
SKDECIDE_SKIP_DEPS: 1 | |
jobs: | |
setup: | |
runs-on: ubuntu-latest | |
outputs: | |
python_version: ${{ steps.generate-matrix.outputs.python_version }} | |
build: ${{ steps.generate-matrix.outputs.build}} | |
test: ${{ steps.generate-matrix.outputs.test}} | |
do_macos: ${{ steps.generate-matrix.outputs.do_macos}} | |
do_ubuntu: ${{ steps.generate-matrix.outputs.do_ubuntu}} | |
do_windows: ${{ steps.generate-matrix.outputs.do_windows}} | |
build_doc: ${{ steps.generate-matrix.outputs.build_doc}} | |
steps: | |
- uses: actions/setup-python@v4 | |
with: | |
python-version: 3.8 | |
- name: Generate Matrix | |
id: generate-matrix | |
shell: python3 {0} | |
run: | | |
from os import environ | |
python_version = ["3.7", "3.8", "3.9", "3.10"] | |
build = [ "macos-latest", "ubuntu-latest", "windows-latest" ] | |
test = [ "macos-11", "macos-12", "ubuntu-22.04", "ubuntu-20.04", "windows-2019", "windows-2022"] | |
build_doc = "true" | |
if "${{ github.event_name }}" != "schedule": | |
to_bool = lambda s: True if s == "true" else False | |
python_filter = { | |
'3.7' : to_bool("${{ contains(github.event.head_commit.message, '[ci: python-3.7]') }}"), | |
'3.8' : to_bool("${{ contains(github.event.head_commit.message, '[ci: python-3.8]') }}"), | |
'3.9' : to_bool("${{ contains(github.event.head_commit.message, '[ci: python-3.9]') }}"), | |
'3.10' : to_bool("${{ contains(github.event.head_commit.message, '[ci: python-3.10]') }}"), | |
} | |
if any(python_filter.values()): | |
python_version = [v for v in python_version if python_filter[v]] | |
os_filter = { | |
'macos-11' : to_bool("${{ contains(github.event.head_commit.message, '[ci: macos-11]') }}"), | |
'macos-12' : to_bool("${{ contains(github.event.head_commit.message, '[ci: macos-12]') }}"), | |
'ubuntu-22.04' : to_bool("${{ contains(github.event.head_commit.message, '[ci: ubuntu-22.04]') }}"), | |
'ubuntu-20.04' : to_bool("${{ contains(github.event.head_commit.message, '[ci: ubuntu-20.04]') }}"), | |
'windows-2019' : to_bool("${{ contains(github.event.head_commit.message, '[ci: windows-2019]') }}"), | |
'windows-2022' : to_bool("${{ contains(github.event.head_commit.message, '[ci: windows-2022]') }}"), | |
} | |
if set(os_filter.keys()) != set(test): | |
raise Exception("test and os_filter do not contain the same keys") | |
if "${{ contains(github.event.head_commit.message, '[ci: windows]') }}" == "true": | |
os_filter.update({k: True for k in os_filter if k.startswith("windows")}) | |
if "${{ contains(github.event.head_commit.message, '[ci: macos]') }}" == "true": | |
os_filter.update({k: True for k in os_filter if k.startswith("macos")}) | |
if "${{ contains(github.event.head_commit.message, '[ci: ubuntu]') }}" == "true": | |
os_filter.update({k: True for k in os_filter if k.startswith("ubuntu")}) | |
# If there is no keyword, proceed as if all were present | |
if not any(os_filter.values()): | |
os_filter.update({k: True for k in os_filter}) | |
test = [ v for v in test if os_filter[v]] | |
test_os = { v.split('-')[0] for v in test } | |
build = [ v for v in build if v.split('-')[0] in test_os ] | |
if "${{ contains(github.event.head_commit.message, '[ci: skip-doc]') }}" == "true" or "ubuntu-latest" not in build: | |
build_doc = "false" | |
oses = ["macos", "ubuntu", "windows"] | |
build_dict = {os : [k for k in build if k.startswith(os)] for os in oses} | |
with open(environ["GITHUB_OUTPUT"], "a") as f: | |
f.write(f"build={build_dict}\n") | |
f.write(f"test={dict({os : [k for k in test if k.startswith(os)] for os in oses})}\n") | |
f.write(f"build_doc={build_doc}\n") | |
for os in oses: | |
f.write(f"do_{os}={'true' if len(build_dict[os]) > 0 else 'false'}\n") | |
f.write(f"python_version={python_version}\n") | |
lint-sources: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v3 | |
- uses: actions/setup-python@v4 | |
with: | |
python-version: "3.8" | |
- name: install pre-commit | |
run: python -m pip install pre-commit | |
- name: get cached pre-commit hooks | |
uses: actions/cache@v3 | |
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 | |
build-windows: | |
needs: [setup] | |
if: needs.setup.outputs.do_windows == 'true' | |
strategy: | |
matrix: | |
os: ${{ fromJSON(needs.setup.outputs.build).windows }} | |
python-version: ${{ fromJSON(needs.setup.outputs.python_version) }} | |
fail-fast: false | |
defaults: | |
run: | |
shell: bash | |
runs-on: ${{ matrix.os }} | |
steps: | |
- name: Checkout scikit-decide source code | |
uses: actions/checkout@v3 | |
with: | |
submodules: true | |
fetch-depth: 0 | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Load cached venv | |
id: cached-pip-wheels | |
uses: actions/cache@v3 | |
with: | |
path: ~/.cache | |
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} | |
- name: Restore Boost cache | |
uses: actions/cache@v3 | |
id: cache-boost | |
with: | |
path: ${{env.BOOST_DIR}} | |
key: BOOST_${{env.BOOST_VERSION}} | |
- name: Install Boost | |
if: steps.cache-boost.outputs.cache-hit != 'true' | |
run: | | |
mkdir -p $BOOST_DIR | |
curl --silent --location --output - \ | |
https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_${BOOST_VERSION//./_}.tar.bz2 |\ | |
tar jxf - -C $BOOST_DIR --strip-components=1 boost_${BOOST_VERSION//./_}/boost | |
shell: bash | |
- name: Restore build dependencies | |
id: cache-build-dependencies | |
uses: actions/cache@v3 | |
with: | |
path: | | |
skdecide/hub/bin | |
skdecide/hub/share | |
skdecide/hub/*.msc | |
key: ${{ runner.os }}-cache-deps | |
- name: Update SKDECIDE_SKIP_DEPS | |
if: steps.cache-build-dependencies.outputs.cache-hit != 'true' | |
run: echo "SKDECIDE_SKIP_DEPS=0" >> $GITHUB_ENV | |
- name: Build wheel | |
run: | | |
export "BOOST_ROOT=$PWD/$BOOST_DIR" | |
python -m pip install --upgrade pip | |
pip install build poetry-dynamic-versioning | |
python -m build --sdist --wheel | |
- name: Update build cache from wheels | |
if: steps.cache-build-dependencies.outputs.cache-hit != 'true' | |
run: 7z x dist/*.whl -y | |
- name: Upload as build artifacts | |
uses: actions/upload-artifact@v3 | |
with: | |
name: wheels | |
path: dist/*.whl | |
build-macos: | |
needs: [setup] | |
if: needs.setup.outputs.do_macos == 'true' | |
strategy: | |
matrix: | |
os: ${{ fromJSON(needs.setup.outputs.build).macos }} | |
python-version: ${{ fromJSON(needs.setup.outputs.python_version) }} | |
fail-fast: false | |
defaults: | |
run: | |
shell: bash | |
runs-on: ${{ matrix.os }} | |
steps: | |
- name: Checkout scikit-decide source code | |
uses: actions/checkout@v3 | |
with: | |
submodules: true | |
fetch-depth: 0 | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Load cached venv | |
id: cached-pip-wheels | |
uses: actions/cache@v3 | |
with: | |
path: ~/.cache | |
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} | |
- name: Restore Boost cache | |
uses: actions/cache@v3 | |
id: cache-boost | |
with: | |
path: ${{env.BOOST_DIR}} | |
key: BOOST_${{env.BOOST_VERSION}} | |
- name: Install Boost | |
if: steps.cache-boost.outputs.cache-hit != 'true' | |
run: | | |
mkdir -p $BOOST_DIR | |
curl --silent --location --output - \ | |
https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_${BOOST_VERSION//./_}.tar.bz2 |\ | |
tar jxf - -C $BOOST_DIR --strip-components=1 boost_${BOOST_VERSION//./_}/boost | |
shell: bash | |
- name: Restore build dependencies | |
id: cache-build-dependencies | |
uses: actions/cache@v3 | |
with: | |
path: | | |
skdecide/hub/bin | |
skdecide/hub/share | |
skdecide/hub/*.msc | |
key: ${{ runner.os }}-cache-deps | |
- name: Update SKDECIDE_SKIP_DEPS | |
if: steps.cache-build-dependencies.outputs.cache-hit != 'true' | |
run: echo "SKDECIDE_SKIP_DEPS=0" >> $GITHUB_ENV | |
- name: Install omp | |
run: brew install libomp | |
- name: Install and restore ccache | |
uses: hendrikmuhs/[email protected] | |
with: | |
key: ${{ runner.os }}-py${{ matrix.python-version }} | |
max-size: 80M | |
- name: Let cmake use ccache | |
run: | | |
echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> ${GITHUB_ENV} | |
echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> ${GITHUB_ENV} | |
- name: Build wheel | |
run: | | |
export "BOOST_ROOT=$PWD/$BOOST_DIR" | |
python -m pip install --upgrade pip | |
pip install build poetry-dynamic-versioning | |
# cross-compile for macosx-10.15 | |
export MACOSX_DEPLOYMENT_TARGET=10.15 | |
python -m build --sdist --wheel "--config-setting=--plat-name=macosx_10_15_x86_64" | |
# hack wheel name to be recognized by macos 10.15 | |
wheel_name=$(ls dist/*.whl) | |
new_wheel_name=$(echo $wheel_name | sed -e 's/macosx_.*_x86_64.whl/macosx_10_15_x86_64.whl/') | |
echo "mv $wheel_name $new_wheel_name" | |
mv $wheel_name $new_wheel_name | |
- name: Update build cache from wheels | |
if: steps.cache-build-dependencies.outputs.cache-hit != 'true' | |
run: 7z x dist/*.whl -y | |
- name: Upload as build artifacts | |
uses: actions/upload-artifact@v3 | |
with: | |
name: wheels | |
path: dist/*.whl | |
build-ubuntu: | |
needs: [setup] | |
if: needs.setup.outputs.do_ubuntu == 'true' | |
strategy: | |
matrix: | |
os: ${{ fromJSON(needs.setup.outputs.build).ubuntu }} | |
python-version: ${{ fromJSON(needs.setup.outputs.python_version) }} | |
fail-fast: false | |
defaults: | |
run: | |
shell: bash | |
runs-on: ${{ matrix.os }} | |
steps: | |
- name: Checkout scikit-decide source code | |
uses: actions/checkout@v3 | |
with: | |
submodules: true | |
fetch-depth: 0 | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Load cached venv | |
id: cached-pip-wheels | |
uses: actions/cache@v3 | |
with: | |
path: ~/.cache | |
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }} | |
- name: Restore Boost cache | |
uses: actions/cache@v3 | |
id: cache-boost | |
with: | |
path: ${{env.BOOST_DIR}} | |
key: BOOST_${{env.BOOST_VERSION}} | |
- name: Install Boost | |
if: steps.cache-boost.outputs.cache-hit != 'true' | |
run: | | |
mkdir -p $BOOST_DIR | |
curl --silent --location --output - \ | |
https://boostorg.jfrog.io/artifactory/main/release/$BOOST_VERSION/source/boost_${BOOST_VERSION//./_}.tar.bz2 |\ | |
tar jxf - -C $BOOST_DIR --strip-components=1 boost_${BOOST_VERSION//./_}/boost | |
shell: bash | |
- name: Restore build dependencies | |
id: cache-build-dependencies | |
uses: actions/cache@v3 | |
with: | |
path: | | |
skdecide/hub/bin | |
skdecide/hub/share | |
skdecide/hub/*.msc | |
key: ${{ runner.os }}-cache-deps | |
- name: Update SKDECIDE_SKIP_DEPS | |
if: steps.cache-build-dependencies.outputs.cache-hit != 'true' | |
run: echo "SKDECIDE_SKIP_DEPS=0" >> $GITHUB_ENV | |
- name: Restore docker dev image | |
id: cache-dev-deps | |
uses: actions/cache@v3 | |
with: | |
path: /tmp/docker | |
key: dev-deps-${{ runner.os }}-${{ hashFiles('scripts/build-skdecide_dev.sh', 'scripts/Dockerfile_x86_64_dev') }} | |
- name: Restore ccache cache | |
id: ccache-restore | |
uses: actions/cache@v3 | |
with: | |
path: .ccache | |
key: ccache-${{ runner.os }}-py${{ matrix.python-version }}-${{ github.run_id }}-${{github.run_number}} | |
restore-keys: ccache-${{ runner.os }}-py${{ matrix.python-version }} | |
- name: Build wheels | |
run: | | |
# Load skdecide_dev image from cache, or build it if not found | |
if test -f /tmp/docker/skdecide_dev.tar; then | |
docker image load -i /tmp/docker/skdecide_dev.tar | |
else | |
docker build -f scripts/Dockerfile_x86_64_dev -t skdecide_dev . | |
mkdir -p /tmp/docker | |
docker image save -o /tmp/docker/skdecide_dev.tar skdecide_dev | |
fi | |
# The existence of .ccache directory triggers ccache use in builds-manylinux-wheels.sh | |
test -d .ccache || mkdir .ccache | |
docker build -f scripts/Dockerfile_x86_64 -t skdecide_x86_64 --build-arg PYTHON_VERSION=${{matrix.python-version}} --build-arg SKDECIDE_SKIP_DEPS=${SKDECIDE_SKIP_DEPS} --build-arg BOOST_DIR=${BOOST_DIR} . | |
# Fetch wheels from Docker | |
docker run --rm -v $PWD:/mytmp skdecide_x86_64 cp -r /io/dist /mytmp | |
# Fetch ccache from Docker | |
docker run --rm -v $PWD:/mytmp skdecide_x86_64 cp -r /io/.ccache /mytmp | |
- name: Update build cache from wheels | |
if: steps.cache-build-dependencies.outputs.cache-hit != 'true' | |
run: 7z x dist/*.whl -y | |
- name: Upload as build artifacts | |
uses: actions/upload-artifact@v3 | |
with: | |
name: wheels | |
path: dist/*.whl | |
test-windows: | |
needs: [build-macos, build-ubuntu, build-windows, setup] | |
strategy: | |
matrix: | |
os: ${{ fromJSON(needs.setup.outputs.test).windows }} | |
python-version: ${{ fromJSON(needs.setup.outputs.python_version) }} | |
compiler: [gnu] | |
fail-fast: true | |
runs-on: ${{ matrix.os }} | |
defaults: | |
run: | |
shell: bash | |
env: | |
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.6.3/MiniZincIDE-2.6.3-bundled-setup-win64.exe | |
minizinc_downloaded_filepath: minizinc_setup.exe | |
minizinc_install_cmdline: cmd //c "minizinc_setup.exe /verysilent /currentuser /norestart /suppressmsgboxes /sp" | |
steps: | |
- uses: actions/checkout@v3 | |
with: | |
submodules: true | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Install dependencies | |
run: | | |
# preinstall gym==0.21.0 with legacy method (python setup.py) because its requirements list is broken | |
python -m pip install "pip==22" # starting with pip 23.1, gym 0.21.0 is not intallable anymore | |
python -m pip uninstall -y wheel # wheel must not be here to fall back directly to python setup.py | |
python -m pip install gym==0.21.0 --no-use-pep517 | |
# install remaining dependencies | |
python -m pip install --upgrade pip | |
python -m pip install pytest | |
- name: Download artifacts | |
uses: actions/download-artifact@v3 | |
with: | |
name: wheels | |
path: wheels | |
- name: get MininZinc path to cache | |
id: get-mzn-cache-path | |
run: | | |
echo "path=${{ env.minizinc_cache_path }}" >> $GITHUB_OUTPUT # expands variables | |
- name: Restore MiniZinc cache | |
id: cache-minizinc | |
uses: actions/cache@v3 | |
with: | |
path: ${{ steps.get-mzn-cache-path.outputs.path }} | |
key: ${{ env.minizinc_url }} | |
- name: Download MiniZinc | |
if: steps.cache-minizinc.outputs.cache-hit != 'true' | |
run: | | |
curl -o "${{ env.minizinc_downloaded_filepath }}" -L ${{ env.minizinc_url }} | |
- name: Install MiniZinc | |
if: steps.cache-minizinc.outputs.cache-hit != 'true' | |
run: | | |
${{ env.minizinc_install_cmdline }} | |
- name: Test minizinc install | |
run: | | |
${{ env.minizinc_config_cmdline }} | |
minizinc --version | |
- name: Install scikit-decide | |
run: | | |
python_version=${{ matrix.python-version }} | |
wheelfile=$(ls ./wheels/scikit_decide*-cp${python_version/\./}-*win*.whl) | |
pip install ${wheelfile}[all] | |
- name: Test with pytest | |
run: | | |
# configure minizinc | |
${{ env.minizinc_config_cmdline }} | |
# test minizinc | |
python -c "import minizinc; print(minizinc.default_driver.minizinc_version); minizinc.Solver.lookup('gecode')" | |
# run pytest | |
pytest -v -s tests/autocast | |
pytest -v -s tests/solvers/cpp | |
pytest -v -s tests/solvers/python | |
pytest -v -s tests/scheduling | |
test-macos: | |
needs: [build-macos, build-ubuntu, build-windows, setup] | |
strategy: | |
matrix: | |
os: ${{ fromJSON(needs.setup.outputs.test).macos }} | |
python-version: ${{ fromJSON(needs.setup.outputs.python_version) }} | |
fail-fast: true | |
runs-on: ${{ matrix.os }} | |
env: | |
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.6.3/MiniZincIDE-2.6.3-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/. | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Install libomp package | |
run: brew install libomp | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Install dependencies | |
run: | | |
# preinstall gym==0.21.0 with legacy method (python setup.py) because its requirements list is broken | |
python -m pip install "pip==22" # starting with pip 23.1, gym 0.21.0 is not intallable anymore | |
python -m pip uninstall -y wheel # wheel must not be here to fall back directly to python setup.py | |
python -m pip install gym==0.21.0 --no-use-pep517 | |
# install remaining dependencies | |
python -m pip install --upgrade pip | |
python -m pip install pytest | |
- name: Download artifacts | |
uses: actions/download-artifact@v3 | |
with: | |
name: wheels | |
path: wheels | |
- name: Create bin/ | |
run: mkdir -p bin | |
- name: get MininZinc path to cache | |
id: get-mzn-cache-path | |
run: | | |
echo "path=${{ env.minizinc_cache_path }}" >> $GITHUB_OUTPUT # expands variables | |
- name: Restore MiniZinc cache | |
id: cache-minizinc | |
uses: actions/cache@v3 | |
with: | |
path: ${{ steps.get-mzn-cache-path.outputs.path }} | |
key: ${{ env.minizinc_url }} | |
- name: Download MiniZinc | |
if: steps.cache-minizinc.outputs.cache-hit != 'true' | |
run: | | |
curl -o "${{ env.minizinc_downloaded_filepath }}" -L ${{ env.minizinc_url }} | |
- name: Install MiniZinc | |
if: steps.cache-minizinc.outputs.cache-hit != 'true' | |
run: | | |
${{ env.minizinc_install_cmdline }} | |
- name: Test minizinc install | |
run: | | |
${{ env.minizinc_config_cmdline }} | |
minizinc --version | |
- name: Install scikit-decide | |
run: | | |
python_version=${{ matrix.python-version }} | |
wheelfile=$(ls ./wheels/scikit_decide*-cp${python_version/\./}-*macos*.whl) | |
pip install ${wheelfile}[all] | |
- name: Test with pytest | |
run: | | |
# configure minizinc | |
${{ env.minizinc_config_cmdline }} | |
# test minizinc | |
python -c "import minizinc; print(minizinc.default_driver.minizinc_version); minizinc.Solver.lookup('gecode')" | |
# run pytest | |
pytest -v -s tests/autocast | |
pytest -v -s tests/solvers/cpp | |
pytest -v -s tests/solvers/python | |
pytest -v -s tests/scheduling | |
test-ubuntu: | |
needs: [build-macos, build-ubuntu, build-windows, setup] | |
strategy: | |
matrix: | |
os: ${{ fromJSON(needs.setup.outputs.test).ubuntu }} | |
python-version: ${{ fromJSON(needs.setup.outputs.python_version) }} | |
fail-fast: true | |
runs-on: ${{ matrix.os }} | |
env: | |
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.6.3/MiniZincIDE-2.6.3-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 .. | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Install dependencies | |
run: | | |
# preinstall gym==0.21.0 with legacy method (python setup.py) because its requirements list is broken | |
python -m pip install "pip==22" # starting with pip 23.1, gym 0.21.0 is not intallable anymore | |
python -m pip uninstall -y wheel # wheel must not be here to fall back directly to python setup.py | |
python -m pip install gym==0.21.0 --no-use-pep517 | |
# install remaining dependencies | |
python -m pip install --upgrade pip | |
python -m pip install pytest docopt commonmark | |
- name: Download artifacts | |
uses: actions/download-artifact@v3 | |
with: | |
name: wheels | |
path: wheels | |
- name: Create bin/ | |
run: mkdir -p bin | |
- name: get MininZinc path to cache | |
id: get-mzn-cache-path | |
run: | | |
echo "path=${{ env.minizinc_cache_path }}" >> $GITHUB_OUTPUT # expands variables | |
- name: Restore MiniZinc cache | |
id: cache-minizinc | |
uses: actions/cache@v3 | |
with: | |
path: ${{ steps.get-mzn-cache-path.outputs.path }} | |
key: ${{ env.minizinc_url }} | |
- name: Download MiniZinc | |
if: steps.cache-minizinc.outputs.cache-hit != 'true' | |
run: | | |
curl -o "${{ env.minizinc_downloaded_filepath }}" -L ${{ env.minizinc_url }} | |
- name: Install MiniZinc | |
if: steps.cache-minizinc.outputs.cache-hit != 'true' | |
run: | | |
${{ env.minizinc_install_cmdline }} | |
- name: Test minizinc install | |
run: | | |
${{ env.minizinc_config_cmdline }} | |
minizinc --version | |
- name: Install scikit-decide | |
run: | | |
python_version=${{ matrix.python-version }} | |
wheelfile=$(ls ./wheels/scikit_decide*-cp${python_version/\./}-*manylinux*.whl) | |
pip install ${wheelfile}[all] | |
- name: Test with pytest | |
run: | | |
# configure minizinc | |
${{ env.minizinc_config_cmdline }} | |
# test minizinc | |
python -c "import minizinc; print(minizinc.default_driver.minizinc_version); minizinc.Solver.lookup('gecode')" | |
# run pytest | |
pytest -v -s tests/autocast | |
pytest -v -s tests/solvers/cpp | |
pytest -v -s tests/solvers/python | |
pytest -v -s tests/scheduling | |
- name: Test python block codes from guide | |
run: | | |
python scripts/md2py.py docs/guide/README.md tests/test_guide.py | |
python tests/test_guide.py | |
build-doc: | |
needs: [build-macos, build-ubuntu, build-windows, test-ubuntu, setup] | |
if: needs.setup.outputs.build_doc == 'true' | |
strategy: | |
matrix: | |
os: [ubuntu-latest] | |
python-version: ["3.7"] | |
fail-fast: false | |
runs-on: ${{ matrix.os }} | |
steps: | |
- name: Set env variables for github+binder links in doc | |
run: | | |
# binder environment repo and branch | |
AUTODOC_BINDER_ENV_GH_REPO_NAME=${GITHUB_REPOSITORY} | |
AUTODOC_BINDER_ENV_GH_BRANCH="binder" | |
# notebooks source repo and branch depending if it is a commit push or a PR | |
if [[ $GITHUB_REF == refs/pull* ]]; | |
then | |
AUTODOC_NOTEBOOKS_REPO_URL="${GITHUB_SERVER_URL}/${{ github.event.pull_request.head.repo.full_name }}" | |
AUTODOC_NOTEBOOKS_BRANCH=${GITHUB_HEAD_REF} | |
elif [[ $GITHUB_REF == refs/heads* ]]; | |
then | |
AUTODOC_NOTEBOOKS_REPO_URL=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} | |
AUTODOC_NOTEBOOKS_BRANCH=${GITHUB_REF/refs\/heads\//} | |
fi | |
# export in GITHUB_ENV for next steps | |
echo "AUTODOC_BINDER_ENV_GH_REPO_NAME=${AUTODOC_BINDER_ENV_GH_REPO_NAME}" >> $GITHUB_ENV | |
echo "AUTODOC_BINDER_ENV_GH_BRANCH=${AUTODOC_BINDER_ENV_GH_BRANCH}" >> $GITHUB_ENV | |
echo "AUTODOC_NOTEBOOKS_REPO_URL=${AUTODOC_NOTEBOOKS_REPO_URL}" >> $GITHUB_ENV | |
echo "AUTODOC_NOTEBOOKS_BRANCH=${AUTODOC_NOTEBOOKS_BRANCH}" >> $GITHUB_ENV | |
# check computed variables | |
echo "Binder env: ${AUTODOC_BINDER_ENV_GH_REPO_NAME}/${AUTODOC_BINDER_ENV_GH_BRANCH}" | |
echo "Notebooks source: ${AUTODOC_NOTEBOOKS_REPO_URL}/tree/${AUTODOC_NOTEBOOKS_BRANCH}" | |
- uses: actions/checkout@v3 | |
with: | |
submodules: true | |
- name: Setup python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Download artifacts | |
uses: actions/download-artifact@v3 | |
with: | |
name: wheels | |
path: wheels | |
- name: Install scikit-decide | |
run: | | |
# preinstall gym==0.21.0 with legacy method (python setup.py) because its requirements list is broken | |
python -m pip install "pip==22" # starting with pip 23.1, gym 0.21.0 is not intallable anymore | |
python -m pip uninstall -y wheel # wheel must not be here to fall back directly to python setup.py | |
python -m pip install gym==0.21.0 --no-use-pep517 | |
# upgrade pip | |
python -m pip install --upgrade pip | |
# find proper wheel and install it | |
python_version=${{ matrix.python-version }} | |
wheelfile=$(ls ./wheels/scikit_decide*-cp${python_version/\./}-*manylinux*.whl) | |
pip install ${wheelfile}[all] | |
- name: generate documentation | |
run: | | |
yarn global add vuepress && yarn install | |
export NODE_OPTIONS=--openssl-legacy-provider # avoid issue with node 18 and current dependencies (ok because no interaction with external network during the build) | |
export DO_SKIP_MZN_CHECK=1 # avoid having to install minizinc for discrete-optimization | |
yarn docs:build | |
touch docs/.vuepress/dist/.nojekyll | |
- name: upload as artifact | |
uses: actions/upload-artifact@v3 | |
with: | |
name: doc | |
path: docs/.vuepress/dist | |
upload-doc: | |
needs: [build-doc, test-windows, test-macos, test-ubuntu] | |
if: github.ref == 'refs/heads/master' | |
strategy: | |
matrix: | |
os: [ubuntu-latest] | |
python-version: ["3.7"] | |
fail-fast: false | |
runs-on: ${{ matrix.os }} | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Download artifacts | |
uses: actions/download-artifact@v3 | |
with: | |
name: doc | |
path: docs/.vuepress/dist | |
- name: Deploy documentation in root folder on GH pages 🚀 | |
uses: JamesIves/github-pages-deploy-action@v4 | |
with: | |
branch: gh-pages # The branch the action should deploy to. | |
folder: docs/.vuepress/dist # The folder the action should deploy. | |
target-folder: / # The folder the action should deploy to. | |
commit-message: publish documentation | |
clean-exclude: | | |
"version/*" | |
upload-nightly: | |
if: (github.ref == 'refs/heads/master') && (github.repository == 'airbus/scikit-decide') && (github.event_name == 'schedule') | |
needs: [test-ubuntu, test-macos, test-windows] | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/download-artifact@v3 | |
with: | |
name: wheels | |
path: dist/ | |
- run: | | |
zip -r dist.zip dist/ | |
- uses: actions/github-script@v6 | |
id: asset | |
with: | |
script: | | |
const fs = require('fs'); | |
// Get the ref for master | |
const master_sha = '${{ github.sha }}'; | |
console.log(`master reference ${master_sha}`); | |
// Retrieve ref for tag `nightly` | |
let ref_nightly = null; | |
try { | |
ref_nightly = await github.rest.git.getRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: 'tags/nightly', | |
}); | |
if (ref_nightly.data.object.sha === master_sha) { | |
return ''; | |
} | |
} catch (err) { | |
// The tag does not exist so let's create it | |
ref_nightly = await github.rest.git.createRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: 'refs/tags/nightly', | |
sha: master_sha, | |
}); | |
} | |
// Call the GitHub API to get a release by tag | |
let release = null; | |
try { | |
release = await github.rest.repos.getReleaseByTag({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
tag: 'nightly', | |
}); | |
console.log(`Found release ${release.data.tag_name} ${release.data.draft} ${release.data.prerelease}`); | |
} catch (err) { | |
console.log(`Release 'nightly' not found`); | |
// If the release doesn't exist, create it | |
release = await github.rest.repos.createRelease({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
tag_name: 'nightly', | |
name: 'nightly', | |
body: 'Nightly release crafted with ♥️ somewhere on 🌎', | |
draft: false, | |
prerelease: true, | |
}); | |
console.log(`Created release ${release.data.tag_name} ${release.data.draft} ${release.data.prerelease}`); | |
} | |
console.log(`Release does exist with tag ${release.data.tag_name} [${release.data.draft} ${release.data.prerelease}]`); | |
// At this stage both tag & release exist | |
// Update nightly tag | |
await github.rest.git.updateRef({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
ref: 'tags/nightly', | |
sha: master_sha, | |
force: true, | |
}); | |
console.log(`Updated tag with sha ${ref_nightly.data.object.sha}`); | |
// Update the release | |
await github.rest.repos.updateRelease({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
release_id: release.data.id, | |
tag_name: 'nightly', | |
name: 'nightly', | |
body: 'Nightly release crafted with ♥️ somewhere on 🌎', | |
draft: false, | |
prerelease: true, | |
}); | |
console.log(`Updated ${release.data.tag_name} nightly release ${release.data.draft} ${release.data.prerelease}`); | |
// Get all tags and keep the newest one starting by v | |
let newest_tag = { name: 'v0.0.0' }; | |
const tags = await github.rest.repos.listTags({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
}); | |
// Keep latest tag | |
for (const tag of tags.data) { | |
if (tag.name.startsWith('v')) { | |
if (tag.name.localeCompare(newest_tag.name, undefined, { numeric: true}) > 0) { | |
newest_tag = tag; | |
} | |
} | |
} | |
console.log(`Previous release has tag ${newest_tag.name} → ${newest_tag.commit.sha}`); | |
// Count all commits between HEAD and newest tag | |
// Limited to 250 commits | |
const distance = await github.rest.repos.compareCommitsWithBasehead({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
basehead: `${newest_tag.commit.sha}...${master_sha}`, | |
}).then(d => d.data.total_commits); | |
// Zip a zip file from dist directory | |
let release_name = `nightly_${distance}_${master_sha.substring(0,8)}` + '.zip'; | |
console.log(`Release file name: ${release_name}`); | |
fs.renameSync('dist.zip', release_name); | |
// Upload the zip file to GitHub | |
const uploadedAsset = await github.rest.repos.uploadReleaseAsset({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
release_id: release.data.id, | |
name: release_name, | |
data: fs.readFileSync(release_name), | |
headers: { | |
'content-type': 'application/zip', | |
}, | |
}); | |
return uploadedAsset.data.browser_download_url; | |
result-encoding: string | |
- uses: actions/checkout@v3 | |
# only if a nightly release occured | |
if: ${{ steps.asset.outputs.result != '' }} | |
with: | |
ref: binder | |
- name: Force a rebuild of binder environment | |
# only if a nightly release occured | |
if: ${{ steps.asset.outputs.result != '' }} | |
run: | | |
# update nightly_build_url to install last nightly build over last release | |
sed -i -e 's|nightly_build_url="[^"]*"|nightly_build_url="${{ steps.asset.outputs.result }}"|' postBuild | |
git config user.name "Actions" | |
git config user.email "[email protected]" | |
git commit postBuild -m "Use scikit-decide last nightly build" | |
git push origin binder | |
- uses: actions/checkout@v3 # checkout triggering branch to get scripts/trigger_binder.sh | |
# only if a nightly release occured | |
if: ${{ steps.asset.outputs.result != '' }} | |
- name: Trigger a build on each BinderHub deployments in the mybinder.org federation | |
# only if a nightly release occured | |
if: ${{ steps.asset.outputs.result != '' }} | |
run: | | |
bash scripts/trigger_binder.sh https://gke.mybinder.org/build/gh/${GITHUB_REPOSITORY}/binder | |
bash scripts/trigger_binder.sh https://ovh.mybinder.org/build/gh/${GITHUB_REPOSITORY}/binder | |
bash scripts/trigger_binder.sh https://turing.mybinder.org/build/gh/${GITHUB_REPOSITORY}/binder | |
bash scripts/trigger_binder.sh https://gesis.mybinder.org/build/gh/${GITHUB_REPOSITORY}/binder |