5959 esac
6060 shell : bash
6161
62+ # DAG-based test scoping: use cargo-rail to compute affected crates from the
63+ # workspace dependency graph + git diff, avoiding the full test suite when only
64+ # a subset of crates changed.
65+ # Safety: cargo check/clippy run on the full workspace separately, catching all
66+ # compilation errors. This only scopes test BUILD and EXECUTION.
67+ - name : Fetch base branch for DAG analysis
68+ if : startsWith(inputs.task, 'test-')
69+ run : git fetch origin master --depth=1 2>/dev/null || true
70+ shell : bash
71+
72+ - name : Install cargo-rail
73+ if : startsWith(inputs.task, 'test-')
74+ uses : taiki-e/install-action@v2
75+ with :
76+ tool : cargo-rail
77+
78+ - name : Compute affected crates (cargo-rail)
79+ if : startsWith(inputs.task, 'test-')
80+ run : |
81+ TOTAL_CRATES=$(cargo metadata --format-version 1 --no-deps 2>/dev/null | jq '.workspace_members | length' 2>/dev/null || echo "?")
82+ echo "$TOTAL_CRATES" > /tmp/total-crates.txt
83+
84+ PLAN_JSON=$(cargo rail plan --since origin/master -f json 2>/tmp/affected-stderr.txt || echo "")
85+
86+ if [[ -n "$PLAN_JSON" ]]; then
87+ MODE=$(echo "$PLAN_JSON" | jq -r '.scope.mode')
88+ if [[ "$MODE" == "crates" ]]; then
89+ CRATES=$(echo "$PLAN_JSON" | jq -r '.scope.crates[]')
90+ CRATE_COUNT=$(echo "$CRATES" | wc -l)
91+ # Build -p flags for cargo build/test
92+ echo "$CRATES" | sed 's/^/-p /' | tr '\n' ' ' > /tmp/packages.txt
93+ # Build nextest filter expression for cargo nextest run
94+ echo "$CRATES" | sed 's/^/package(/; s/$/)/' | paste -sd ' | ' > /tmp/nextest-filter.txt
95+ echo "::notice::DAG analysis: ${CRATE_COUNT} affected crates (of ${TOTAL_CRATES} total)"
96+ else
97+ echo "::notice::Full workspace affected (${TOTAL_CRATES} crates)"
98+ fi
99+ else
100+ STDERR=$(cat /tmp/affected-stderr.txt 2>/dev/null || echo "")
101+ echo "::warning::Could not compute affected crates, running full test suite. ${STDERR}"
102+ rm -f /tmp/nextest-filter.txt /tmp/packages.txt
103+ fi
104+ shell : bash
105+
62106 # Individual lint tasks for parallel execution
63107 - name : Cargo check
64108 if : inputs.task == 'check'
@@ -117,16 +161,45 @@ runs:
117161 echo "::notice::Running test partition ${PARTITION_INDEX}/2"
118162 fi
119163
164+ # Read DAG-based affected crate filter (computed in earlier step)
165+ NEXTEST_FILTER=""
166+ PACKAGE_FLAGS=""
167+ TOTAL_CRATES="?"
168+ if [[ -f /tmp/nextest-filter.txt ]]; then
169+ NEXTEST_FILTER=$(cat /tmp/nextest-filter.txt)
170+ fi
171+ if [[ -f /tmp/packages.txt ]]; then
172+ PACKAGE_FLAGS=$(cat /tmp/packages.txt)
173+ fi
174+ if [[ -f /tmp/total-crates.txt ]]; then
175+ TOTAL_CRATES=$(cat /tmp/total-crates.txt)
176+ fi
177+
178+ if [[ -n "$PACKAGE_FLAGS" ]]; then
179+ CRATE_COUNT=$(echo "$NEXTEST_FILTER" | grep -o 'package(' | wc -l)
180+ echo "::notice::DAG-scoped build: ${CRATE_COUNT} crates (cargo check/clippy cover full workspace separately)"
181+ else
182+ echo "::notice::Full workspace build (no DAG filter available)"
183+ fi
184+
120185 source <(cargo llvm-cov show-env --export-prefix)
121186
122187 bins_start=$(date +%s)
123- cargo build --locked
188+ if [[ -n "$PACKAGE_FLAGS" ]]; then
189+ cargo build --locked $PACKAGE_FLAGS
190+ else
191+ cargo build --locked
192+ fi
124193 bins_end=$(date +%s)
125194 bins_duration=$((bins_end - bins_start))
126195 echo "::notice::Binaries and libraries built in ${bins_duration}s ($(date -ud @${bins_duration} +'%M:%S'))"
127196
128197 compile_start=$(date +%s)
129- cargo test --locked --no-run
198+ if [[ -n "$PACKAGE_FLAGS" ]]; then
199+ cargo test --locked --no-run $PACKAGE_FLAGS
200+ else
201+ cargo test --locked --no-run
202+ fi
130203 compile_end=$(date +%s)
131204 compile_duration=$((compile_end - compile_start))
132205 echo "::notice::Tests compiled in ${compile_duration}s ($(date -ud @${compile_duration} +'%M:%S'))"
@@ -144,12 +217,20 @@ runs:
144217
145218 test_start=$(date +%s)
146219 if command -v cargo-nextest &> /dev/null; then
147- cargo nextest run --locked --no-fail-fast --profile ci $PARTITION_FLAG
220+ if [[ -n "$NEXTEST_FILTER" ]]; then
221+ cargo nextest run --locked --no-fail-fast --profile ci $PARTITION_FLAG -E "$NEXTEST_FILTER"
222+ else
223+ cargo nextest run --locked --no-fail-fast --profile ci $PARTITION_FLAG
224+ fi
148225 else
149226 if [[ -n "$PARTITION_FLAG" ]]; then
150227 echo "::error::cargo-nextest not found, falling back to cargo test without partitioning (all tests will run on every partition)"
151228 fi
152- cargo test --locked --no-fail-fast
229+ if [[ -n "$PACKAGE_FLAGS" ]]; then
230+ cargo test --locked --no-fail-fast $PACKAGE_FLAGS
231+ else
232+ cargo test --locked --no-fail-fast
233+ fi
153234 fi
154235 test_end=$(date +%s)
155236 test_duration=$((test_end - test_start))
@@ -159,6 +240,12 @@ runs:
159240 total_duration=$((build_duration + test_duration))
160241 echo ""
161242 echo "========================================="
243+ if [[ -n "$PACKAGE_FLAGS" ]]; then
244+ CRATE_COUNT=$(echo "$NEXTEST_FILTER" | grep -o 'package(' | wc -l)
245+ echo "DAG scope: ${CRATE_COUNT}/${TOTAL_CRATES} crates"
246+ else
247+ echo "DAG scope: full workspace (${TOTAL_CRATES} crates)"
248+ fi
162249 echo "All targets build: ${bins_duration}s ($(date -ud @${bins_duration} +'%M:%S'))"
163250 echo "Tests compile: ${compile_duration}s ($(date -ud @${compile_duration} +'%M:%S'))"
164251 echo "Tests execute: ${test_duration}s ($(date -ud @${test_duration} +'%M:%S'))"
0 commit comments