@@ -155,6 +155,23 @@ jobs:
155155 path : ${{ env.JIT_CACHE_DIR }}
156156 key : ${{ env.JIT_CACHE_KEY_PREFIX }}-latest
157157
158+ # asv shells out to git for every command (the container runs as root but
159+ # the checked-out tree is owned by a different uid, which otherwise trips
160+ # git's "detected dubious ownership" guard and fails `asv publish`). Also
161+ # ensure a local `main` ref exists: asv resolves conf.branches (["main"]
162+ # in asv.conf.json) to build its commit list, which fails on a feature
163+ # branch where `main` was never fetched. Results are still stored under
164+ # the real HEAD sha via --set-commit-hash below, so pointing `main` at
165+ # HEAD here only satisfies asv's branch lookup.
166+ - name : Prepare git for asv
167+ run : |
168+ set -euo pipefail
169+ git config --global --add safe.directory "$GITHUB_WORKSPACE"
170+ git config --global --add safe.directory '*'
171+ if ! git rev-parse --verify --quiet main >/dev/null; then
172+ git branch main HEAD
173+ fi
174+
158175 # asv prompts interactively for machine metadata on first use; --yes
159176 # accepts the autodetected defaults and writes ~/.asv-machine.json.
160177 - name : Configure asv machine
@@ -166,13 +183,54 @@ jobs:
166183 # CUDA compatibility). --python=same runs in the existing uv env and
167184 # ignores the asv.conf.json matrix. `uv run --no-sync` puts .venv/bin on
168185 # PATH and keeps the env read-only (UV_FROZEN/UV_NO_SYNC).
186+ #
187+ # --set-commit-hash is REQUIRED: with an existing environment (--python=
188+ # same) asv skips saving results entirely unless a commit hash is pinned
189+ # (see asv/commands/run.py: skip_save is True for ExistingEnvironment when
190+ # set_commit_hash is None). Without it, .asv/results stays empty and the
191+ # publish/plot steps have nothing to consume. --no-pull avoids an
192+ # unnecessary `git fetch` against the shallow checkout.
193+ #
194+ # Verbosity:
195+ # -v / --verbose : per-benchmark progress and asv internals.
196+ # --show-stderr : surface each benchmark process's stdout/stderr so
197+ # a slow or hanging benchmark is visible live in the
198+ # job log instead of only the terminal summary line.
199+ # PYTHONUNBUFFERED=1: flush asv/benchmark output immediately so the live
200+ # log is not held back by stdio buffering.
201+ #
202+ # Exit-code handling: asv returns 2 when one or more benchmarks fail (e.g.
203+ # the functional KNN "scipy" cases, which cannot run on CUDA inputs).
204+ # Those are individual benchmark failures, not an infrastructure problem,
205+ # so we downgrade exit code 2 to a warning and still publish the partial
206+ # results. Any other nonzero exit (config/usage/infra errors) remains
207+ # fatal.
169208 - name : Run ASV benchmarks
170209 env :
171210 WARP_CACHE_PATH : ${{ env.JIT_CACHE_DIR }}/warp
172211 TRITON_CACHE_DIR : ${{ env.JIT_CACHE_DIR }}/triton
173212 TORCHINDUCTOR_CACHE_DIR : ${{ env.JIT_CACHE_DIR }}/inductor
213+ PYTHONUNBUFFERED : " 1"
174214 run : |
175- uv run --no-sync asv run --launch-method spawn --python=same
215+ commit_hash="$(git rev-parse HEAD)"
216+ # GitHub's default bash shell runs with `set -e`; disable it around
217+ # the asv call so we can inspect the exit code instead of aborting.
218+ set +e
219+ uv run --no-sync asv run \
220+ --launch-method spawn \
221+ --python=same \
222+ --set-commit-hash "$commit_hash" \
223+ --no-pull \
224+ --verbose \
225+ --show-stderr
226+ rc=$?
227+ set -e
228+ if [ "$rc" -eq 2 ]; then
229+ echo "::warning::asv reported one or more failed benchmarks (exit code 2); publishing partial results."
230+ elif [ "$rc" -ne 0 ]; then
231+ echo "::error::asv run failed with exit code $rc."
232+ exit "$rc"
233+ fi
176234
177235 # Generate the browsable HTML dashboard from the fresh results.
178236 - name : Publish ASV HTML report
0 commit comments