Skip to content

Update CI lock files [free-threaded] #132

Update CI lock files [free-threaded]

Update CI lock files [free-threaded] #132

Workflow file for this run

name: Unit tests
permissions:
contents: read
on:
push:
pull_request:
schedule:
# Nightly build at 02:30 UTC
- cron: "30 2 * * *"
# Manual run
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
VIRTUALENV: testvenv
TEST_DIR: ${{ github.workspace }}/tmp_folder
CCACHE_DIR: ${{ github.workspace }}/ccache
COVERAGE: 'true'
JUNITXML: 'test-data.xml'
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
if: github.repository == 'scikit-learn/scikit-learn'
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.12'
cache: 'pip'
- name: Install linters
run: |
source build_tools/shared.sh
# Include pytest compatibility with mypy
pip install pytest $(get_dep ruff min) $(get_dep mypy min) cython-lint
- name: Run linters
run: ./build_tools/linting.sh
- name: Run Meson OpenMP checks
run: |
pip install ninja meson scipy
python build_tools/check-meson-openmp-dependencies.py
retrieve-commit-message:
name: Retrieve the latest commit message
runs-on: ubuntu-latest
if: github.repository == 'scikit-learn/scikit-learn'
outputs:
message: ${{ steps.git-log.outputs.message }}
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- id: git-log
name: Retrieve the latest commit message
shell: bash
run: |
set -eu
message=$(git log --format=%B -n 1)
{
echo 'message<<EOF'
echo "${message}"
echo EOF
} >> "${GITHUB_OUTPUT}"
retrieve-selected-tests:
# Parse the commit message to check if `build_tools/azure/test_script.sh` should run
# only specific tests.
#
# If so, selected tests will be run with SKLEARN_TESTS_GLOBAL_RANDOM_SEED="all".
#
# The commit message must take the form:
# <title> [all random seeds]
# <test_name_1>
# <test_name_2>
# ...
name: Retrieve the selected tests
runs-on: ubuntu-latest
if: github.repository == 'scikit-learn/scikit-learn'
outputs:
tests: ${{ steps.selected-tests.outputs.tests }}
needs: [retrieve-commit-message]
steps:
- id: selected-tests
name: Retrieve the selected tests
shell: python
env:
COMMIT_MESSAGE: ${{ needs.retrieve-commit-message.outputs.message }}
run: |
import os
commit_message = os.environ["COMMIT_MESSAGE"]
# Retrieve selected tests from commit message
if "[all random seeds]" in commit_message:
selected_tests = commit_message.split("[all random seeds]")[1].strip()
selected_tests = selected_tests.replace("\n", " or ")
# quote 'selected_tests' to cover the case of multiple selected tests
selected_tests = f"{selected_tests!r}"
else:
selected_tests = ""
# Write selected tests to `GITHUB_OUTPUT`
with open(os.environ["GITHUB_OUTPUT"], "a") as file:
file.write(f"tests={selected_tests}\n")
unit-tests:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
if: github.repository == 'scikit-learn/scikit-learn'
needs: [lint, retrieve-commit-message, retrieve-selected-tests]
strategy:
# Ensures that all builds run to completion even if one of them fails
fail-fast: false
matrix:
include:
- name: Linux pymin_conda_forge_arm
os: ubuntu-24.04-arm
DISTRIB: conda
LOCK_FILE: build_tools/github/pymin_conda_forge_arm_linux-aarch64_conda.lock
- name: Linux x86-64 pylatest_conda_forge_mkl
os: ubuntu-22.04
DISTRIB: conda
LOCK_FILE: build_tools/azure/pylatest_conda_forge_mkl_linux-64_conda.lock
COVERAGE: true
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 42 # default global random seed
SCIPY_ARRAY_API: 1
# Tests that require large downloads over the networks are skipped in CI.
# Here we make sure, that they are still run on a regular basis.
SKLEARN_SKIP_NETWORK_TESTS: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && '0' || '1' }}
# Check compilation with Ubuntu 22.04 LTS (Jammy Jellyfish) and scipy from conda-forge
- name: Linux x86-64 pymin_conda_forge_openblas_ubuntu_2204
os: ubuntu-22.04
DISTRIB: conda
LOCK_FILE: build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_linux-64_conda.lock
SKLEARN_WARNINGS_AS_ERRORS: 1
COVERAGE: false
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 0 # non-default seed
# Linux build with minimum supported version of dependencies
- name: Linux x86-64 pymin_conda_forge_openblas_min_dependencies
os: ubuntu-22.04
DISTRIB: conda
LOCK_FILE: build_tools/azure/pymin_conda_forge_openblas_min_dependencies_linux-64_conda.lock
# Enable debug Cython directives to capture IndexError exceptions in
# combination with the -Werror::pytest.PytestUnraisableExceptionWarning
# flag for pytest.
# https://github.com/scikit-learn/scikit-learn/pull/24438
SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES: 1
SKLEARN_RUN_FLOAT32_TESTS: 1
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 2 # non-default seed
# Linux environment to test the latest available dependencies.
# It runs tests requiring lightgbm, pandas and PyAMG.
- name: Linux pylatest_pip_openblas_pandas
os: ubuntu-24.04
DISTRIB: conda
LOCK_FILE: build_tools/azure/pylatest_pip_openblas_pandas_linux-64_conda.lock
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 3 # non-default seed
SCIPY_ARRAY_API: 1
CHECK_PYTEST_SOFT_DEPENDENCY: true
SKLEARN_WARNINGS_AS_ERRORS: 1
# disable pytest-xdist to have 1 job where OpenMP and BLAS are not single
# threaded because by default the tests configuration (sklearn/conftest.py)
# makes sure that they are single threaded in each xdist subprocess.
PYTEST_XDIST_VERSION: none
PIP_BUILD_ISOLATION: true
# Linux environment to test that scikit-learn can be built against
# versions of numpy, scipy with ATLAS that comes with Ubuntu 24.04
# Noble Numbat i.e. numpy 1.26.4 and scipy 1.11.4
- name: Linux x86-64 ubuntu_atlas
os: ubuntu-24.04
DISTRIB: ubuntu
LOCK_FILE: build_tools/azure/ubuntu_atlas_lock.txt
COVERAGE: false
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 1 # non-default seed
- name: macOS pylatest_conda_forge_arm
os: macos-15
DISTRIB: conda
LOCK_FILE: build_tools/azure/pylatest_conda_forge_osx-arm64_conda.lock
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 5 # non-default seed
SCIPY_ARRAY_API: 1
PYTORCH_ENABLE_MPS_FALLBACK: 1
CHECK_PYTEST_SOFT_DEPENDENCY: true
- name: macOS x86-64 pylatest_conda_forge_mkl_no_openmp
os: macos-15-intel
DISTRIB: conda
LOCK_FILE: build_tools/azure/pylatest_conda_forge_mkl_no_openmp_osx-64_conda.lock
SKLEARN_TEST_NO_OPENMP: true
SKLEARN_SKIP_OPENMP_TEST: true
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 6 # non-default seed
- name: Windows x64 pymin_conda_forge_openblas
os: windows-latest
DISTRIB: conda
LOCK_FILE: build_tools/azure/pymin_conda_forge_openblas_win-64_conda.lock
SKLEARN_WARNINGS_AS_ERRORS: 1
# The Windows runner is typically much slower than other CI runners
# due to the lack of compiler cache. Running the tests with coverage
# enabled makes them run extra slow. Since very few parts of the
# code should have windows-specific code branches, code coverage
# collection is only done for the non-windows runners.
COVERAGE: false
# Enable debug Cython directives to capture IndexError exceptions in
# combination with the -Werror::pytest.PytestUnraisableExceptionWarning
# flag for pytest.
# https://github.com/scikit-learn/scikit-learn/pull/24438
SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES: 1
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 7 # non-default seed
env: ${{ matrix }}
steps: &unit-tests-steps
- name: Checkout
uses: actions/checkout@v6
# This step is necessary to access the job name the same way in both matrix and
# non-matrix jobs (like free-threaded or scipy-dev builds).
- name: Set JOB_NAME variable
shell: bash
run: |
if [[ -z "$JOB_NAME" ]]; then
echo "JOB_NAME=${{ matrix.name }}" >> $GITHUB_ENV
fi
- name: Create cache for ccache
uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-v1-${{ env.JOB_NAME }}-${{ hashFiles('**/*.pyx*', '**/*.pxd*', '**/*.pxi*', '**/*.h', '**/*.c', '**/*.cpp', format('{0}', env.LOCK_FILE)) }}
restore-keys: ccache-${{ env.JOB_NAME }}
- name: Set up conda
uses: conda-incubator/setup-miniconda@v3
if: ${{ startsWith(env.DISTRIB, 'conda') }}
with:
miniforge-version: latest
auto-activate-base: true
activate-environment: ""
- name: Build scikit-learn
run: bash -l build_tools/azure/install.sh
- name: Set random seed for nightly/manual runs
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
run: echo "SKLEARN_TESTS_GLOBAL_RANDOM_SEED=$((RANDOM % 100))" >> $GITHUB_ENV
shell: bash
- name: Run tests
env:
COMMIT_MESSAGE: ${{ needs.retrieve-commit-message.outputs.message }}
SELECTED_TESTS: ${{ needs.retrieve-selected-tests.outputs.tests }}
COVERAGE: ${{ env.COVERAGE == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
run: bash -l build_tools/azure/test_script.sh
- name: Run doctests in .py and .rst files
run: bash -l build_tools/azure/test_docs.sh
if: ${{ needs.retrieve-selected-tests.outputs.tests == ''}}
- name: Run pytest soft dependency test
run: bash -l build_tools/azure/test_pytest_soft_dependency.sh
if: ${{ env.CHECK_PYTEST_SOFT_DEPENDENCY == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
- name: Combine coverage reports from parallel test runners
run: bash -l build_tools/azure/combine_coverage_reports.sh
if: ${{ env.COVERAGE == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v5
if: ${{ env.COVERAGE == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
with:
files: ./coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
disable_search: true
- name: Update tracking issue
if: ${{ always() && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')}}
shell: bash
run: |
set -ex
pip install defusedxml PyGithub
python maint_tools/update_tracking_issue.py \
${{ secrets.BOT_GITHUB_TOKEN }} \
"$GITHUB_WORKFLOW $JOB_NAME" \
"$GITHUB_REPOSITORY" \
https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID \
--junit-file $TEST_DIR/$JUNITXML \
--auto-close false \
--job-name "$JOB_NAME"
free-threaded:
name: &free-threaded-job-name
Linux x86-64 pylatest_free_threaded
runs-on: ubuntu-latest
needs: [lint, retrieve-commit-message, retrieve-selected-tests]
if: contains(needs.retrieve-commit-message.outputs.message, '[free-threaded]') || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
env:
DISTRIB: conda-free-threaded
LOCK_FILE: build_tools/azure/pylatest_free_threaded_linux-64_conda.lock
COVERAGE: false
# Disable pytest-xdist to use multiple cores for stress-testing with pytest-run-parallel
PYTEST_XDIST_VERSION: none
# To be able to access the job name in the steps, it must be set as an env variable.
JOB_NAME: *free-threaded-job-name
steps: *unit-tests-steps
scipy-dev:
name: &scipy-dev-job-name
Linux x86-64 pylatest_pip_scipy_dev
runs-on: ubuntu-22.04
needs: [lint, retrieve-commit-message, retrieve-selected-tests]
if: contains(needs.retrieve-commit-message.outputs.message, '[scipy-dev]') || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
env:
DISTRIB: conda-pip-scipy-dev
LOCK_FILE: build_tools/azure/pylatest_pip_scipy_dev_linux-64_conda.lock
SKLEARN_WARNINGS_AS_ERRORS: 1
CHECK_PYTEST_SOFT_DEPENDENCY: true
# To be able to access the job name in the steps, it must be set as an env variable.
JOB_NAME: *scipy-dev-job-name
steps: *unit-tests-steps
debian-32bit:
name: &debian-32bit-job-name
Linux i386 debian_32bit
runs-on: ubuntu-24.04
needs: [lint, retrieve-commit-message, retrieve-selected-tests]
env:
DISTRIB: debian-32
LOCK_FILE: build_tools/azure/debian_32bit_lock.txt
SKLEARN_TESTS_GLOBAL_RANDOM_SEED: 4 # non-default seed
DOCKER_CONTAINER: i386/debian:trixie
# To be able to access the job name in the steps, it must be set as an env variable.
JOB_NAME: *debian-32bit-job-name
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Create cache for ccache
uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-v1-${{ env.JOB_NAME }}-${{ hashFiles('**/*.pyx*', '**/*.pxd*', '**/*.pxi*', '**/*.h', '**/*.c', '**/*.cpp', format('{0}', env.LOCK_FILE)) }}
restore-keys: ccache-${{ env.JOB_NAME }}
- name: Set up conda
uses: conda-incubator/setup-miniconda@v3
if: ${{ startsWith(env.DISTRIB, 'conda') }}
with:
miniforge-version: latest
auto-activate-base: true
activate-environment: ""
- name: Set random seed for nightly/manual runs
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
run: echo "SKLEARN_TESTS_GLOBAL_RANDOM_SEED=$((RANDOM % 100))" >> $GITHUB_ENV
- name: Start container
# Environment variable are passed when starting the container rather
# than in "Run tests step" for more standard jobs
env:
COMMIT_MESSAGE: ${{ needs.retrieve-commit-message.outputs.message }}
SELECTED_TESTS: ${{ needs.retrieve-selected-tests.outputs.tests }}
COVERAGE: ${{ env.COVERAGE == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
run: >
docker container run --rm
--volume $TEST_DIR:/temp_dir
--volume $PWD:/scikit-learn
--volume $CCACHE_DIR:/ccache
-w /scikit-learn
--detach
--name skcontainer
-e TEST_DIR=/temp_dir
-e CCACHE_DIR=/ccache
-e COVERAGE
-e DISTRIB
-e LOCK_FILE
-e JUNITXML
-e VIRTUALENV
-e PYTEST_XDIST_VERSION
-e SKLEARN_SKIP_NETWORK_TESTS
-e SELECTED_TESTS
-e CCACHE_COMPRESS
-e COMMIT_MESSAGE
-e JOB_NAME
-e SKLEARN_TESTS_GLOBAL_RANDOM_SEED
$DOCKER_CONTAINER
sleep 1000000
- name: Build scikit-learn
run: docker exec skcontainer bash -l build_tools/azure/install.sh
- name: Run tests
run: docker exec skcontainer bash -l build_tools/azure/test_script.sh
- name: Run doctests in .py and .rst files
run: docker exec skcontainer bash -l build_tools/azure/test_docs.sh
if: ${{ needs.retrieve-selected-tests.outputs.tests == ''}}
- name: Run pytest soft dependency test
run: docker exec skcontainer build_tools/azure/test_pytest_soft_dependency.sh
if: ${{ env.CHECK_PYTEST_SOFT_DEPENDENCY == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
- name: Combine coverage reports from parallel test runners
run: docker exec skcontainer bash -l build_tools/azure/combine_coverage_reports.sh
if: ${{ env.COVERAGE == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v5
if: ${{ env.COVERAGE == 'true' && needs.retrieve-selected-tests.outputs.tests == ''}}
with:
files: ./coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
disable_search: true
- name: Update tracking issue
if: ${{ always() && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')}}
run: |
set -ex
pip install defusedxml PyGithub
python maint_tools/update_tracking_issue.py \
${{ secrets.BOT_GITHUB_TOKEN }} \
"$GITHUB_WORKFLOW $JOB_NAME" \
"$GITHUB_REPOSITORY" \
https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID \
--junit-file $TEST_DIR/$JUNITXML \
--auto-close false \
--job-name "$JOB_NAME"