| Title: | Migrate Installed R Packages Between R Versions |
|---|---|
| Description: | Detects all R installations on the current machine and migrates installed R packages between them. Provides find_routes() to discover R versions, manifest() to scan package libraries via 'subprocess', inventory() to compare two libraries, and ship() to install packages into a target R version using 'pak'. Includes a Shiny dashboard (open_hub()) for interactive source-to-target migration. |
| Authors: | Lennon Li [aut, cre] |
| Maintainer: | Lennon Li <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.3.0 |
| Built: | 2026-06-09 19:15:51 UTC |
| Source: | https://github.com/lennon-li/courier |
Run an R command in the background and log output
dispatch( project_path, expr, phase, label, rscript_path = NULL, timeout_sec = 600L )dispatch( project_path, expr, phase, label, rscript_path = NULL, timeout_sec = 600L )
project_path |
Path to the project |
expr |
Expression to run (as a quoted expression or function) |
phase |
Character: "baseline" or "post_migration" |
label |
Character: "document", "test", or "check" |
rscript_path |
Optional path to Rscript |
timeout_sec |
Timeout in seconds |
A list with process info
tmp <- tempdir() job <- dispatch(tmp, "message('hello')", "baseline", "document") Sys.sleep(1) job$process$is_alive()tmp <- tempdir() job <- dispatch(tmp, "message('hello')", "baseline", "document") Sys.sleep(1) job$process$is_alive()
Scans the current machine for every R installation it can find, across multiple sources per platform, and returns a tidy data frame of results.
find_routes(search_paths = NULL)find_routes(search_paths = NULL)
search_paths |
An optional character vector of additional paths to
search. Each element may be a directory containing |
Detection sources by platform:
Windows
HKLM registry (SOFTWARE\R-core\R) — standard admin installs via
the CRAN Windows installer.
HKCU registry (SOFTWARE\R-core\R) — non-admin installs that
register under the current user hive only.
%ProgramFiles%\R — directory scan for admin installs not in the
registry.
%LOCALAPPDATA%\Programs\R — rig-managed and other user-local
installs.
%USERPROFILE%\Documents\R — installs placed in the user's
Documents folder.
rig (rig list) — any additional versions managed by rig that
were not found by path scanning.
macOS
/Library/Frameworks/R.framework/Versions — system-wide CRAN
installer.
~/Library/Frameworks/R.framework/Versions — user-local framework
installs (no admin required).
Homebrew: /opt/homebrew/opt/r (Apple Silicon) and
/usr/local/opt/r (Intel).
rig (rig list) — rig-managed versions.
Linux
/opt/R — rig system-wide installs.
~/.local/share/rig/R — rig user-local installs.
conda environments (active $CONDA_PREFIX).
System Rscript on $PATH.
Symlinks are resolved via fs::path_real() so that duplicate entries from
different detection sources pointing to the same executable are collapsed.
A data frame with one row per unique R installation and the following columns:
Character. R version string, e.g. "4.4.1".
Character. Absolute path to the Rscript executable.
Character. The installation directory (R.home()), i.e. where
this R is installed. Normalized lexically for comparison.
Character. The installation's primary library location
(.libPaths()[1] under a vanilla session) — where install.packages()
writes by default. This is the effective package store; two installs
that share a library hold the same packages. Normalized lexically for
comparison. NA if it could not be determined.
Logical. TRUE for the R session running courieR.
routes <- find_routes() routes[, c("version", "rscript_path", "is_current")] # include a non-standard install routes <- find_routes(search_paths = "/opt/custom-r/bin/Rscript")routes <- find_routes() routes[, c("version", "rscript_path", "is_current")] # include a non-standard install routes <- find_routes(search_paths = "/opt/custom-r/bin/Rscript")
Detect project characteristics
inspect_shipment(project_path)inspect_shipment(project_path)
project_path |
Path to the project |
A named list
res <- inspect_shipment(tempdir()) res$is_packageres <- inspect_shipment(tempdir()) res$is_package
Takes two package manifests (from manifest()) and classifies every
source package as missing, outdated, newer, or the same relative to the
target.
inventory(source_pkgs, target_pkgs)inventory(source_pkgs, target_pkgs)
source_pkgs |
|
target_pkgs |
|
A named list with the following elements:
missingPackages in source that are absent from target.
outdatedPackages where the source version is newer than the target version.
newerPackages where the target already has a newer version than the source.
samePackages at identical versions in both installations.
comparisonFull merged data.table of all source packages
with a status column ("missing", "outdated", "newer",
or "same").
summaryOne-row data.frame with counts: missing,
outdated, newer, same, total_source.
Each data.table includes columns package, version.x (source
version), version.y (target version), and source.
src <- data.table::data.table( package = c("dplyr", "ggplot2"), version = c("1.1.4", "3.5.1"), priority = NA_character_ ) tgt <- data.table::data.table( package = "dplyr", version = "1.0.0" ) inventory(src, tgt)src <- data.table::data.table( package = c("dplyr", "ggplot2"), version = c("1.1.4", "3.5.1"), priority = NA_character_ ) tgt <- data.table::data.table( package = "dplyr", version = "1.0.0" ) inventory(src, tgt)
Runs a subprocess under the given R executable and returns all user-installed packages. Base and recommended packages are excluded automatically.
manifest( rscript_path = NULL, lib_path = NULL, format = c("data.table", "data.frame"), timeout_sec = 30L )manifest( rscript_path = NULL, lib_path = NULL, format = c("data.table", "data.frame"), timeout_sec = 30L )
rscript_path |
Full path to an |
lib_path |
Library path to query within the target R. Defaults to the
first element of |
format |
|
timeout_sec |
Maximum seconds to wait for the subprocess. Increase
this on slow machines or network-mounted drives. Default |
A data.table (or data.frame) with one row per user-installed
package and columns: package, version, source ("CRAN",
"GitHub", "Bioconductor", or "unknown"), remotetype,
remoteusername, remoterepo, libpath. Base and recommended packages
are never included in the output.
pkgs <- manifest() head(pkgs)pkgs <- manifest() head(pkgs)
Convenience wrapper around find_routes() and ship(). Matches
installations by version string (e.g. "4.5.2") or full Rscript path, then
runs the migration. Use ship() directly if you need fine-grained control.
migrate(from, to, dry_run = FALSE, upgrade = TRUE, mode = "online", ...)migrate(from, to, dry_run = FALSE, upgrade = TRUE, mode = "online", ...)
from |
Version string or Rscript path of the source installation (packages are copied from here). |
to |
Version string or Rscript path of the target installation (packages are installed into here). |
dry_run |
If |
upgrade |
If |
mode |
Transfer mode passed to |
... |
Additional arguments passed to |
The same named list returned by ship(): plan, results,
comparison, dry_run, elapsed_sec.
## Not run: # dry run first migrate("4.5.2", "4.6.0", dry_run = TRUE) # for real result <- migrate("4.5.2", "4.6.0") table(result$results$status) ## End(Not run)## Not run: # dry run first migrate("4.5.2", "4.6.0", dry_run = TRUE) # for real result <- migrate("4.5.2", "4.6.0") table(result$results$status) ## End(Not run)
Creates .courier-depot/ and its subdirectories in the project path.
Writes a .gitignore to prevent tracking of logs and artifacts.
open_depot(project_path)open_depot(project_path)
project_path |
Path to the R project |
Invisibly returns the path to the .courier-depot directory.
depot <- open_depot(tempdir())depot <- open_depot(tempdir())
Opens the Shiny dashboard in your browser. The dashboard detects all R installations on the machine and lets you compare and sync packages between any two of them without writing any code.
open_hub(project_path = NULL, port = NULL, launch.browser = TRUE) hub(project_path = NULL, port = NULL, launch.browser = TRUE)open_hub(project_path = NULL, port = NULL, launch.browser = TRUE) hub(project_path = NULL, port = NULL, launch.browser = TRUE)
project_path |
Reserved; currently unused. |
port |
Port to run the Shiny app on. |
launch.browser |
Whether to open the system browser automatically. Default |
hub() is a short alias for open_hub().
Called for its side effect of launching a Shiny application.
if (interactive()) { hub() # short form open_hub() # same thing }if (interactive()) { hub() # short form open_hub() # same thing }
Parse test log
parse_dispatch_log(log_path)parse_dispatch_log(log_path)
log_path |
Path to the log file |
data.table
tmp <- tempfile(fileext = ".log") writeLines(c( "-- Failure (test-foo.R:1): addition works ----", "Expected 3, got 4." ), tmp) parse_dispatch_log(tmp) file.remove(tmp)tmp <- tempfile(fileext = ".log") writeLines(c( "-- Failure (test-foo.R:1): addition works ----", "Expected 3, got 4." ), tmp) parse_dispatch_log(tmp) file.remove(tmp)
Parse R CMD check log
parse_inspection_log(log_path)parse_inspection_log(log_path)
log_path |
Path to the log file |
data.table
tmp <- tempfile(fileext = ".log") writeLines(c( "* checking examples ... WARNING", " An example result is marked with \\donttest." ), tmp) parse_inspection_log(tmp) file.remove(tmp)tmp <- tempfile(fileext = ".log") writeLines(c( "* checking examples ... WARNING", " An example result is marked with \\donttest." ), tmp) parse_inspection_log(tmp) file.remove(tmp)
Classify shipment risk based on check and test results
rate_shipment(baseline_results, post_results)rate_shipment(baseline_results, post_results)
baseline_results |
data.table from baseline check |
post_results |
data.table from post-shipment check |
A list
baseline <- data.table::data.table( severity = character(), message = character(), file = character(), line = character() ) post <- data.table::data.table( severity = "ERROR", message = "undefined symbol", file = "R/foo.R", line = "10" ) rate_shipment(baseline, post)baseline <- data.table::data.table( severity = character(), message = character(), file = character(), line = character() ) post <- data.table::data.table( severity = "ERROR", message = "undefined symbol", file = "R/foo.R", line = "10" ) rate_shipment(baseline, post)
Check if rig is available
rig_available()rig_available()
Logical
rig_available()rig_available()
Install R via rig
rig_install(version, wait = TRUE)rig_install(version, wait = TRUE)
version |
R version |
wait |
Logical |
The result of processx::run().
if (interactive() && rig_available()) { rig_install("4.5.0", wait = FALSE) }if (interactive() && rig_available()) { rig_install("4.5.0", wait = FALSE) }
List rig installations
rig_list()rig_list()
data.frame
if (rig_available()) rig_list()if (rig_available()) rig_list()
Compares the package libraries of two R installations and transfers missing or outdated packages into the target.
ship( source_path, target_path, packages = NULL, dry_run = FALSE, upgrade = FALSE, log_callback = NULL, mode = c("online", "offline", "preserve"), source_pkgs = NULL, target_pkgs = NULL, ... )ship( source_path, target_path, packages = NULL, dry_run = FALSE, upgrade = FALSE, log_callback = NULL, mode = c("online", "offline", "preserve"), source_pkgs = NULL, target_pkgs = NULL, ... )
source_path |
Full path to the |
target_path |
Full path to the |
packages |
Character vector of package names to act on. If |
dry_run |
If |
upgrade |
If |
log_callback |
Optional function of one argument. When provided, it is called with a single character string for each progress message emitted during package transfer. |
mode |
Transfer mode: |
source_pkgs, target_pkgs
|
Optional pre-scanned manifests (as returned by
|
... |
Reserved for future arguments. |
A named list with the following elements:
plandata.table of planned actions with columns package,
action ("install" or "upgrade"), mode, version.x (source
version), version.y (target version, NA if the package is
missing), and pak_spec (the spec passed to pak).
resultsdata.table of per-package outcomes with columns
package, status ("success", "skipped", or "error"), and
message.
comparisonThe raw inventory() comparison table.
dry_runTRUE if no packages were installed.
elapsed_secTotal wall-clock time in seconds.
ship() can install packages into the target R library via
pak::pkg_install() running in a subprocess, or copy package directories
directly for offline/preserve transfers. Set dry_run = TRUE to preview the
migration plan without installing or copying anything. The source R need not
have pak installed. Subprocess calls and file copies are confined to the
target library path and R temporary directory.
routes <- find_routes() if (nrow(routes) >= 2) { result <- ship( source_path = routes$rscript_path[1], target_path = routes$rscript_path[2], dry_run = TRUE ) print(result$plan) }routes <- find_routes() if (nrow(routes) >= 2) { result <- ship( source_path = routes$rscript_path[1], target_path = routes$rscript_path[2], dry_run = TRUE ) print(result$plan) }
Scan project dependencies
take_inventory(project_path)take_inventory(project_path)
project_path |
Path to the project |
A data.table
take_inventory(tempdir())take_inventory(tempdir())
Generate a pak specification for a package
wrap(package, version = NULL, source_hint = NULL, github_ref = NULL)wrap(package, version = NULL, source_hint = NULL, github_ref = NULL)
package |
Package name |
version |
Optional version constraint or exact version |
source_hint |
Optional hint: "CRAN", "Bioconductor", "GitHub", "local" |
github_ref |
Optional GitHub ref like "owner/repo@ref" |
A character vector of pak specs
wrap("dplyr") wrap("dplyr", version = "1.1.4") wrap("mypackage", source_hint = "Bioconductor") wrap("r-lib/rlang", source_hint = "GitHub", github_ref = "r-lib/rlang")wrap("dplyr") wrap("dplyr", version = "1.1.4") wrap("mypackage", source_hint = "Bioconductor") wrap("r-lib/rlang", source_hint = "GitHub", github_ref = "r-lib/rlang")