Skip to content

Commit 3135e1d

Browse files
timtreisclaude
andcommitted
Switch test CI from pixi to hatch with dynamic matrix
- Dynamic test matrix extracted from pyproject.toml via hatch env show - Add hatch-test env config with dependency-groups, scripts, and pre-release matrix - Add concurrency control, check job (alls-green), and bi-monthly cron schedule - Switch coverage upload to Codecov OIDC - Keep visual test artifact upload, MPLBACKEND=agg, and tag triggers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d510e96 commit 3135e1d

2 files changed

Lines changed: 102 additions & 50 deletions

File tree

.github/workflows/test.yaml

Lines changed: 81 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,105 @@ name: Test
33
on:
44
push:
55
branches: [main]
6-
tags: ["v*"] # Push events to matching v*, i.e. v1.0, v20.15.10
6+
tags: ["v*"]
77
pull_request:
8-
branches: ["*"]
8+
branches: [main]
9+
schedule:
10+
- cron: "0 5 1,15 * *"
11+
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
env:
17+
MPLBACKEND: agg
918

1019
jobs:
11-
test:
20+
# Dynamically extract the test matrix from hatch so pyproject.toml is the single source of truth.
21+
# See [[tool.hatch.envs.hatch-test.matrix]] in pyproject.toml.
22+
get-environments:
1223
runs-on: ubuntu-latest
24+
outputs:
25+
envs: ${{ steps.get-envs.outputs.envs }}
26+
steps:
27+
- uses: actions/checkout@v5
28+
with:
29+
filter: blob:none
30+
fetch-depth: 0
31+
- name: Install uv
32+
uses: astral-sh/setup-uv@v7
33+
- name: Get test environments
34+
id: get-envs
35+
run: |
36+
ENVS_JSON=$(uvx hatch env show --json | jq -c 'to_entries
37+
| map(
38+
select(.key | startswith("hatch-test"))
39+
| {
40+
name: .key,
41+
label: (if (.key | contains("pre")) then .key + " (PRE-RELEASE DEPENDENCIES)" else .key end),
42+
python: .value.python
43+
}
44+
)')
45+
echo "envs=${ENVS_JSON}" | tee $GITHUB_OUTPUT
46+
47+
test:
48+
needs: get-environments
49+
permissions:
50+
id-token: write # for codecov OIDC
51+
1352
strategy:
1453
fail-fast: false
1554
matrix:
16-
env: ["dev-py311", "dev-py313"]
55+
os: [ubuntu-latest]
56+
env: ${{ fromJSON(needs.get-environments.outputs.envs) }}
1757

18-
# Configure pytest-xdist
19-
env:
20-
OMP_NUM_THREADS: "1"
21-
OPENBLAS_NUM_THREADS: "1"
22-
MKL_NUM_THREADS: "1"
23-
NUMEXPR_MAX_THREADS: "1"
24-
MPLBACKEND: "agg"
25-
DISPLAY: ":42"
26-
PYTEST_ADDOPTS: "-n auto --dist=load --durations=10"
58+
name: ${{ matrix.env.label }}
59+
runs-on: ${{ matrix.os }}
60+
continue-on-error: ${{ contains(matrix.env.name, 'pre') }}
2761

2862
steps:
29-
- uses: actions/checkout@v4
30-
31-
# Cache rattler's shared package cache (speeds up downloads)
32-
- name: Restore rattler cache
33-
uses: actions/cache@v4
63+
- uses: actions/checkout@v5
3464
with:
35-
path: ~/.cache/rattler
36-
key: rattler-${{ runner.os }}-${{ matrix.env }}-${{ hashFiles('pyproject.toml') }}
37-
restore-keys: |
38-
rattler-${{ runner.os }}-${{ matrix.env }}-
39-
rattler-${{ runner.os }}-
40-
41-
# Install pixi and the requested environment
42-
- uses: prefix-dev/setup-pixi@v0.9.0
65+
filter: blob:none
66+
fetch-depth: 0
67+
- name: Install uv
68+
uses: astral-sh/setup-uv@v7
4369
with:
44-
environments: ${{ matrix.env }}
45-
# We're not comitting the pixi-lock file
46-
locked: false
47-
cache: false
48-
activate-environment: ${{ matrix.env }}
49-
50-
- name: Show versions
51-
run: |
52-
python --version
53-
pixi --version
54-
70+
python-version: ${{ matrix.env.python }}
71+
- name: Ensure figure directory exists
72+
run: mkdir -p tests/figures
73+
- name: Create hatch environment
74+
run: uvx hatch env create ${{ matrix.env.name }}
5575
- name: Run tests
5676
env:
57-
MPLBACKEND: agg
5877
DISPLAY: ":42"
78+
run: uvx hatch run ${{ matrix.env.name }}:run-cov -v --color=yes -n auto
79+
- name: Generate coverage report
5980
run: |
60-
pytest -v --cov --color=yes --cov-report=xml
61-
62-
- name: Archive figures generated during testing
81+
test -f .coverage || uvx hatch run ${{ matrix.env.name }}:cov-combine
82+
uvx hatch run ${{ matrix.env.name }}:cov-report
83+
- name: Archive visual test figures
6384
if: always()
6485
uses: actions/upload-artifact@v4
6586
with:
66-
name: visual_test_results_${{ matrix.env }}
67-
path: /home/runner/work/spatialdata-plot/spatialdata-plot/tests/figures/*
87+
name: visual_test_results_${{ matrix.env.name }}
88+
path: tests/figures/*
89+
- name: Upload coverage
90+
uses: codecov/codecov-action@v5
91+
with:
92+
fail_ci_if_error: true
93+
use_oidc: true
6894

69-
- name: Upload coverage to Codecov
70-
uses: codecov/codecov-action@v4
95+
# Single required check for branch protection.
96+
# See https://github.com/re-actors/alls-green#why
97+
check:
98+
name: Tests pass in all hatch environments
99+
if: always()
100+
needs:
101+
- get-environments
102+
- test
103+
runs-on: ubuntu-latest
104+
steps:
105+
- uses: re-actors/alls-green@release/v1
71106
with:
72-
name: coverage
73-
verbose: true
74-
env:
75-
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
107+
jobs: ${{ toJSON(needs) }}

pyproject.toml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ dev = [
4242
"twine>=4.0.2",
4343
]
4444
test = [
45+
"coverage[toml]>=7.4",
4546
"pooch",
4647
"pytest",
4748
"pytest-cov",
48-
"pytest-xdist",
49+
"pytest-xdist[psutil]",
4950
]
5051
doc = [
5152
"ipykernel",
@@ -85,9 +86,28 @@ build = "sphinx-build -M html docs docs/_build -W {args}"
8586
open = "python -m webbrowser -t docs/_build/html/index.html"
8687
clean = "git clean -fdX -- {args:docs}"
8788

89+
[tool.hatch.envs.hatch-test]
90+
dependency-groups = [ "test" ]
91+
92+
[tool.hatch.envs.hatch-test.scripts]
93+
run = "pytest{env:HATCH_TEST_ARGS:} -p no:cov {args}"
94+
run-cov = "coverage run -m pytest{env:HATCH_TEST_ARGS:} -p no:cov {args}"
95+
cov-combine = "coverage combine"
96+
cov-report = [ "coverage report", "coverage xml -o coverage.xml" ]
97+
8898
[[tool.hatch.envs.hatch-test.matrix]]
99+
deps = [ "stable" ]
89100
python = [ "3.11", "3.13" ]
90101

102+
[[tool.hatch.envs.hatch-test.matrix]]
103+
deps = [ "pre" ]
104+
python = [ "3.13" ]
105+
106+
[tool.hatch.envs.hatch-test.overrides]
107+
matrix.deps.env-vars = [
108+
{ key = "UV_PRERELEASE", value = "allow", if = [ "pre" ] },
109+
]
110+
91111
[tool.ruff]
92112
line-length = 120
93113
exclude = [

0 commit comments

Comments
 (0)