From 63326c3d56cd502a8deec99d0efe04a5f9e5719c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 13:54:49 +0000 Subject: [PATCH 1/4] ci: Run the sanitizer check on the daily-rebuilt `r-debug` image The `Sanitizer` job pulled `r-debug-csan-igraph` from `ghcr.io/cynkra/docker-images`, which stopped being rebuilt on 2026-03-30. Its R-devel snapshot already reports 4.6.0, but predates the commit that made `R_getRegisteredNamespace()` part of the API, so the `#if R_VERSION >= R_Version(4, 6, 0)` branch in `src/rinterface_extra.c` failed to compile: rinterface_extra.c:2520:9: error: use of undeclared identifier 'R_getRegisteredNamespace' The same image is now published from https://github.com/cynkra/r-debug and rebuilt daily, so its R-devel is at most a day old. Verified locally with Docker against `ghcr.io/cynkra/r-debug/r-debug-csan-igraph:latest` (R-devel 2026-07-27 r90310): `RDcsan CMD INSTALL .` completes, and the header declares `R_getRegisteredNamespace()`. Also bump `actions/checkout` to v6, matching the other workflows and clearing the Node 20 deprecation warning, and point the reproduction command in `test-foreign.R` at an image that still exists. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FcwDG9RDPdiXBjqw8zvUNu --- .github/workflows/build-and-check.yml | 8 ++++++-- tests/testthat/test-foreign.R | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index e543c7e3a79..6b15172e125 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -21,7 +21,7 @@ jobs: name: Sanitizer steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -34,7 +34,11 @@ jobs: - name: run sanitizer uses: addnab/docker-run-action@v3 with: - image: ghcr.io/cynkra/docker-images/r-debug-csan-igraph:latest + # Built daily from https://github.com/cynkra/r-debug, so R-devel is + # at most a day old. + # The images under ghcr.io/cynkra/docker-images are no longer + # rebuilt; their R-devel is stale. + image: ghcr.io/cynkra/r-debug/r-debug-csan-igraph:latest options: --rm --platform linux/amd64 -v ${{ github.workspace }}:/rigraph run: | set -e diff --git a/tests/testthat/test-foreign.R b/tests/testthat/test-foreign.R index d0c29e32e7b..0332707c7e9 100644 --- a/tests/testthat/test-foreign.R +++ b/tests/testthat/test-foreign.R @@ -63,7 +63,7 @@ test_that("graph_from_graphdb works", { skip_on_cran() # Bug in base R? Checked with 2024-11-01 r87285: - # docker run --rm -ti -v $PWD:/rigraph -e MAKEFLAGS=-j4 ghcr.io/cynkra/docker-images/rigraph-san:latest RDcsan -q -e 'filename <- "/rigraph/DESCRIPTION"; gz_file_con <- file(filename, open = "rb"); file_con <- gzcon(gz_file_con); close(file_con); gc()' + # docker run --rm -ti -v $PWD:/rigraph -e MAKEFLAGS=-j4 ghcr.io/cynkra/r-debug/r-debug-csan-igraph:latest RDcsan -q -e 'filename <- "/rigraph/DESCRIPTION"; gz_file_con <- file(filename, open = "rb"); file_con <- gzcon(gz_file_con); close(file_con); gc()' skip_if(Sys.getenv("R_SANITIZER") == "true") expect_silent(graph_from_graphdb(nodes = 1000)) From bf48c1c9aafe946f8e57d2ac1a5bcf7dbe451bce Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 14:49:48 +0000 Subject: [PATCH 2/4] fix: Free `alpha` and `alpham1` in `Rx_igraph_is_chordal()` Both vectors are allocated by `Rz_SEXP_to_vector_int_copy()` but were never destroyed, so every `is_chordal()` call passing them leaked 72 bytes each. `c_fillin` in the same function was already registered and destroyed; these two were not. LeakSanitizer only surfaced this once the sanitizer job could build again -- it reported 288 bytes in 4 allocations at the end of an otherwise green suite: #1 igraph_vector_int_init src/vendor/cigraph/src/core/vector.pmt:144 #2 Rz_SEXP_to_vector_int_copy src/rinterface_extra.c:3725 #3 Rx_igraph_is_chordal src/rinterface_extra.c:6160 Register both with `IGRAPH_FINALLY_PV()` and destroy them in reverse order, so each `IGRAPH_FINALLY_CLEAN()` pops the entry belonging to the vector being destroyed. Also check the conversion result with `IGRAPH_R_CHECK()`, matching every other call site of `Rz_SEXP_to_vector_int_copy()`; the return value was silently discarded, leaving the vector uninitialised on failure. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FcwDG9RDPdiXBjqw8zvUNu --- src/rinterface_extra.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/rinterface_extra.c b/src/rinterface_extra.c index b9699995125..0ffcc2f4d40 100644 --- a/src/rinterface_extra.c +++ b/src/rinterface_extra.c @@ -6157,8 +6157,14 @@ SEXP Rx_igraph_is_chordal(SEXP graph, SEXP alpha, SEXP alpham1, SEXP result, names; /* Convert input */ Rz_SEXP_to_igraph(graph, &c_graph); - if (!Rf_isNull(alpha)) { Rz_SEXP_to_vector_int_copy(alpha, &c_alpha); } - if (!Rf_isNull(alpham1)) { Rz_SEXP_to_vector_int_copy(alpham1, &c_alpham1); } + if (!Rf_isNull(alpha)) { + IGRAPH_R_CHECK(Rz_SEXP_to_vector_int_copy(alpha, &c_alpha)); + IGRAPH_FINALLY_PV(igraph_vector_int_destroy, &c_alpha); + } + if (!Rf_isNull(alpham1)) { + IGRAPH_R_CHECK(Rz_SEXP_to_vector_int_copy(alpham1, &c_alpham1)); + IGRAPH_FINALLY_PV(igraph_vector_int_destroy, &c_alpham1); + } if (LOGICAL(pfillin)[0]) { if (0 != igraph_vector_int_init(&c_fillin, 0)) { igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); @@ -6187,6 +6193,16 @@ SEXP Rx_igraph_is_chordal(SEXP graph, SEXP alpha, SEXP alpham1, } else { PROTECT(newgraph=R_NilValue); } + /* Unwound in reverse order of registration, so that IGRAPH_FINALLY_CLEAN() + * pops the entry that belongs to the vector being destroyed. */ + if (!Rf_isNull(alpham1)) { + igraph_vector_int_destroy(&c_alpham1); + IGRAPH_FINALLY_CLEAN(1); + } + if (!Rf_isNull(alpha)) { + igraph_vector_int_destroy(&c_alpha); + IGRAPH_FINALLY_CLEAN(1); + } SET_VECTOR_ELT(result, 0, chordal); SET_VECTOR_ELT(result, 1, fillin); SET_VECTOR_ELT(result, 2, newgraph); From 78c48a35d272e805daa0fee391689e396a27378b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 18:38:15 +0000 Subject: [PATCH 3/4] ci: run R-CMD-check on claude/** branches The workflow only triggered on push to main/master/release/next/cran-* and on pull_request against main/master, so a PR based on another claude/** branch got no CI runs at all. Add claude/** to both branch filters. The push trigger is the one that matters for coverage: the versions-matrix step is skipped for same-repo pull requests, but runs for push events, so pushing a claude/** branch generates the version matrix and exercises the rcc-full job. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019E6CUgb5aFw6uvWXztcHgV --- .github/workflows/R-CMD-check.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 007c01cd86f..e4dc38469eb 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -12,10 +12,12 @@ on: - release - next - cran-* + - claude/** pull_request: branches: - main - master + - claude/** workflow_dispatch: inputs: ref: From 254741aeb8ff5b2df26f031d110a89ba7e750f1e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 21:38:30 +0000 Subject: [PATCH 4/4] ci: Run the sanitizer check on `claude/**` branches too The previous commit broadened `R-CMD-check.yaml` to `claude/**`, but the sanitizer lives in `build-and-check.yml` (`R-CMD-check-extra`), a separate workflow that only fires on pushes to `cran-*`, the nightly schedule, and manual dispatch. So the full-matrix branch exercised everything except the sanitizer. Add `claude/**` to its push filter, so pushing a test branch runs the sanitizer alongside the rest of the matrix. Deliberately not adding a `pull_request` trigger: that would run the ~40-minute sanitizer job on every pull request, which is what the nightly schedule is for. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FcwDG9RDPdiXBjqw8zvUNu --- .github/workflows/build-and-check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index 6b15172e125..3d8c3ff2dce 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -4,6 +4,7 @@ on: push: branches: - cran-* + - claude/** schedule: - cron: '0 2 * * *' workflow_dispatch: