Skip to content

Bump conda-incubator/setup-miniconda from 3 to 4 #1830

Bump conda-incubator/setup-miniconda from 3 to 4

Bump conda-incubator/setup-miniconda from 3 to 4 #1830

# There are three things in this file that need regular maintenance:
#
# 1) Updating GraphBLAS version
# - We should probably make this a variable to make it easier to change
# 2) Updating dependency versions in "Update env"
# - This typically means adding new major versions of packages
# - `scripts/check_versions.sh` may be helpful to get version info
# 3) Updating Python versions (search for "Python version" or "pyver")
name: Tests
on:
# Run this workflow every time a PR is opened or a new commit is pushed to the PR
pull_request:
# Run this workflow every time a PR is merged to main
push:
branches:
- main
# concurrency:
# group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
# cancel-in-progress: true
jobs:
rngs:
# To achieve consistent coverage, we need a little bit of correlated collaboration.
# Specifically, we need to make sure we run with and without `--mapnumpy` on slow tests.
# We also use correlated RNG to run both backends on slow tests.
#
# {A,B,C,D},yes = --mapnumpy
# {A,B,C,D},no = --no-mapnumpy
# {D,E,F,G},yes = --backend=suitesparse
# {D,E,F,G},no = --backend=suitesparse-vanilla
#
# linux windows osx
# +-----------------------+
# A E normal | yes no |
# A E bizarro | no yes |
# B F normal | no yes |
# B F bizarro | yes no |
# C G normal | no yes |
# C G bizarro | yes no |
# D H normal | yes no |
# D H bizarro | no yes |
# +-----------------------+
runs-on: ubuntu-latest
outputs:
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idoutputs
mapnumpy: ${{ steps.mapnumpy.outputs.selected }}
backend: ${{ steps.backend.outputs.selected }}
steps:
- name: RNG for mapnumpy
uses: ddradar/choose-random-action@v4.1.0
id: mapnumpy
with:
contents: |
A
B
C
D
weights: |
1
1
1
1
- name: RNG for backend
uses: ddradar/choose-random-action@v4.1.0
id: backend
with:
contents: |
E
F
G
H
weights: |
1
1
1
1
build_and_test:
needs: rngs
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash -l {0}
strategy:
# To "stress test" in CI, set `fail-fast` to `false` and use `repeat` in matrix below
fail-fast: false
# The build matrix is [os]x[slowtask] and then randomly chooses [pyver] and [sourcetype].
# This should ensure we'll have full code coverage (i.e., no chance of getting unlucky),
# since we need to run all slow tests on Windows and non-Windoes OSes.
matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
slowtask: ["pytest_normal", "pytest_bizarro", "notebooks"]
# repeat: [1, 2, 3] # For stress testing
include:
# Additional architectures: run pytest_normal only to limit CI cost
- os: macos-15-intel # macOS x86_64 (Intel)
slowtask: pytest_normal
- os: ubuntu-24.04-arm # Linux aarch64
slowtask: pytest_normal
# Uncomment these temporarily for stress testing
# - os: macos-15-intel # macOS x86_64 (Intel)
# slowtask: pytest_bizarro
# - os: ubuntu-24.04-arm # Linux aarch64
# slowtask: pytest_bizarro
# - os: macos-15-intel # macOS x86_64 (Intel)
# slowtask: notebooks
# - os: ubuntu-24.04-arm # Linux aarch64
# slowtask: notebooks
env:
# Wheels on OS X come with an OpenMP that conflicts with OpenMP from conda-forge.
# Setting this is a workaround.
KMP_DUPLICATE_LIB_OK: ${{ contains(matrix.os, 'macos') && 'TRUE' || 'FALSE' }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- name: RNG for Python version
uses: ddradar/choose-random-action@v4.1.0
id: pyver
with:
# We should support major Python versions for at least 36 months as per SPEC 0
# We may be able to support pypy if anybody asks for it
# 3.9.16 0_73_pypy
contents: |
3.11
3.12
3.13
3.14
weights: |
1
1
1
1
- name: RNG for source of python-suitesparse-graphblas
uses: ddradar/choose-random-action@v4.1.0
id: sourcetype
with:
# Weights must be natural numbers, so set weights to very large to skip one
# (such as if 'upstream' is known to not work).
contents: |
conda-forge
wheel
source
upstream
weights: |
1
1
1
1
- name: Setup conda
uses: conda-incubator/setup-miniconda@v4
id: setup_conda
with:
auto-update-conda: true
python-version: ${{ steps.pyver.outputs.selected }}
channels: conda-forge${{ contains(steps.pyver.outputs.selected, 'pypy') && ',defaults' || '' }}
conda-remove-defaults: ${{ contains(steps.pyver.outputs.selected, 'pypy') && 'false' || 'true' }}
channel-priority: ${{ contains(steps.pyver.outputs.selected, 'pypy') && 'flexible' || 'strict' }}
activate-environment: graphblas
auto-activate-base: false
- name: Update env
run: |
# Pick random compatible dependency versions using the version picker script.
# See scripts/ci_pick_versions.py for version pools and constraint logic.
pyver=$(python -c "v='${{ steps.pyver.outputs.selected }}'.split('.');print(f'{v[0]}.{v[1]}')")
eval "$(python scripts/ci_pick_versions.py --python "$pyver" --source ${{ steps.sourcetype.outputs.selected }})"
# Set up psg conda package variable (conda-forge installs via conda, others via pip)
psg=""
if [[ ${{ steps.sourcetype.outputs.selected}} == "conda-forge" && -n "$psgver" ]] ; then
psg=python-suitesparse-graphblas${psgver}
fi
# Decide whether to install numba and sparse.
# Random skip: pypy always, 20% on Windows, 40% on other OSes (unless notebooks).
awkward=awkward${akver}
if [[ ${{ contains(steps.pyver.outputs.selected, 'pypy') }} == true ||
( ${{ matrix.slowtask != 'notebooks'}} == true && (
( ${{ matrix.os == 'windows-latest' }} == true && $(python -c 'import random ; print(random.random() < .2)') == True ) ||
( ${{ matrix.os == 'windows-latest' }} == false && $(python -c 'import random ; print(random.random() < .4)') == True ))) ]]
then
echo "skipping numba"
numba=""
numbaver=NA
sparse=""
sparsever=NA
if [[ ${{ contains(steps.pyver.outputs.selected, 'pypy') }} ]]; then
awkward=""
akver=NA
npver=""
spver=""
pdver=""
yamlver=""
fi
elif [[ ${numbaver} == "NA" ]] ; then
# Version picker already determined numba is incompatible
numba=""
sparse=""
sparsever=NA
else
numba=numba${numbaver}
if [[ $sparsever == "NA" ]] ; then
sparse=""
else
sparse=sparse${sparsever}
fi
fi
echo "versions: np${npver} sp${spver} pd${pdver} ak${akver} nx${nxver} numba${numbaver} yaml${yamlver} sparse${sparsever} psg${psgver}"
set -x # echo on
$(command -v mamba || command -v conda) install -c nodefaults \
packaging pytest coverage pytest-randomly cffi donfig tomli c-compiler make \
pyyaml${yamlver} ${sparse} pandas${pdver} scipy${spver} numpy${npver} ${awkward} \
networkx${nxver} ${numba} ${psg} \
${{ matrix.slowtask == 'pytest_bizarro' && 'black' || '' }} \
${{ matrix.slowtask == 'notebooks' && 'matplotlib nbconvert jupyter "ipython>=7" drawsvg' || '' }} \
${{ steps.sourcetype.outputs.selected == 'upstream' && 'cython' || '' }} \
${{ steps.sourcetype.outputs.selected != 'wheel' && '"graphblas>=7.4"' || '' }} \
${{ contains(steps.pyver.outputs.selected, 'pypy') && 'pypy' || '' }} \
${{ matrix.os == 'windows-latest' && 'cmake' || 'm4' }} \
# ${{ matrix.os != 'windows-latest' && 'pytest-forked' || '' }} # to investigate crashes
- name: Build extension module
run: |
if [[ ${{ steps.sourcetype.outputs.selected }} == "wheel" ]]; then
# Add --pre if installing a pre-release
pip install --no-deps --only-binary ":all:" suitesparse-graphblas${psgver}
# Add the below line to the conda install command above if installing from test.pypi.org
# ${{ steps.sourcetype.outputs.selected == 'wheel' && 'setuptools setuptools-git-versioning wheel cython' || '' }} \
# pip install --no-deps --only-binary ":all:" --index-url https://test.pypi.org/simple/ "suitesparse-graphblas>=7.4.3"
elif [[ ${{ steps.sourcetype.outputs.selected }} == "source" ]]; then
# Add --pre if installing a pre-release
pip install --no-deps --no-binary suitesparse-graphblas suitesparse-graphblas${psgver}
# Add the below line to the conda install command above if installing from test.pypi.org
# ${{ steps.sourcetype.outputs.selected == 'source' && 'setuptools setuptools-git-versioning wheel cython' || '' }} \
# pip install --no-deps --no-build-isolation --no-binary suitesparse-graphblas --index-url https://test.pypi.org/simple/ suitesparse-graphblas==7.4.3.3
elif [[ ${{ steps.sourcetype.outputs.selected }} == "upstream" ]]; then
pip install --no-deps git+https://github.com/GraphBLAS/python-suitesparse-graphblas.git@main#egg=suitesparse-graphblas
fi
pip install --no-deps -e .
- name: python-suitesparse-graphblas tests
run: |
# Don't use our conftest.py ; allow `test_print_jit_config` to fail if it doesn't exist
(cd ..
pytest --pyargs suitesparse_graphblas -s -k test_print_jit_config || true
pytest -v --pyargs suitesparse_graphblas || true)
- name: JIT diagnostics
run: python scripts/jit_diagnostics.py || true
- name: Unit tests
run: |
A=${{ needs.rngs.outputs.mapnumpy == 'A' || '' }} ; B=${{ needs.rngs.outputs.mapnumpy == 'B' || '' }}
C=${{ needs.rngs.outputs.mapnumpy == 'C' || '' }} ; D=${{ needs.rngs.outputs.mapnumpy == 'D' || '' }}
E=${{ needs.rngs.outputs.backend == 'E' || '' }} ; F=${{ needs.rngs.outputs.backend == 'F' || '' }}
G=${{ needs.rngs.outputs.backend == 'G' || '' }} ; H=${{ needs.rngs.outputs.backend == 'H' || '' }}
normal=${{ matrix.slowtask == 'pytest_normal' || '' }} ; bizarro=${{ matrix.slowtask == 'pytest_bizarro' || '' }}
ubuntu=${{ matrix.os == 'ubuntu-latest' || '' }} ; windows=${{ matrix.os == 'windows-latest' || '' }} ; macos=${{ matrix.os == 'macos-latest' || '' }}
mapnumpy='--mapnumpy' ; nomapnumpy='--no-mapnumpy' ; suitesparse='--backend=suitesparse' ; vanilla='--backend=suitesparse-vanilla'
args=$(
if [[ $A && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $A && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $B && $normal ]] ; then if [[ $macos ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $B && $bizarro ]] ; then if [[ $macos ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $C && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $C && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $D && $normal ]] ; then if [[ $macos ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $D && $bizarro ]] ; then if [[ $macos ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $E && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)$( \
if [[ $E && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)$( \
if [[ $F && $normal ]] ; then if [[ $macos ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)$( \
if [[ $F && $bizarro ]] ; then if [[ $macos ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)$( \
if [[ $G && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)$( \
if [[ $G && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)$( \
if [[ $H && $normal ]] ; then if [[ $macos ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)$( \
if [[ $H && $bizarro ]] ; then if [[ $macos ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)
echo ${args}
set -x # echo on
# pytest ${{ matrix.os != 'windows-latest' && '--forked' || '' }} \ # to investigate crashes
# --color=yes --randomly -v -s ${args} \
coverage run -m pytest --color=yes --randomly -v ${args} \
${{ matrix.slowtask == 'pytest_normal' && '--runslow' || '' }}
- name: Unit tests (bizarro scalars)
run: |
# Run tests again with Scalars being C scalars by default
find graphblas -type f -name "*.py" -print0 | xargs -0 sed -i -s \
-e '/# pragma: is_grbscalar/! s/is_cscalar=False/is_cscalar=True/g' \
-e '/# pragma: is_grbscalar/! s/is_cscalar = False/is_cscalar = True/g' \
-e '/# pragma: to_grb/ s/is_cscalar=True/is_cscalar=False/g' \
-e '/# pragma: to_grb/ s/is_cscalar = True/is_cscalar = False/g'
A=${{ needs.rngs.outputs.mapnumpy == 'A' || '' }} ; B=${{ needs.rngs.outputs.mapnumpy == 'B' || '' }}
C=${{ needs.rngs.outputs.mapnumpy == 'C' || '' }} ; D=${{ needs.rngs.outputs.mapnumpy == 'D' || '' }}
E=${{ needs.rngs.outputs.backend == 'E' || '' }} ; F=${{ needs.rngs.outputs.backend == 'F' || '' }}
G=${{ needs.rngs.outputs.backend == 'G' || '' }} ; H=${{ needs.rngs.outputs.backend == 'H' || '' }}
normal=${{ matrix.slowtask == 'pytest_normal' || '' }} ; bizarro=${{ matrix.slowtask == 'pytest_bizarro' || '' }}
ubuntu=${{ matrix.os == 'ubuntu-latest' || '' }} ; windows=${{ matrix.os == 'windows-latest' || '' }} ; macos=${{ matrix.os == 'macos-latest' || '' }}
mapnumpy='--mapnumpy' ; nomapnumpy='--no-mapnumpy' ; suitesparse='--backend=suitesparse' ; vanilla='--backend=suitesparse-vanilla'
args=$(
if [[ $A && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $A && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $B && $normal ]] ; then if [[ $macos ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $B && $bizarro ]] ; then if [[ $macos ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $C && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $C && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $D && $normal ]] ; then if [[ $macos ]] ; then echo " $mapnumpy" ; elif [[ $windows ]] ; then echo " $nomapnumpy" ; fi ; fi)$( \
if [[ $D && $bizarro ]] ; then if [[ $macos ]] ; then echo " $nomapnumpy" ; elif [[ $windows ]] ; then echo " $mapnumpy" ; fi ; fi)$( \
if [[ $E && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)$( \
if [[ $E && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)$( \
if [[ $F && $normal ]] ; then if [[ $macos ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)$( \
if [[ $F && $bizarro ]] ; then if [[ $macos ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)$( \
if [[ $G && $normal ]] ; then if [[ $ubuntu ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)$( \
if [[ $G && $bizarro ]] ; then if [[ $ubuntu ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)$( \
if [[ $H && $normal ]] ; then if [[ $macos ]] ; then echo " $suitesparse" ; elif [[ $windows ]] ; then echo " $vanilla" ; fi ; fi)$( \
if [[ $H && $bizarro ]] ; then if [[ $macos ]] ; then echo " $vanilla" ; elif [[ $windows ]] ; then echo " $suitesparse" ; fi ; fi)
echo ${args}
set -x # echo on
# pytest ${{ matrix.os != 'windows-latest' && '--forked' || '' }} \ # to investigate crashes
# --color=yes --randomly -v -s ${args} \
coverage run -a -m pytest --color=yes --randomly -v ${args} \
${{ matrix.slowtask == 'pytest_bizarro' && '--runslow' || '' }}
git checkout . # Undo changes to scalar default
- name: Miscellaneous tests
if: matrix.slowtask == 'pytest_normal'
run: |
# Test (and cover) automatic initialization
coverage run -a graphblas/tests/test_auto_init.py
coverage run -a graphblas/tests/test_external_init.py
# Test (and cover) lazy module loader
echo "from graphblas.agg import count" > script.py
coverage run -a script.py
echo "from graphblas import agg" > script.py # Does this still cover?
echo "from graphblas.core.operator import agg" >> script.py
coverage run -a script.py
# Tests lazy loading of lib, ffi, and NULL in gb.core
echo "from graphblas.core import base" > script.py
coverage run -a script.py
# Test another code pathway for loading lib
echo "from graphblas.core import lib" > script.py
coverage run -a script.py
rm script.py
# Tests whose coverage depend on order of tests :/
# TODO: understand why these are order-dependent and try to fix
coverage run -a -m pytest --color=yes -x --no-mapnumpy --runslow -k test_binaryop_attributes_numpy graphblas/tests/test_op.py
# coverage run -a -m pytest --color=yes -x --no-mapnumpy -k test_npmonoid graphblas/tests/test_numpyops.py --runslow
- name: More tests for coverage
if: matrix.slowtask == 'notebooks' && matrix.os == 'windows-latest'
run: |
# We use 'notebooks' slow task b/c it should have numba installed
coverage run -a -m pytest --color=yes --runslow --no-mapnumpy -p no:randomly -v -k 'test_commutes or test_bool_doesnt_get_too_large or test_npbinary or test_npmonoid or test_npsemiring'
coverage run -a -m pytest --color=yes --runslow --mapnumpy -p no:randomly -k 'test_bool_doesnt_get_too_large or test_npunary or test_binaryop_monoid_numpy'
coverage run -a -m pytest --color=yes -x --no-mapnumpy --runslow -k test_binaryop_attributes_numpy graphblas/tests/test_op.py
- name: Auto-generated code check
if: matrix.slowtask == 'pytest_bizarro'
run: |
# This step uses `black`
coverage run -a -m graphblas.core.automethods
coverage run -a -m graphblas.core.infixmethods
git diff --exit-code
- name: Coverage
run: |
coverage xml
coverage report --show-missing
- name: codecov
uses: codecov/codecov-action@v5
- name: Notebooks Execution check
if: matrix.slowtask == 'notebooks'
run: |
# Run notebooks only if numba is installed
if python -c 'import numba' 2> /dev/null ; then
jupyter nbconvert --to notebook --execute notebooks/*ipynb
fi