Skip to content

feat(positron-r): support portable Mac R builds#13325

Open
jmwoliver wants to merge 1 commit into
mainfrom
feat/r-portable-mac-fallback
Open

feat(positron-r): support portable Mac R builds#13325
jmwoliver wants to merge 1 commit into
mainfrom
feat/r-portable-mac-fallback

Conversation

@jmwoliver

@jmwoliver jmwoliver commented May 1, 2026

Copy link
Copy Markdown
Contributor

The portable Mac R builds from rstudio/r-builds#284 that we just merged let you extract a tarball anywhere and have R work. Historically, Mac R builds have not been relocatable so Positron did not support this.

On Mac, the R wrapper script bakes in an absolute R_HOME_DIR line at build time. Positron read that line and used it as-is, so portable installs ended up pointing at a path that didn't exist on the user's machine. If the user happened to have a CRAN R at that same path, Positron silently registered the portable with the CRAN install's metadata instead.

The fix adds a path-derived fallback to getRHomePathDarwin: if the parsed R_HOME_DIR doesn't validate, or the binary doesn't actually live under it, fall back to dirname(dirname(realpath(binpath))). This implements similar relative path discovery used by Windows since R is already relocatable on Windows. System R installs still take the parsed path; portable builds get rescued by the fallback.

Test plan

Download a Mac R tarball and extract it:

R_VERSION=4.4.3
curl -O https://cdn.posit.co/r/macos/R-${R_VERSION}-macos-arm64.tar.gz
mkdir -p ~/R
tar xzf R-${R_VERSION}-macos-arm64.tar.gz -C ~/R
~/R/R-${R_VERSION}/bin/R --version

Point Positron at it via user settings (adjust the path if you extracted elsewhere):

"positron.r.interpreters.override": [
  "/Users/<you>/R/R-4.4.3/bin/R"
]

On current main the portable install is rejected:

2026-05-01 09:39:14.056 [info] R installation paths from 'positron.r.interpreters.override' to exclusively include:
[
  "/Users/jacobwoliver/R/R-4.4.3/bin/R"
]
2026-05-01 09:39:14.056 [info] Candidate R binary at /Users/jacobwoliver/R/R-4.4.3/bin/R
2026-05-01 09:39:14.056 [info] Can't find DESCRIPTION for the utils package at /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library/utils/DESCRIPTION
2026-05-01 09:39:14.056 [info] Filtering out /Users/jacobwoliver/R/R-4.4.3/bin/R, reason: Invalid installation.
2026-05-01 09:39:14.056 [warning] All discovered R installations are unusable by Positron.
2026-05-01 09:39:14.056 [warning] Learn more about R discovery at https://positron.posit.co/r-installations

On this branch it registers correctly with the right version and homepath:

2026-05-01 09:36:49.258 [info] Candidate R binary at /Users/jacobwoliver/R/R-4.4.3/bin/R
2026-05-01 09:36:49.258 [info] R installation discovered: {
  "usable": true,
  "supported": true,
  "reasonDiscovered": [
    "Found in a location specified via user settings"
  ],
  "reasonRejected": null,
  "binpath": "/Users/jacobwoliver/R/R-4.4.3/bin/R",
  "homepath": "/Users/jacobwoliver/R/R-4.4.3",
  "version": "4.4.3",
  "arch": "arm64",
  "current": false,
  "orthogonal": true,
  "default": false
}

When the R wrapper's hardcoded R_HOME_DIR doesn't point at a real R
install, derive R_HOME from dirname(dirname(realpath(binpath))) instead.
This rescues portable Mac R builds (rstudio/r-builds#284) extracted to
arbitrary locations, where the build-time R_HOME_DIR baked into bin/R
is stale.

getRHomePathDarwin now follows a try-then-validate-then-fallback flow:

1. Parse R_HOME_DIR from the script.
2. If it validates (DESCRIPTION exists at <home>/library/utils) AND the
   binpath actually lives under it (realpath check), return it. The
   binpath-under-home guard prevents conflation: a portable R-4.5.3 with
   R_HOME_DIR baked to /Library/.../4.5-arm64/Resources will validate
   against a CRAN R 4.5.1 installed at that same path on the user's
   machine. Without the guard the portable would silently register with
   CRAN's homepath and version.
3. Otherwise derive R_HOME from the binpath. realpath the binpath first
   so a shim like /usr/local/bin/R points at the real script.
4. If neither validates, return undefined as before.

New file-private helpers: derivedRHomePathDarwin, hasRBaseLibrary,
binpathIsUnder. Linux and Windows discovery are unchanged. The
RInstallation constructor's existing DESCRIPTION check stays as a
final cross-platform guard.

New unit tests in extensions/positron-r/src/test/r-installation.test.ts
cover all five rows of the behavior matrix:

- parsed R_HOME_DIR validates and binpath lives under it -> returned
- parsed is stale and points at nothing -> derived rescues
- parsed validates but binpath lives elsewhere (conflation) -> derived rescues
- neither validates -> undefined
- not an R wrapper script -> undefined

Refs rstudio/r-builds#284.
@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical

readme  valid tags

@juliasilge juliasilge requested a review from jennybc May 7, 2026 15:48
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.

1 participant