@@ -12,14 +12,48 @@ name: unittest
1212permissions :
1313 contents : read
1414
15+ # Configurable global environment variables for batching
16+ env :
17+ TEST_ALL_PACKAGES : " false" # Set to "false" to only run tests for packages with a git diff
18+ ALL_PYTHON : " ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.15']"
1519
1620jobs :
21+ # Dynamic package discovery job to calculate required matrix size automatically
22+ python_config :
23+ runs-on : ubuntu-latest
24+ outputs :
25+ all_python : ${{ steps.export.outputs.python_list }}
26+ steps :
27+ - id : export
28+ run : echo "python_list=${{ env.ALL_PYTHON }}" >> "$GITHUB_OUTPUT"
29+
30+ discover-packages :
31+ runs-on : ubuntu-latest
32+ outputs :
33+ batch-indices : ${{ steps.set-matrix.outputs.indices }}
34+ steps :
35+ - name : Checkout
36+ uses : actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
37+ with :
38+ persist-credentials : false
39+ - name : Setup Python
40+ uses : actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
41+ with :
42+ python-version : " 3.14"
43+ - name : Generate Batch Indices
44+ id : set-matrix
45+ run : |
46+ INDICES=$(python -c 'import sys; sys.path.append("ci"); import get_batches; print(get_batches.get_batch_indices())')
47+ echo "indices=${INDICES}" >> "$GITHUB_OUTPUT"
48+
1749 unit :
50+ name : " unit-run (${{ matrix.python }}, Batch ${{ matrix.batch-index }})"
1851 runs-on : ubuntu-22.04
52+ needs : [python_config, discover-packages]
1953 strategy :
20- fail-fast : true
2154 matrix :
22- python : ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
55+ python : ${{ fromJSON(needs.python_config.outputs.all_python) }}
56+ batch-index : ${{ fromJson(needs.discover-packages.outputs.batch-indices) }}
2357 steps :
2458 - name : Checkout
2559 uses : actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
5488 restore-keys : |
5589 ${{ runner.os }}-uv-wheels-3.15-
5690 - name : Run unit tests
91+ id : run-tests
5792 env :
58- COVERAGE_FILE : ${{ github.workspace }}/.coverage-${{ matrix.python }}
59- BUILD_TYPE : presubmit
93+ COVERAGE_FILE : ${{ github.workspace }}/.coverage-${{ matrix.python }}-${{ matrix.batch-index }}
94+ # Dynamically set BUILD_TYPE to an empty string to skip the diff calculation if TEST_ALL_PACKAGES is true
95+ BUILD_TYPE : ${{ env.TEST_ALL_PACKAGES == 'true' && '' || 'presubmit' }}
6096 TARGET_BRANCH : ${{ github.base_ref || github.event.merge_group.base_ref }}
6197 TEST_TYPE : unit
6298 PY_VERSION : ${{ matrix.python }}
@@ -65,6 +101,15 @@ jobs:
65101 # Follow https://github.com/Instagram/LibCST/issues/1445 for updates.
66102 PYO3_USE_ABI3_FORWARD_COMPATIBILITY : " 1"
67103 run : |
104+ UNIQUE_BATCH_PACKAGES=$(python -c 'import sys; sys.path.append("ci"); import get_batches; print(get_batches.get_batch_slice(${{ matrix.batch-index }}))')
105+
106+ if [ -z "$UNIQUE_BATCH_PACKAGES" ]; then
107+ echo "No structural units allocated to this matrix slice."
108+ exit 0
109+ fi
110+
111+ echo "Running tests for packages in this weighted batch: ${UNIQUE_BATCH_PACKAGES}"
112+
68113 # Only inject overrides if we are strictly on the 3.15 pre-release
69114 # This is needed to speed up builds
70115 if [ "${{ matrix.python }}" = "3.15" ]; then
@@ -74,18 +119,31 @@ jobs:
74119 # Route uv's wheel and environment cache to our persistent workspace directory
75120 export UV_CACHE_DIR="${{ github.workspace }}/.uv-wheel-cache"
76121 fi
77- ci/run_conditional_tests.sh
122+
123+ ci/run_conditional_tests.sh ${UNIQUE_BATCH_PACKAGES}
124+
125+ - name : Save Status Footprint
126+ run : |
127+ mkdir -p footprints
128+ echo "${{ steps.run-tests.outcome }}" > footprints/status.txt
129+
130+ - name : Upload Status Footprint
131+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
132+ with :
133+ name : footprint-${{ matrix.python }}-${{ matrix.batch-index }}
134+ path : footprints/
135+
78136 - name : Upload coverage results
79137 uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
80138 with :
81- name : coverage-artifact-${{ matrix.python }}
82- path : .coverage-${{ matrix.python }}
139+ # Appended batch-index to separate parallel coverage uploads cleanly
140+ name : coverage-artifact-${{ matrix.python }}-${{ matrix.batch-index }}
141+ path : .coverage-${{ matrix.python }}-${{ matrix.batch-index }}
83142 include-hidden-files : true
84143
85144 cover :
86145 runs-on : ubuntu-latest
87- needs :
88- - unit
146+ needs : [unit]
89147 steps :
90148 - name : Checkout
91149 uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -197,3 +255,52 @@ jobs:
197255 echo "This usually means the unit tests did not run or failed to upload their coverage files."
198256 exit 1
199257 fi
258+
259+ unittest-runtime-result :
260+ name : " unit (${{ matrix.python }})"
261+ needs : [python_config, discover-packages, unit]
262+ if : always()
263+ strategy :
264+ fail-fast : false
265+ matrix :
266+ python : ${{ fromJSON(needs.python_config.outputs.all_python) }}
267+ runs-on : ubuntu-latest
268+ steps :
269+ - name : Download all status footprints for this runtime
270+ uses : actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
271+ with :
272+ pattern : footprint-${{ matrix.python }}-*
273+ path : footprint-results/
274+
275+ - name : Validate complete batch execution footprint status
276+ run : |
277+ EXPECTED_BATCHES=$(echo '${{ needs.discover-packages.outputs.batch-indices }}' | jq '. | length')
278+
279+ if [ -d "footprint-results" ]; then
280+ ACTUAL_BATCHES=$(find footprint-results -type f -name 'status.txt' | wc -l | tr -d ' ')
281+ else
282+ ACTUAL_BATCHES=0
283+ fi
284+
285+ echo "Validation metrics for Python ${{ matrix.python }}:"
286+ echo " -> Expected footprint files: $EXPECTED_BATCHES"
287+ echo " -> Downloaded footprint files: $ACTUAL_BATCHES"
288+
289+ if [ "$ACTUAL_BATCHES" -ne "$EXPECTED_BATCHES" ]; then
290+ echo "Error: Footprint count mismatch! Expected $EXPECTED_BATCHES files, but found $ACTUAL_BATCHES."
291+ exit 1
292+ fi
293+
294+ FAILED_RUNS=0
295+ while read -r STATUS_FILE; do
296+ RUN_STATUS=$(cat "$STATUS_FILE" | tr -d '[:space:]')
297+ if [[ "$RUN_STATUS" != "success" ]]; then
298+ echo "Failure detected in batch profile path: $STATUS_FILE (Status: $RUN_STATUS)"
299+ FAILED_RUNS=$((FAILED_RUNS + 1))
300+ fi
301+ done < <(find footprint-results -type f -name 'status.txt' 2>/dev/null)
302+
303+ if [ "$FAILED_RUNS" -gt 0 ]; then
304+ echo "Error: Validation failed. Found $FAILED_RUNS failing test batches."
305+ exit 1
306+ fi
0 commit comments