courieR syncs installed R packages between R versions on the same machine. You can migrate from an old version to a new one, keep two versions in parity, or selectively push packages in either direction — all from the R console or from a point-and-click dashboard.
Install courieR from CRAN:
The core CLI workflow (find_routes(),
manifest(), inventory(), ship())
has no Shiny dependency. The dashboard (open_hub())
requires additional packages:
find_routes() scans the system and returns a data frame
of every R version it can find:
library(courieR)
routes <- find_routes()
routes
#> version rscript_path is_current
#> 1 4.4.2 C:/Program Files/R/R-4.4.2/bin/x64/Rscript.exe TRUE
#> 2 4.3.1 C:/Users/you/AppData/Local/Programs/R/R-4.3.1/bin/... FALSE
#> 3 4.1.3 C:/Users/you/Documents/R/R-4.1.3/bin/... FALSEis_current = TRUE marks the R session you are running
right now.
| Platform | Locations checked |
|---|---|
| Windows | HKLM/HKCU registry, %ProgramFiles%\R,
%LOCALAPPDATA%\Programs\R,
%USERPROFILE%\Documents\R, rig |
| macOS | /Library/Frameworks/R.framework,
~/Library/Frameworks/R.framework, Homebrew
(/opt/homebrew, /usr/local), rig |
| Linux | /opt/R (rig system), ~/.local/share/rig/R
(rig user), conda envs, system Rscript on
$PATH |
To include a non-standard path, pass it explicitly:
The four core functions form a pipeline:
find_routes() → manifest() → inventory() → ship()
discover scan compare migrate
manifest()manifest() runs a subprocess under a given Rscript and
returns every installed package with its version and source:
# scan the first (newest) R
src_pkgs <- manifest(rscript_path = routes$rscript_path[1])
head(src_pkgs[, c("package", "version", "source")])
#> package version source
#> 1 broom 1.0.7 CRAN
#> 2 callr 3.7.6 CRAN
#> 3 courieR 0.2.0 GitHub
#> 4 ggplot2 3.5.1 CRAN
#> 5 glue 1.8.0 CRAN
#> 6 stringr 1.5.1 CRANCalling manifest() with no arguments scans the library
of the current R session:
Base and recommended packages are included in the raw manifest. Filter them out before comparison:
inventory()inventory() takes two manifests and returns a classified
diff:
src_pkgs <- manifest(rscript_path = routes$rscript_path[2]) # old R
tgt_pkgs <- manifest(rscript_path = routes$rscript_path[1]) # new R
comp <- inventory(src_pkgs, tgt_pkgs)The result is a list with three elements:
# packages in source but missing from target
nrow(comp$missing)
#> [1] 47
# packages where source has a newer version
nrow(comp$outdated)
#> [1] 12
# packages at the same version in both
nrow(comp$same)
#> [1] 201Inspect what needs to move:
comp$missing[, c("package", "version", "source")]
#> package version source
#> 1: bookdown 0.39 CRAN
#> 2: brms 2.21.0 CRAN
#> 3: officer 0.6.6 CRAN
#> ...
comp$outdated[, c("package", "version_src", "version_tgt", "source")]
#> package version_src version_tgt source
#> 1: ggplot2 3.5.1 3.4.4 CRAN
#> 2: tibble 3.2.1 3.2.0 CRANship()ship() takes a source and target Rscript path, computes
the plan internally, and runs it:
result <- ship(
source_path = routes$rscript_path[2], # old R — package source
target_path = routes$rscript_path[1] # new R — install destination
)By default, ship() only installs missing packages. Pass
upgrade = TRUE to also update packages that are present but
at an older version (mirrors what the Sync tab does):
ship() detects source-specific packages automatically.
CRAN packages are reinstalled from CRAN; GitHub packages become
"owner/repo" pak specs; Bioconductor packages use
"bioc::pkg" specs. No extra configuration needed.
# mixed-source example — all handled automatically
result$plan[, c("package", "source", "pak_spec")]
#> package source pak_spec
#> 1: ggplot2 CRAN ggplot2
#> 2: courieR GitHub lennon-li/courieR
#> 3: DESeq2 Bioconductor bioc::DESeq2library(courieR)
routes <- find_routes()
old_r <- routes$rscript_path[!routes$is_current][1]
new_r <- routes$rscript_path[routes$is_current]
# dry run
ship(source_path = old_r, target_path = new_r, dry_run = TRUE, upgrade = TRUE)
# for real
result <- ship(source_path = old_r, target_path = new_r, upgrade = TRUE)
result$summarypkgs <- manifest(rscript_path = routes$rscript_path[1])
write.csv(pkgs[, c("package", "version", "source")], "my_packages.csv", row.names = FALSE)Restore from a saved manifest on a new machine:
saved <- read.csv("my_packages.csv", stringsAsFactors = FALSE)
# pak installs from a character vector of package names
pak::pkg_install(saved$package)If you prefer point-and-click, open_hub() launches a
Shiny dashboard that wraps the same pipeline. Install the dashboard
dependencies first if you haven’t already:
The Sync tab mirrors the ship() CLI workflow. The
Advanced tab exposes manifest() output and lets you inspect
any detected R installation’s full package list.
ship() uses pak under the hood, which
resolves dependencies automaticallyGITHUB_PAT to be setresult$results — the
message column has the pak errorfind_routes() may only detect the
R on $PATH; pass additional paths via
search_paths if needed