Skip to content

Fix R-devel R CMD check on GHA - Now temporarily disabled #143

@GegznaV

Description

@GegznaV

R-CMD-check on R-Devel on GHA fails. Currently I do not know how to fix that. So I will temorarily disable that paericular job - some working tests are better than no tests. Some context especially for AI assistance is below.

Related:

Details ----

GHA R-devel Roxygenize Failure — Debug Log & AI Context

Problem Statement

R CMD check on GHA fails exclusively on ubuntu-latest (R-devel).
The error is always one of:

  • undefined symbol: SET_GROWABLE_BIT — roxygen2 .so cannot load under R-devel
  • ERROR: a 'NAMESPACE' file is required — NAMESPACE was deleted before the crash

Root Cause Analysis

C ABI Break in R 4.6.0 (R-devel)

R 4.6.0 (r89670, March 2026) removed SET_GROWABLE_BIT from libR.so exported symbols — it became a static inline in R.h. Any roxygen2 binary compiled against R ≤ 4.5.x references this as an external symbol and cannot be loaded under R-devel.

roxygen2 7.3.x parser_setMethod Bug

roxygen2 ≥ 7.3.0 introduced a bug in parser_setMethod: it calls methods::getMethod(name, eval(call$signature), where = env) with no error handling. In a clean Rscript session, S4 method tables for primitives ([[, [, $, etc.) are not populated → crash.

hyperSpec has ~60+ setMethod() calls on primitives, making it particularly vulnerable.


Timeline of Fix Attempts

✅ Session 1 — all.equal S4 → S3 conversion

  • Problem: roxygen2 7.3.3 crashed on setMethod("all.equal", ...) (S3 generic + S4 registration)
  • Fix: Converted to S3 all.equal.hyperSpec <- .all.equal with @method all.equal hyperSpec @export
  • Outcome: ✅ Fixed locally. NAMESPACE updated to S3method(all.equal,hyperSpec)

✅ Session 2–3 — Pin roxygen2 to 7.2.3 on GHA

  • Problem: roxygen2 7.3.x crashes on setMethod("[[", ...) (S4 method on primitive) — cannot be fixed without rewriting ~60 S4 methods
  • Fix: GHA workflow pins to remotes::install_version("roxygen2", "7.2.3")
  • Outcome: ✅ Works on macOS/Windows/ubuntu release & oldrel

❌ Session 3 mistake — Removed the pin

  • Problem: Pin was removed thinking the all.equal fix was sufficient
  • Outcome: ❌ GHA failed on ALL platforms with parser_setMethod crash

❌ Attempt A — Pin 7.2.3 binary on R-devel

  • Assumption: Same pin would work for R-devel
  • Outcome: ❌ undefined symbol: SET_GROWABLE_BIT — binary compiled against older R ABI

❌ Attempt B — Compile roxygen2 7.2.3 from source on R-devel

  • Code: remotes::install_version("roxygen2", "7.2.3", type = "source")
  • Assumption: Source compilation against R-devel headers would avoid the symbol issue
  • Outcome: ❌ Same error — source-compiled 7.2.3 also references SET_GROWABLE_BIT as external symbol (Rcpp or R-internal headers in 7.2.3 still emit the reference)

❌ Attempt C — Skip Roxygenize step on R-devel + restore from git

  • Code: if: matrix.config.r != 'devel' on the Roxygenize step; separate "Restore NAMESPACE" step with git checkout HEAD -- NAMESPACE man/
  • Assumption: if: condition would prevent Roxygenize from running; setup-r-dependencies with local:. would not wipe NAMESPACE
  • Outcome: ❌ Still fails — devtools::document() still runs and still hits the SET_GROWABLE_BIT error
  • Analysis: Either (a) GHA if: step conditions don't reliably prevent execution in all scenarios, (b) the GHA run shown was from a pre-fix commit, or (c) setup-r-dependencies itself triggers devtools::document() internally via pak's local package installation

❌ Attempt D — tryCatch in Roxygenize step body (current)

  • Code: Run Roxygenize on ALL platforms; wrap in tryCatch; on failure system2("git", c("checkout", "HEAD", "--", "NAMESPACE", "man/")) and continue
  • Rationale: Eliminates reliance on if: conditions; self-healing; handles ALL failure modes:
    • Mode 1: dyn.load(roxygen2.so) fails → error caught → git restore → continue (NAMESPACE NOT wiped)
    • Mode 2: roxygen2 loads, parser_setMethod crashes after deleting NAMESPACE → error caught → git restore → continue
  • Removed: Separate "Restore NAMESPACE and man/ from git" step, sed -i '/^RoxygenNote:/d' DESCRIPTION modification
  • Outcome: 🔄 Testing

Key Facts

Fact Verified
roxygen2 7.3.3 RSPM binary fails on R-devel (SET_GROWABLE_BIT) ✅ Yes
roxygen2 7.2.3 RSPM binary fails on R-devel (SET_GROWABLE_BIT) ✅ Yes
roxygen2 7.2.3 source-compiled fails on R-devel (SET_GROWABLE_BIT) ✅ Yes
roxygen2 7.3.3 loads on R release (4.5.3) but crashes on [[ setMethod ✅ Yes
roxygen2 7.2.3 works correctly on R release ✅ Yes
setup-r-dependencies wipes NAMESPACE ❓ Unclear — pak does R CMD INSTALL, NOT devtools::document()
Roxygenize step if: matrix.config.r != 'devel' prevents execution ❓ Unclear — error still shows in log

How Similar S4 Repos Handle This

  • Bioconductor (BiocGenerics, S4Vectors, IRanges): Pre-commit NAMESPACE and man/, skip roxygenize in CI, or don't use roxygen2 at all
  • Matrix CRAN package: Hand-written Rd files, no roxygen2 in CI
  • Common pattern: The Roxygenize step uses tryCatch() → on failure, restore from git and continue with committed docs

Current Strategy (Attempt D — tryCatch)

Use tryCatch in the Roxygenize step instead of relying on if: conditions:

ok <- tryCatch({
  devtools::document()
  TRUE
}, error = function(e) {
  message("devtools::document() failed: ", conditionMessage(e))
  message("Restoring NAMESPACE and man/ from committed versions...")
  system2("git", c("checkout", "HEAD", "--", "NAMESPACE", "man/"))
  FALSE
})
if (!ok) message("Using committed documentation.")

Why this is correct:

  • On R-devel: dyn.load(roxygen2.so) fails before touching NAMESPACE → error caught → git checkout is a no-op (NAMESPACE intact) → continue with committed docs ✅
  • On R-devel (if parker_setMethod crashes after NAMESPACE deletion): error caught → git checkout restores → continue ✅
  • On R release/oldrel (with pinned 7.2.3): devtools::document() succeeds → no error → continue with fresh docs ✅
  • No if: conditions to debug or misfire ✅

Companion changes:

  • "Restore NAMESPACE and man/ from git (R-devel)" step removed (handled inside Roxygenize)
  • sed -i '/^RoxygenNote:/d' DESCRIPTION removed (was a hack; RoxygenNote should stay)
  • Session info step re-added (was accidentally dropped) using sessioninfo::session_info()

How Similar S4 Repositories Handle This

  • Bioconductor (BiocGenerics, S4Vectors, IRanges): Pre-commit NAMESPACE and man/, skip roxygenize in CI entirely, or don't use roxygen2
  • Matrix (CRAN): Hand-written Rd files, no roxygen2 in CI pipeline
  • Common CRAN S4 packages: Pre-commit docs and use tryCatch or non-roxygenize CI flows
  • Best practice: When breaking R API changes occur (like 4.6.0 ABI break), use defensive tryCatch rather than OS/version conditions

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions