From a675dc627119aef59b1c50e0fd5d4e683044b522 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 07:54:55 +0000 Subject: [PATCH 1/4] ci(coverage): print testthat output on failure covr's test phase (tools::testInstalledPackage) writes test results only to testthat.Rout.fail inside its temporary library; the job log shows nothing beyond 'running the tests in testthat.R failed', so coverage failures cannot be diagnosed from CI. Dump the file into the log when the job fails. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AB3bYjhyFftW5pXgRXDs27 --- .github/workflows/test-coverage.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 0973de3..0ebd869 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -79,3 +79,21 @@ jobs: quiet = FALSE, clean = FALSE ) + + # covr runs the test suite via tools::testInstalledPackage, which writes + # test output only to testthat.Rout(.fail) inside its temp library -- + # the job log otherwise shows nothing but "running the tests failed", + # making CI failures undiagnosable. Surface the file on failure. + - name: Show testthat output on failure + if: failure() + shell: bash + run: | + found=0 + while IFS= read -r f; do + found=1 + echo "=== $f ===" + tail -n 500 "$f" + done < <(find /tmp -name 'testthat.Rout*' 2>/dev/null) + if [ "$found" -eq 0 ]; then + echo "No testthat.Rout* files found under /tmp" + fi From 016785b37a8afb974fcff825bea131bfcdc643af Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 07:56:38 +0000 Subject: [PATCH 2/4] chore: gitignore gcov coverage artifacts covr / --coverage builds drop .gcda/.gcno files into src/, which showed up as untracked churn after running the coverage recipe locally. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AB3bYjhyFftW5pXgRXDs27 --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index caea292..e381c2a 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,9 @@ inst/bin/scconvert inst/bin/scconvert.exe inst/bin/*.dll vignettes/cosmx_lung9 + +# gcov coverage artifacts (covr / --coverage builds) +src/*.gcda +src/*.gcno +src/cli_obj/*.gcda +src/cli_obj/*.gcno From 282b6e2196065da81fc08da99ffdbe5322f6301c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 08:19:24 +0000 Subject: [PATCH 3/4] ci(coverage): keep covr install dir outside the R session tempdir R removes its own Rtmp* directory when the Rscript process exits -- clean = FALSE does not prevent that -- so testthat.Rout.fail was gone before the failure-diagnostics step could print it. Pin install_path to RUNNER_TEMP/covr-install so the test output survives the failing step. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AB3bYjhyFftW5pXgRXDs27 --- .github/workflows/test-coverage.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 0ebd869..e447711 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -72,16 +72,20 @@ jobs: extra-packages: any::covr any::reticulate cache-version: 1 + # install_path must live outside the R session tempdir: R deletes its + # own Rtmp* directory when the process exits (clean = FALSE does not + # prevent that), which used to take testthat.Rout.fail with it. - name: Run test coverage shell: Rscript {0} run: | covr::codecov( quiet = FALSE, - clean = FALSE + clean = FALSE, + install_path = file.path(Sys.getenv("RUNNER_TEMP"), "covr-install") ) # covr runs the test suite via tools::testInstalledPackage, which writes - # test output only to testthat.Rout(.fail) inside its temp library -- + # test output only to testthat.Rout(.fail) inside its install library -- # the job log otherwise shows nothing but "running the tests failed", # making CI failures undiagnosable. Surface the file on failure. - name: Show testthat output on failure @@ -93,7 +97,7 @@ jobs: found=1 echo "=== $f ===" tail -n 500 "$f" - done < <(find /tmp -name 'testthat.Rout*' 2>/dev/null) + done < <(find "$RUNNER_TEMP/covr-install" /tmp -name 'testthat.Rout*' 2>/dev/null) if [ "$found" -eq 0 ]; then - echo "No testthat.Rout* files found under /tmp" + echo "No testthat.Rout* files found" fi From ea81e8eefe1ba45913d60b81afc7c68bdf0eb5d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 08:48:46 +0000 Subject: [PATCH 4/4] ci: pin python-igraph + leidenalg in the scverse test env The coverage job's two failures were sc.tl.leiden(flavor='igraph') raising ImportError: python-igraph is not a dependency of conda-forge scanpy, so it only ever arrived transitively. When the micromamba env cache (unused since June) expired, the fresh conda-forge solve dropped it and both leiden-based python-validation tests broke -- 976 of 978 tests passed, so nothing in the package itself was at fault. Pin the leiden backends explicitly; the env-file hash change also busts the stale cache. Verified locally: a fresh micromamba solve of this env file runs the exact failing leiden calls cleanly. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AB3bYjhyFftW5pXgRXDs27 --- tests/scverse-env.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/scverse-env.yml b/tests/scverse-env.yml index 9e87a33..434ed87 100644 --- a/tests/scverse-env.yml +++ b/tests/scverse-env.yml @@ -8,6 +8,11 @@ dependencies: - squidpy>=1.3 - mudata>=0.2 - loompy>=3.0 + # sc.tl.leiden dependencies: conda-forge scanpy does not depend on these, + # so they must be pinned explicitly. They used to arrive transitively and + # vanished when the CI env cache expired and conda-forge re-solved. + - python-igraph + - leidenalg - numpy - pandas - scipy