---
title: "Using miniCRAN to create and maintain a local CRAN repository"
author: "Andrie de Vries and Alex Chubaty"
date:   "`r as.character(format(Sys.Date(), format='%B %d, %Y'))`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Using miniCRAN to create and maintain a local CRAN repository}
  %\VignettePackage{miniCRAN}
  %\VignetteKeyword{miniCRAN}
  %\usepackage[utf8]{inputenc}
  %\VignetteEngine{knitr::rmarkdown}
editor_options: 
  chunk_output_type: console
---

Start by creating the recursive dependency tree for your target packages.

For example, imagine a scenario where you want to create a repository that consists of the package `foreach` and its dependencies.
 
Start by creating the dependency list:

```{r make-repo-1}
library("miniCRAN")

# define CRAN mirror
mirror <- c(CRAN = "https://cloud.r-project.org")

# Specify list of packages to download
pkgs <- c("foreach")
pkgList <- pkgDep(pkgs, repos = mirror, type = "source", suggests = FALSE, 
                  availPkgs = cranJuly2014)
pkgList
```

Next, create a repository with the function `makeRepo()`.  In this example, get the required files for `source` packages as well as windows binaries:

```{r make-repo-2, eval=FALSE}
# Create temporary folder for miniCRAN
dir.create(pth <- file.path(tempdir(), "miniCRAN"))

# Make repo for source and win.binary
makeRepo(pkgList, path = pth, repos = mirror, type = c("source", "win.binary"))
```


Investigate the repository file structure:

```{r make-repo-3, eval=FALSE}
# List all files in miniCRAN
list.files(pth, recursive = TRUE, full.names = FALSE)
```


Use `pkgAvail` to list available packages in your repository:

```{r make-repo-4, eval=FALSE}
# Check for available packages
pkgAvail(repos = pth, type = "win.binary")[, c(1:3, 5)]
```

# Install packages from your local repository

To install packages from a local repository, you need to use the [Uniform Resource Identifier](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) (URI) convention `file:///` to point to your file location.

```{r make-repo-5, eval=FALSE}
install.packages(pkgs, 
                 repos = paste0("file:///", pth),
                 type = "source")
```


# Adding packages to an existing miniCRAN repository

## Adding new packages from CRAN

After creating a local miniCRAN repository, additional packages and their dependencies can easily be added. This mechanism can also be used to re-add an existing package to the miniCRAN repo.

```{r addto-repo-new-1, eval=FALSE}
# Add new packages (from CRAN) to the miniCRAN repo
addPackage("Matrix", path = pth, repos = mirror, type = c("source", "win.binary"))
pkgAvail(repos = pth, type = "win.binary")[, c(1:3, 5)]
```

The value that is returned (invisibly) via `addPackage` is the number of packages written to the index file, *i.e.*, the total number of packages in the repo of that type.

## Adding an older version of a package from CRAN

To add a specific version of a package from CRAN (or another CRAN-like repository), we can easily download the source packages from the CRAN archives. Dependencies for old package versions cannot be determined automatically and must be specified by the user.

*Note:* in order to to add binaries of older packages, you will need to download the source and build the binaries on the intended platform yourself. You will need the appropriate R development tools installed in order to build package binaries from source.

```{r addto-repo-old-1, eval=FALSE}
# create a data frame with the package and version info
oldVers <- data.frame(
  package = c("foreach", "codetools", "iterators"),
  version = c("1.4.0", "0.2-7", "1.0.5"),
  stringsAsFactors = FALSE
)

# download old source package version and create repo index
addOldPackage(pkgList, path = pth, vers = oldVers$version, repos = mirror, type = "source")
```

You will get a warning whenever there are multiple versions of a package saved in the repository. Currently, you need to manually remove duplicate versions before rebuilding the repository's package index.

Note: This last step is important, otherwise you may end up with a repo in an inconsistent state.

```{r addto-repo-old-2, eval=FALSE}
# List package versions in the miniCRAN repo (produces warning about duplicates)
pkgVersionsSrc <- checkVersions(pkgList, path = pth, type = "source")
pkgVersionsBin <- checkVersions(pkgList, path = pth, type = "win.binary")

# After inspecting package versions, remove old versions
basename(pkgVersionsSrc) # duplicate versions
basename(pkgVersionsBin)

file.remove(pkgVersionsSrc[c(2,4,6)])

# rebuild the package index after removing duplicate package versions
updateRepoIndex(pth, type = c("source", "win.binary"))
```

To see the updated list of packages available in the miniCRAN repo:

```{r addto-repo-old-3, eval=FALSE}
pkgAvail(pth, type = "source")[, c(1:3, 5)] # contains the old versions
pkgAvail(pth, type = "win.binary")[, c(1:3, 5)] # contains the current versions
```



## Adding packages from other sources

This feature will be implemented in a future release.


# Updating the packages in a miniCRAN repository

Checking for updated versions of the packages currently stored in the miniCRAN repository:

```{r update-repo-1, eval=FALSE}
# Check if updated packages are available
oldPackages(path = pth, repos = mirror, type = "source")[, 1:3] # should need update
oldPackages(path = pth, repos = mirror, type = "win.binary")[, 1:3] # should be current
```


Update the versions of the packages currently stored in the miniCRAN repository. By default, a prompt is given to confirm the update for each package. This prompt can be suppressed using `ask = FALSE`, which will update all packages. Be careful using this option if you want to keep certain packages at an older version.

```{r update-repo-2, eval=FALSE}
# Update available packages
updatePackages(path = pth, repos = mirror, type = "source", ask = FALSE) # should need update
updatePackages(path = pth, repos = mirror, type = "win.binary", ask = FALSE) # should be current
```




```{r cleanup, include=FALSE, eval=FALSE}
# Delete temporary folder
unlink(pth, recursive = TRUE)
```