---
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, keep two versions in parity, or selectively push packages in either direction — all from the R console or from a point-and-click dashboard.

## Installation

Install courieR from CRAN:

```{r}
install.packages("courieR")
```

The core CLI workflow (`find_routes()`, `manifest()`, `inventory()`, `ship()`) has no Shiny dependency. The dashboard (`open_hub()`) requires additional packages:

```{r}
install.packages(c("shiny", "bslib", "bsicons", "DT"))
```

## 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 included in the raw manifest. Filter them out before comparison:

```{r}
user_pkgs <- src_pkgs[is.na(src_pkgs$priority) | !(src_pkgs$priority %in% c("base", "recommended")), ]
```

### 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_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   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 Sync tab does):

```{r}
result <- ship(
  source_path = routes$rscript_path[2],
  target_path = routes$rscript_path[1],
  upgrade = TRUE
)
```

#### Check results

```{r}
# summary counts
result$summary
#> $installed   47
#> $upgraded    12
#> $failed       1
#> $skipped      0

# per-package results
result$results[result$results$status == "error", c("package", "message")]
#>    package                         message
#> 1:    rJava  installation of rJava failed...
```

#### 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
```

---

## Common Recipes

### One-way migration (old R → new R)

```{r}
library(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$summary
```

### Two-way sync (keep two R versions in parity)

```{r}
r_a <- routes$rscript_path[1]
r_b <- routes$rscript_path[2]

# push everything A has to B
ship(source_path = r_a, target_path = r_b, upgrade = TRUE)

# push everything B has to 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

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:

```{r}
install.packages(c("shiny", "bslib", "bsicons", "DT"))
open_hub()
```

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.

---

## Tips

- courieR skips base and recommended packages — only user-installed packages are migrated
- `ship()` uses `pak` under the hood, which resolves dependencies automatically
- GitHub packages require the source repository to be public, or a `GITHUB_PAT` to be set
- If a package fails, check `result$results` — the `message` column has the pak error
- On Linux without rig, `find_routes()` may only detect the R on `$PATH`; pass additional paths via `search_paths` if needed
