--- title: "Getting Started with courieR" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting Started with courieR} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE) ``` courieR syncs installed R packages between R versions on the same machine. You can migrate from an old version to a new one, or selectively push packages from a source installation into a target installation — all from the R console or from a point-and-click dashboard. ## Installation Install courieR from GitHub (CRAN submission in progress): ```{r} pak::pkg_install("lennon-li/courieR") ``` All dependencies — including the dashboard packages — are installed automatically. ## Step 1 — Discover Your R Installations `find_routes()` scans the system and returns a data frame of every R version it can find: ```{r} 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/... FALSE ``` `is_current = TRUE` marks the R session you are running right now. ### Platform detection | 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: ```{r} routes <- find_routes(search_paths = "/opt/custom-r/bin/Rscript") ``` --- ## CLI Workflow The four core functions form a pipeline: ``` find_routes() → manifest() → inventory() → ship() discover scan compare migrate ``` ### Step 2 — Scan a library with `manifest()` `manifest()` runs a subprocess under a given Rscript and returns every installed package with its version and source: ```{r} # 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 CRAN ``` Calling `manifest()` with no arguments scans the library of the current R session: ```{r} my_pkgs <- manifest() nrow(my_pkgs) #> [1] 312 ``` Base and recommended packages are excluded automatically — the returned data contains only user-installed packages. ### Step 3 — Compare two libraries with `inventory()` `inventory()` takes two manifests and returns a classified diff: ```{r} 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: ```{r} # 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] 201 ``` Inspect what needs to move: ```{r} 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.x", "version.y", "source")] #> package version.x version.y source #> 1: ggplot2 3.5.1 3.4.4 CRAN #> 2: tibble 3.2.1 3.2.0 CRAN ``` ### Step 4 — Migrate with `ship()` `ship()` takes a source and target Rscript path, computes the plan internally, and runs it: ```{r} result <- ship( source_path = routes$rscript_path[2], # old R — package source target_path = routes$rscript_path[1] # new R — install destination ) ``` #### Always dry-run first ```{r} result <- ship( source_path = routes$rscript_path[2], target_path = routes$rscript_path[1], dry_run = TRUE ) # review the plan before anything is installed print(result$plan) #> package action pak_spec #> 1: bookdown install bookdown #> 2: brms install brms #> 3: ggplot2 upgrade ggplot2 #> ... ``` #### Include version upgrades 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 Bulk Dispatch tab does): ```{r} result <- ship( source_path = routes$rscript_path[2], target_path = routes$rscript_path[1], upgrade = TRUE ) ``` #### Check results ```{r} # per-package outcomes result$results #> package status message #> 1: bookdown success Installed #> 2: brms success Installed #> 3: rJava error installation of rJava failed... # count by outcome table(result$results$status) #> error success #> 1 58 # failed packages only result$results[result$results$status == "error", ] ``` #### Migrate GitHub and Bioconductor packages `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. ```{r} # 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::DESeq2 ``` --- ## Transfer modes `ship()` (and the dashboard's **Transfer mode** selector) offers three mutually-exclusive ways to move packages. Pass one via `mode =`: | Mode | What it does | When to use | |------|--------------|-------------| | `"online"` (default) | Reinstall each package fresh via pak — latest compatible version, dependencies resolved. Pure-R and local packages are *copied* directly (no rebuild); only compiled packages are reinstalled, preferring pre-built binaries. | Moving between **different** R versions, where compiled packages must be rebuilt for the new R. | | `"offline"` | Copy package files directly from the source library — fast, no internet, exact same versions. | Same R **x.y** series, or air-gapped machines. | | `"preserve"` | Same direct copy as `offline`, but anything that can't be copied is reinstalled via pak at its exact source version. | Same series, when you want a network safety net. | ### Why copy modes need the same R x.y series A package's **compiled code** (the `.dll`/`.so` files under `libs/`) is built against a specific R **minor** version's binary interface. Packages compiled for R 4.5.x will not reliably load under R 4.6 — they must be rebuilt. So: * **Within the same series** (e.g. 4.5.0 ↔ 4.5.2) copying is binary-safe, and `offline`/`preserve` are the fast choice. * **Across minor versions** (e.g. 4.5 → 4.6) compiled packages must be reinstalled, so only `online` is safe. The dashboard hides the copy modes for such pairs automatically. Pure-R packages (no `libs/`) are version-independent, so `online` copies those directly even across versions and only rebuilds the compiled minority. ```{r} # explicit mode ship(source_path = old_r, target_path = new_r, mode = "offline") ``` --- ## Common Recipes ### One-way migration (old R → new R) The shortest path uses `migrate()`, which looks up installations by version string so you don't need to handle paths yourself: ```{r} library(courieR) # 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) ``` For more control — filtering by package, custom paths, or progress callbacks — use `ship()` directly: ```{r} routes <- find_routes() old_r <- routes$rscript_path[!routes$is_current][1] new_r <- routes$rscript_path[routes$is_current] result <- ship(source_path = old_r, target_path = new_r, upgrade = TRUE) ``` ### Mirror two same-version installs `ship()` always moves packages one way, from `source_path` into `target_path`. To bring two same-version installations into line with each other, run it twice, swapping the roles: ```{r} r_a <- routes$rscript_path[1] r_b <- routes$rscript_path[2] # push everything A has into B ship(source_path = r_a, target_path = r_b, upgrade = TRUE) # then push everything B has into A ship(source_path = r_b, target_path = r_a, upgrade = TRUE) ``` ### Inspect before migrating ```{r} src <- manifest(rscript_path = old_r) tgt <- manifest(rscript_path = new_r) comp <- inventory(src, tgt) # only migrate CRAN packages — skip GitHub/unknown sources cran_missing <- comp$missing[comp$missing$source == "CRAN", ] cat(nrow(cran_missing), "CRAN packages to install\n") ``` ### Save a manifest to disk ```{r} pkgs <- 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: ```{r} saved <- read.csv("my_packages.csv", stringsAsFactors = FALSE) # pak installs from a character vector of package names pak::pkg_install(saved$package) ``` --- ## Dashboard `hub()` launches a Shiny dashboard that wraps the same pipeline: ```{r} library(courieR) hub() ``` ```{r sync-screenshot, echo=FALSE, out.width="100%", fig.cap="The Bulk Dispatch tab: pick a source and a target R installation, compare their libraries, and ship packages from source into target. The log pane on the right shows real-time progress."} img <- "figures/sync-tab.png" if (file.exists(img)) knitr::include_graphics(img) ``` **Workflow:** 1. The dashboard scans all R installations on the machine automatically — no configuration needed. 2. Select a **source** and a **target** installation from the sidebar dropdowns. The target list is constrained to the same-or-newer R version than the source, since an older R can't reliably hold packages built for a newer one. 3. Click **Compare** — a summary strip shows counts of identical, missing, and version-mismatched packages. The table lists differences first. 4. Click **Ship** to install or upgrade the source's packages into the target. To mirror two same-version installs, run the transfer again with source and target swapped. 5. A confirmation dialog shows the package count and an estimated time. Confirm to start. 6. The log pane shows real-time progress: each package, its pak spec, success or failure, and a post-sync comparison refresh. The first sync on a machine can take 1–2 minutes while pak builds its metadata cache. Subsequent syncs are faster. The **Browse** and **Manifest** tabs expose `manifest()` output and let you inspect any detected R installation's full package list. --- ## Tips - `hub()` is a short alias for `hub()` — both launch the dashboard - courieR skips base and recommended packages automatically — only user-installed packages are compared and migrated - `ship()` uses `pak` under the hood, which resolves dependencies automatically - Use `mode = "offline"` when you have no internet access — courieR copies package directories directly. Packages without a local source path are reported as skipped - Use `mode = "preserve"` to keep exact version numbers; courieR copies where possible and pins the version in the pak spec otherwise - The source R installation does not need any extra packages; only the target R needs `pak` installed, because installation runs under the target R process - GitHub packages require the source repository to be public, or a `GITHUB_PAT` to be set in the environment - If a package fails, check `result$results` — the `message` column has the pak error - Use `dry_run = TRUE` before any large migration to review the plan without installing anything - On Linux without rig, `find_routes()` may only detect the R on `$PATH`; pass additional paths via `search_paths` if needed