@@ -15,6 +15,102 @@ concurrency:
1515 cancel-in-progress : true
1616
1717jobs :
18+ # ===========================================================================
19+ # STAGE 0: Detect what changed (runs immediately)
20+ # ===========================================================================
21+ detect-changes :
22+ name : detect-changes
23+ runs-on : ubuntu-latest
24+ outputs :
25+ core : ${{ steps.filter.outputs.core }}
26+ sklearn : ${{ steps.filter.outputs.sklearn }}
27+ sktime : ${{ steps.filter.outputs.sktime }}
28+ skpro : ${{ steps.filter.outputs.skpro }}
29+ utils : ${{ steps.filter.outputs.utils }}
30+ docs : ${{ steps.filter.outputs.docs }}
31+ examples : ${{ steps.filter.outputs.examples }}
32+ any_src : ${{ steps.filter.outputs.any_src }}
33+ steps :
34+ - uses : actions/checkout@v4
35+ with :
36+ fetch-depth : 0
37+
38+ - name : Detect changed paths
39+ id : filter
40+ run : |
41+ if [ "${{ github.event_name }}" == "push" ]; then
42+ BASE_SHA="${{ github.event.before }}"
43+ # Handle initial push where before is all zeros
44+ if [ "$BASE_SHA" == "0000000000000000000000000000000000000000" ]; then
45+ BASE_SHA="HEAD~1"
46+ fi
47+ else
48+ BASE_SHA="${{ github.event.pull_request.base.sha }}"
49+ fi
50+
51+ CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "${{ github.sha }}" || echo "")
52+ echo "Changed files:"
53+ echo "$CHANGED_FILES"
54+
55+ # Core: base module, main hyperactive files, or general tests
56+ if echo "$CHANGED_FILES" | grep -qE "^src/hyperactive/(base|tests|__init__|hyperactive)"; then
57+ echo "core=true" >> $GITHUB_OUTPUT
58+ else
59+ echo "core=false" >> $GITHUB_OUTPUT
60+ fi
61+
62+ # sklearn integration
63+ if echo "$CHANGED_FILES" | grep -q "^src/hyperactive/integrations/sklearn"; then
64+ echo "sklearn=true" >> $GITHUB_OUTPUT
65+ else
66+ echo "sklearn=false" >> $GITHUB_OUTPUT
67+ fi
68+
69+ # sktime integration
70+ if echo "$CHANGED_FILES" | grep -q "^src/hyperactive/integrations/sktime"; then
71+ echo "sktime=true" >> $GITHUB_OUTPUT
72+ else
73+ echo "sktime=false" >> $GITHUB_OUTPUT
74+ fi
75+
76+ # skpro integration
77+ if echo "$CHANGED_FILES" | grep -q "^src/hyperactive/integrations/skpro"; then
78+ echo "skpro=true" >> $GITHUB_OUTPUT
79+ else
80+ echo "skpro=false" >> $GITHUB_OUTPUT
81+ fi
82+
83+ # utils
84+ if echo "$CHANGED_FILES" | grep -q "^src/hyperactive/utils"; then
85+ echo "utils=true" >> $GITHUB_OUTPUT
86+ else
87+ echo "utils=false" >> $GITHUB_OUTPUT
88+ fi
89+
90+ # docs
91+ if echo "$CHANGED_FILES" | grep -qE "^docs/|\.rst$|\.md$"; then
92+ echo "docs=true" >> $GITHUB_OUTPUT
93+ else
94+ echo "docs=false" >> $GITHUB_OUTPUT
95+ fi
96+
97+ # examples
98+ if echo "$CHANGED_FILES" | grep -q "^examples/"; then
99+ echo "examples=true" >> $GITHUB_OUTPUT
100+ else
101+ echo "examples=false" >> $GITHUB_OUTPUT
102+ fi
103+
104+ # any source code
105+ if echo "$CHANGED_FILES" | grep -q "^src/"; then
106+ echo "any_src=true" >> $GITHUB_OUTPUT
107+ else
108+ echo "any_src=false" >> $GITHUB_OUTPUT
109+ fi
110+
111+ # ===========================================================================
112+ # STAGE 1: Fast checks (code-quality + doctest)
113+ # ===========================================================================
18114 code-quality :
19115 name : code-quality
20116 runs-on : ubuntu-latest
55151
56152 test-no-extras :
57153 name : test-no-extras
58- needs : [code-quality, doctest ]
154+ needs : [targeted-tests-gate ]
59155 strategy :
60156 matrix :
61157 python-version : ["3.10", "3.11", "3.12", "3.13", "3.14"]
93189
94190 test-all-extras :
95191 name : test-all-extras
96- needs : [code-quality, doctest ]
192+ needs : [targeted-tests-gate ]
97193 strategy :
98194 matrix :
99195 python-version : ["3.10", "3.11", "3.12", "3.13", "3.14"]
@@ -131,7 +227,7 @@ jobs:
131227
132228 test-sklearn-versions :
133229 name : test-sklearn-${{ matrix.sklearn-version }}-python-${{ matrix.python-version }}
134- needs : [code-quality, doctest ]
230+ needs : [targeted-tests-gate ]
135231 runs-on : ubuntu-latest
136232
137233 strategy :
@@ -182,9 +278,161 @@ jobs:
182278 run : |
183279 python -m pytest --doctest-modules src/hyperactive/ -v
184280
281+ # ===========================================================================
282+ # STAGE 2: Targeted tests (only for changed areas, fast feedback)
283+ # ===========================================================================
284+ targeted-core :
285+ name : targeted-core
286+ needs : [code-quality, doctest, detect-changes]
287+ if : needs.detect-changes.outputs.core == 'true' || needs.detect-changes.outputs.any_src == 'false'
288+ runs-on : ubuntu-latest
289+ timeout-minutes : 10
290+
291+ steps :
292+ - uses : actions/checkout@v4
293+
294+ - name : Set up Python 3.11
295+ uses : actions/setup-python@v5
296+ with :
297+ python-version : " 3.11"
298+
299+ - name : Install dependencies
300+ run : |
301+ python -m pip install --upgrade pip
302+ python -m pip install .[test]
303+
304+ - name : Run core tests
305+ run : |
306+ python -m pytest src/hyperactive/base/tests/ src/hyperactive/tests/ -v -p no:warnings
307+
308+ targeted-sklearn :
309+ name : targeted-sklearn
310+ needs : [code-quality, doctest, detect-changes]
311+ if : needs.detect-changes.outputs.sklearn == 'true'
312+ runs-on : ubuntu-latest
313+ timeout-minutes : 10
314+
315+ steps :
316+ - uses : actions/checkout@v4
317+
318+ - name : Set up Python 3.11
319+ uses : actions/setup-python@v5
320+ with :
321+ python-version : " 3.11"
322+
323+ - name : Install dependencies
324+ run : |
325+ python -m pip install --upgrade pip
326+ python -m pip install .[all_extras,test]
327+
328+ - name : Run sklearn integration tests
329+ run : |
330+ python -m pytest src/hyperactive/integrations/sklearn/tests/ -v -p no:warnings
331+
332+ targeted-sktime :
333+ name : targeted-sktime
334+ needs : [code-quality, doctest, detect-changes]
335+ if : needs.detect-changes.outputs.sktime == 'true'
336+ runs-on : ubuntu-latest
337+ timeout-minutes : 10
338+
339+ steps :
340+ - uses : actions/checkout@v4
341+
342+ - name : Set up Python 3.11
343+ uses : actions/setup-python@v5
344+ with :
345+ python-version : " 3.11"
346+
347+ - name : Install dependencies
348+ run : |
349+ python -m pip install --upgrade pip
350+ python -m pip install .[sktime-integration,test]
351+
352+ - name : Run sktime integration tests
353+ run : |
354+ python -m pytest src/hyperactive/integrations/sktime/tests/ -v -p no:warnings
355+
356+ targeted-skpro :
357+ name : targeted-skpro
358+ needs : [code-quality, doctest, detect-changes]
359+ if : needs.detect-changes.outputs.skpro == 'true'
360+ runs-on : ubuntu-latest
361+ timeout-minutes : 10
362+
363+ steps :
364+ - uses : actions/checkout@v4
365+
366+ - name : Set up Python 3.11
367+ uses : actions/setup-python@v5
368+ with :
369+ python-version : " 3.11"
370+
371+ - name : Install dependencies
372+ run : |
373+ python -m pip install --upgrade pip
374+ python -m pip install .[sktime-integration,test]
375+
376+ - name : Run skpro integration tests
377+ run : |
378+ python -m pytest src/hyperactive/integrations/skpro/tests/ -v -p no:warnings
379+
380+ targeted-utils :
381+ name : targeted-utils
382+ needs : [code-quality, doctest, detect-changes]
383+ if : needs.detect-changes.outputs.utils == 'true'
384+ runs-on : ubuntu-latest
385+ timeout-minutes : 10
386+
387+ steps :
388+ - uses : actions/checkout@v4
389+
390+ - name : Set up Python 3.11
391+ uses : actions/setup-python@v5
392+ with :
393+ python-version : " 3.11"
394+
395+ - name : Install dependencies
396+ run : |
397+ python -m pip install --upgrade pip
398+ python -m pip install .[test,test_parallel_backends]
399+
400+ - name : Run utils tests
401+ run : |
402+ python -m pytest src/hyperactive/utils/tests/ -v -p no:warnings
403+
404+ # Gate job: passes if all targeted tests pass OR are skipped (not failed)
405+ targeted-tests-gate :
406+ name : targeted-tests-gate
407+ needs : [targeted-core, targeted-sklearn, targeted-sktime, targeted-skpro, targeted-utils]
408+ if : always()
409+ runs-on : ubuntu-latest
410+ steps :
411+ - name : Check targeted test results
412+ run : |
413+ echo "Core: ${{ needs.targeted-core.result }}"
414+ echo "Sklearn: ${{ needs.targeted-sklearn.result }}"
415+ echo "Sktime: ${{ needs.targeted-sktime.result }}"
416+ echo "Skpro: ${{ needs.targeted-skpro.result }}"
417+ echo "Utils: ${{ needs.targeted-utils.result }}"
418+
419+ # Fail if any targeted test failed (not skipped)
420+ if [[ "${{ needs.targeted-core.result }}" == "failure" ]] || \
421+ [[ "${{ needs.targeted-sklearn.result }}" == "failure" ]] || \
422+ [[ "${{ needs.targeted-sktime.result }}" == "failure" ]] || \
423+ [[ "${{ needs.targeted-skpro.result }}" == "failure" ]] || \
424+ [[ "${{ needs.targeted-utils.result }}" == "failure" ]]; then
425+ echo "One or more targeted tests failed"
426+ exit 1
427+ fi
428+ echo "All targeted tests passed or were skipped"
429+
430+ # ===========================================================================
431+ # STAGE 3: Full test matrix (only after targeted tests pass)
432+ # ===========================================================================
185433 coverage :
186434 name : coverage
187- needs : [code-quality, doctest ]
435+ needs : [targeted-tests-gate ]
188436 runs-on : ubuntu-latest
189437 timeout-minutes : 15
190438
@@ -214,7 +462,7 @@ jobs:
214462
215463 test-examples :
216464 name : test-examples
217- needs : [code-quality, doctest ]
465+ needs : [targeted-tests-gate ]
218466 runs-on : ubuntu-latest
219467 timeout-minutes : 15
220468
0 commit comments