Skip to content

ci: migrate R-CMD-check to the org reusable workflow - #206

Closed
eliotmcintire wants to merge 99 commits into
developmentfrom
ci/migrate-to-org-workflow
Closed

eliotmcintire wants to merge 99 commits into
developmentfrom
ci/migrate-to-org-workflow

Conversation

@eliotmcintire

Copy link
Copy Markdown
Contributor

Replaces this repo's hand-rolled matrix with a thin caller on PredictiveEcology/actions/.github/workflows/R-CMD-check.yaml@main.

143 → 27 lines.

Why

Third-party action versions, system dependencies, caching and the check itself were maintained separately in 17 repos. This repo was on actions/checkout@v5; the org spread was v2/v4/v5/v6/v7. Node 20 deprecation warnings are the current symptom. Consolidated, bumping it once in actions updates every repo.

Customizations preserved

_R_CHECK_THINGS_IN_OTHER_DIRS_ via extra-env; oldrel-2 (both OSes) and oldrel-3 (Ubuntu) via extra-config; and critically the runLong leg — R_REQUIRE_RUN_LONG_CI=true on one Ubuntu/release leg, carried as per-leg extra-env. Your slow-test gate and its concurrency-cap rationale survive intact.

Dropped deliberately

Nothing. Gains the no-suggests leg.

Depends on PredictiveEcology/actions#27 (per-leg extra-env).

🤖 Generated with Claude Code

https://claude.ai/code/session_012DVjmY3im9Xak7tXLSMGCa

eliotmcintire and others added 25 commits March 31, 2026 17:02
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Convert all \dontrun{} examples to \donttest{} in R sources and man pages
- Update Date in DESCRIPTION to 2026-03-31
- Update cran-comments.md for 1.1.0 submission

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- fix-typo in README.md lmer to lme4
fix-typo in README.md lmer to lme4
When packages are found to be missing from CRAN (e.g. archived), the
message now shows the full chain of packages that require them, e.g.:
  fastdigest (required by: digest -> reproducible), pryr not on CRAN

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brings in the 2.0.0 release prep work: pak as default install backbone,
install='force' semantic fixes, pre-install integrity check, Windows
SSL warning suppression, and the carried-over 1.1.1 CRAN-prep cleanups.

See NEWS.md (# Require 2.0.0) for the full list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

# Conflicts:
#	.Rbuildignore
#	DESCRIPTION
#	cran-comments.md
#	revdep/README.md
#	revdep/problems.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…work installs

CRAN cancelled the Require 2.0.0 submission (Uwe Ligges, 2026-05-15):

  anduin3.wu.ac.at : hornik : user NOT in sudoers ;
  PWD=.../Require.Rcheck/tests/testthat ; USER=root ; COMMAND=/bin/sh -c id

Root cause: pak's pkgdepends has an automatic system-requirements
("sysreqs") subsystem. On Linux it probes for passwordless sudo
(`sudo sh -c id`) and will `apt-get install` missing system libs.
Require shipped pak as the default backbone WITHOUT disabling this,
so when CRAN ran the test suite, an integration test drove
Require()->pak->sysreqs and pak attempted sudo on CRAN's machine.
CRAN treats any sudo from package code as a machine-hijack attempt.

This is NOT Require code calling sudo directly -- there is no `sudo`
anywhere in R/ or tests/. It is pak's documented-but-dangerous default
that Require failed to neutralise before making pak the default
installer. Owning that miss; fixing it on two independent levels:

1. PRIMARY (R/zzz.R .onLoad, before the first pak call): force the
   entire pak sysreqs subsystem off, process-wide, the moment Require
   loads -- env vars PKG_SYSREQS=false / PKG_SYSREQS_SUDO=false (these
   are inherited by pak's callr subprocess, which is the load-bearing
   part) plus options(pkg.sysreqs=FALSE, pkg.sysreqs_sudo=FALSE).
   Only set when the user has not made an explicit choice, so a user
   who deliberately opts in keeps ownership of that decision.
   Reinforced per-call in pakCall() (defense in depth). Require must
   NEVER escalate privileges or install OS packages -- that is the
   user's/admin's responsibility, outside Require's remit.

2. SECONDARY (CRAN hygiene): test-01/04/11 did real network installs
   via pak BEFORE any skip guard (test-01's `skip_on_cran()` was
   buried ~220 lines in, after several real installs -- which is how
   pak reached CRAN's machine at all). CRAN policy: tests must not
   require internet. Hoisted `skip_on_cran()` to the very top of all
   three test_that blocks, before any pak/network call. GHA still runs
   the full suite. (test-05/06/08/09/10/12/16 were already gated;
   test-02/03/13/14/15/17 do no network/pak on CRAN -- verified.)

Verified: loading Require sets PKG_SYSREQS=false; test-01/04/11 now
report skipped=1 failed=0 passed=0 under NOT_CRAN="" (CRAN-like env).

Version stays 2.0.0: CRAN cancelled the pretest, 2.0.0 was never
accepted/published, so re-submitting the same version with the fix is
the expected convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… preserve opt-in

Adds tests/testthat/test-18nosudo_testthat.R and fixes an inconsistency
in the sysreqs guard that silently defeated a legitimate user opt-in.

Why the opt-in fix: zzz.R .onLoad set the sysreqs-off env/options only
when unset (respecting an explicit user opt-in), but pakCall() then
re-asserted them UNCONDITIONALLY on every pak call -- so a user who set
PKG_SYSREQS=true (or options(pkg.sysreqs=TRUE)) before loading Require
had it silently clobbered. Opt-in was effectively impossible.

Fix (R/zzz.R, R/pak.R):
- New pure helpers .sysreqsTruthy() / .sysreqsUserOptedIn() interpret
  the env var / option consistently.
- .onLoad captures the user's explicit opt-in ONCE (before mutating
  anything) into pkgEnv() as .sysreqsUserOptIn, then only forces the
  subsystem off when the user did NOT opt in.
- pakCall() consults that flag: it re-asserts OFF by default, but never
  clobbers an explicit opt-in. Default (flag absent) = force off (safe).
This stays CRAN-safe: CRAN's check machines never set the opt-in, so
the force-off path always applies there; an informed user on their own
machine keeps ownership of the sudo/system-install decision.

Regression test (test-18nosudo), two layers:
  (A) Always-on, deterministic, no-network unit tests of the contract
      (.sysreqsTruthy, .sysreqsUserOptedIn, and that pakCall forces off
      by default but preserves an explicit opt-in). These run on CRAN
      too and guard the fix on every platform.
  (B) A faithful sudo-trap integration test (skip_on_cran, Linux only):
      builds a fixture pkg whose Config/pak/sysreqs names a real but
      absent apt package (detected at runtime; skip if none), and a
      PATH-shadowing fake `sudo` that records invocations and exits 1
      (mimicking CRAN's "user NOT in sudoers"; it never calls real sudo
      and can install nothing). Includes an in-test NEGATIVE CONTROL
      (bare pak, no Require -> trap MUST fire) so the protective
      assertion (Require loaded -> trap MUST stay empty) can never pass
      vacuously. Unique fixture names per run defeat pak's persistent
      content-addressed build cache; child env force-unsets PKG_SYSREQS
      via NA because callr/processx inherit the parent env and apply
      `env=` only as overrides.

Verified locally: test-18 28/0/0 (negative control fires, protective
stays empty); test-17usePak 125/0/0 (no regression from the opt-in
refactor).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…NEWS

cran-comments.md: finalized for re-submission -- accurate R-version
matrix (release 4.6.0; oldrel-1/2/3 = 4.5.3/4.4.3/4.3.3; win-builder
release 4.6.x / oldrelease 4.5.x), unverified "Local" line removed,
cancelled-submission narrative + two-part fix description reflect the
shipped behaviour (sysreqs disabled by default; explicit opt-in
preserved). .Rbuildignore'd, so not in the tarball.

NEWS.md: added a breaking-changes bullet -- pak's automatic
system-requirements installation is disabled by default (it can probe
sudo / apt-get install system libs; that is the user's/admin's
responsibility and is unwanted in containers/CI/HPC/CRAN). Opt back in
with PKG_SYSREQS=true or options(pkg.sysreqs=TRUE) before loading
Require; honoured everywhere. Guarded by test-18nosudo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GHA R-CMD-check on 91e7085 failed with:
  checking for unstated dependencies in 'tests' ... WARNING
  '::' or ':::' imports not declared from: 'callr' 'pkgload'

test-18nosudo_testthat.R uses callr::r() (clean subprocess for the
sudo-trap scenarios) and pkgload::load_all() (load the in-development
package inside that subprocess; library(Require) would only work under
R CMD check, not in a devtools::load_all dev run). R CMD check flags
undeclared `::` usage in tests *statically*, regardless of skip_*
guards, so this would also fail on CRAN.

Both are tiny, ubiquitous, test-only packages (callr is in fact already
present transitively via pak, but pak vendors its deps in a private
library and does not export them, so it cannot satisfy a declared
dependency for Require's own test code). Declared in Suggests.

The accompanying "detritus in the temp directory" NOTE is pre-existing
and GHA-only: it comes from source-building tests that run when
NOT_CRAN=true; on CRAN those are skip_on_cran() so no detritus there.
NOTEs do not fail the r-lib check action; the WARNING did.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. .github/workflows/R-CMD-check.yaml: the "Install R deps (retry
   fallback)" step used nick-fields/retry@v3 with `shell: Rscript {0}`.
   nick-fields/retry only supports a fixed shell set (bash/sh/pwsh/
   cmd/python) -- the `Rscript {0}` custom shell is valid only in plain
   `run:` steps. On windows-devel the primary setup-r-dependencies hit
   a transient failure, the fallback fired, and the retry action died
   immediately with "Shell Rscript not supported", failing the job.
   Fixed: `shell: bash` + the R code wrapped in `Rscript -e ...`
   (multiple -e args run sequentially in one process; behaviour
   identical). bash is available on all r-lib runners incl. Windows
   Git Bash. (test-coverage.yaml / pkgdown.yaml also use
   `shell: Rscript {0}` but in plain steps, which is valid -- left
   as-is.)

2. cran-comments.md: the GHA version list rendered "R 4.5.3" bare,
   reading as if it were the release. Verified the per-job resolved
   versions from the actual run on the submitted SHA (4336b49):
   release = R 4.6.0; oldrel-1/2/3 = 4.5.3 / 4.4.3 / 4.3.3;
   devel = 2026-05-12 r90049. Relabelled every line with its role so
   the release (4.6.0) is unmistakable, and pinned win-builder to
   concrete numbers (release 4.6.0, oldrelease 4.5.3). No "local"
   line: the 51-min local --as-cran was not re-run after the
   test/DESCRIPTION changes, so asserting local 0/0/0 would be untrue;
   GHA + win-builder cover the matrix truthfully.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
devtools::submit_cran() recorded the 2.0.0 submission at SHA
4c0f8a8; CRAN accepted it. Update the marker (was the stale 1.0.0
2024 entry). .Rbuildignore'd, so not in the tarball.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Conflicts in 15 files (DESCRIPTION, 7 R/, 5 man/, cran-comments.md,
test-18nosudo) resolved in favour of development: every release-time fix
main carried from the 2.0.0 cycle was verified present in development
first -- the pak sysreqs/sudo guard (4e80b2f) in R/zzz.R and R/pak.R,
its test-18 regression guard (4906da1), the callr/pkgload dependency
declarations (4336b49), and the nick-fields/retry `shell: bash` fix
(4c0f8a8). Nothing from that cycle is lost.

Two main-only changes auto-merged and were deliberately KEPT, because
development never had them:

  * R/Require-helpers.R: isUbuntuOrDebian() wraps grepl() in isTRUE(),
    from PR #118 -- guards a NULL utils::osVersion, where bare grepl()
    yields logical(0) and errors in `if`.
  * README.md: the libPaths= argument form, the packageVersionFile=TRUE
    note, and a comment tidy.

Both need merging back into development after this release.

docs/ (pkgdown output, .Rbuildignore'd) stays main-only, untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZWdhVg8NimEUKT5g8mYt9
Written by devtools:::flag_release() at upload. Awaiting the maintainer's
confirmation-link click and CRAN's review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZWdhVg8NimEUKT5g8mYt9
MRAN (Microsoft's CRAN mirror/snapshot service) was retired in July 2023;
Require does not and cannot search it. The line predates the shutdown and
came back into development with the 7cf8761 cherry-pick.

Keeps that commit's renv/versions framing, which is accurate and useful,
and replaces only the false clause. Require retrieves old versions from
the CRAN archives on every platform, not binaries from MRAN on Windows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZWdhVg8NimEUKT5g8mYt9
(cherry picked from commit a04b15a)
usethis::use_github_release() removes the file once the release is
published; that is the usethis strategy. The record it held is not lost:
the submitted contents are in db9dd69, and the v2.1.0 release itself is
pinned to the same SHA (058c44e), the tree that was actually uploaded.
submit_cran() writes a fresh CRAN-SUBMISSION at the next submission.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZWdhVg8NimEUKT5g8mYt9
Without a `codecov: branch:` setting, Codecov falls back to `master`. This
repo's default branch is `main` and `master` does not exist, so the repo
total was computed from nothing and read far below the real figure.

SpaDES.tools already carried this fix; propagating it to the rest of the
packages whose default branch is main. Applied to both main and development
so the branches do not drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces this repo's hand-rolled matrix with a thin caller, so third-party
action versions, system dependencies, caching and the check itself are
maintained in one place instead of 17. Bumping actions/checkout there now
updates every repo at once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012DVjmY3im9Xak7tXLSMGCa
The org concurrency budget is shared across every PredictiveEcology repo and is
the binding constraint on getting work through. These legs added R versions
below the default matrix; testing older R is explicitly not worth the runner
contention it causes.

oldrel-N is relative, so the default matrix's floor still rises on its own with
each R release -- no standing commitment to any particular old version.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012DVjmY3im9Xak7tXLSMGCa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants