Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
* dev:
  Use package caching on Travis
  Create folder recursively
  Try once more #233
  Explicitly save manifest.R #233
  Try writing to getwd()
  Use dirname(tempdir()) to persist .checkpoint folder #233
  Rebuild vignettes #233
  Add vignette on using checkpoint inside a markdown file #216
  Add argument documentation
  Add experimental unCheckpoint() function #124
  Skip all online checks if `scanForPackages = FALSE` #234
  Expand help for mranUrl() #237
  Documentation improvements and fixes
  Track snapshot use and remove unused snapshots #209
  • Loading branch information
andrie committed Mar 30, 2017
2 parents 2a0f3a5 + 64a0d8e commit eee7ee4
Show file tree
Hide file tree
Showing 26 changed files with 564 additions and 145 deletions.
10 changes: 3 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
language: r
cache: packages
sudo: false

r:
- release
- devel

sudo: required

env:
- NOT_CRAN="true"
- NOT_CRAN="false"

r_binary_packages:
- testthat
- knitr
- rmarkdown

branches:
only:
- master
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
export(checkpoint)
export(checkpointArchives)
export(checkpointRemove)
export(getAccessDate)
export(getValidSnapshots)
export(mranUrl)
export(setSnapshot)
export(unCheckpoint)
importFrom(utils,Stangle)
importFrom(utils,available.packages)
importFrom(utils,capture.output)
Expand Down
38 changes: 38 additions & 0 deletions R/access_date.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Write a small file into the snapshot that contains the last date this snapshot was accessed by checkpoint()
writeAccessDate <- function(snapshotDate, checkpointLocation = "~/"){
today <- strftime(Sys.Date(), "%Y-%m-%d", tz = FALSE)
if(missing(snapshotDate) || is.null(snapshotDate)){
snapshotDate <- today
}
cpdir <- checkpointPath(snapshotDate,
checkpointLocation = checkpointLocation,
type = "snapshot")
lastaccessFile <- file.path(cpdir, ".lastaccessed")
writeLines(today, con = lastaccessFile)
}


#' Returns the date the snapshot was last accessed.
#'
#' The [checkpoint()] function stores a marker in the snapshot folder every time the function gets called. This marker contains the system date, thus indicating the the last time the snapshot was accessed.
#'
#' @inheritParams checkpoint
#' @return Named character with last access date
#' @export
#' @family checkpoint functions
#' @seealso [checkpointRemove()]
getAccessDate <- function(checkpointLocation = "~/"){
cp <- checkpointPath(NULL,
checkpointLocation = checkpointLocation,
type = "root"
)
z <- dir(cp, pattern = ".{4}-.{2}-.{2}",
include.dirs = TRUE,
full.names = TRUE
)
sapply(z, function(x){
laf <- file.path(x, ".lastaccessed")
if(file.exists(laf)) readLines(laf) else NA
})
}

19 changes: 12 additions & 7 deletions R/checkpoint-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
#'
#' To create the snapshot archives, once a day (at midnight UTC) we refresh the Austria CRAN mirror, on the checkpoint server (https://mran.microsoft.com/). Immediately after completion of the `rsync`` mirror process, we take a snapshot, thus creating the archive. Snapshot archives exist starting from 2014-09-17.
#'
#' checkpoint exposes functions for:
#'
#' * [checkpoint()]: Configures R session to use packages as they existed on CRAN at time of snapshot.
#' * [checkpointArchives()]: List checkpoint archives on disk.
#' * [checkpointRemove()]: Remove checkpoint archive from disk.
#' * [setSnapshot()]: Set default CRAN repository to MRAN snapshot date.
#' * [getValidSnapshots()]: Read list of available snapshot dates from MRAN.
#' `checkpoint` exposes functions for:
#'
#' * Creating and using snapshots:
#' * [checkpoint()]: Configures R session to use packages as they existed on CRAN at time of snapshot.
#' * [setSnapshot()]: Set default CRAN repository to MRAN snapshot date.
#' * [getValidSnapshots()]: Read list of available snapshot dates from MRAN.
#' * Managing local archives:
#' * [checkpointArchives()]: List checkpoint archives on disk.
#' * [checkpointRemove()]: Remove checkpoint archive from disk.
#' * [getAccessDate()]: Returns the date the snapshot was last accessed.
#'
#'
#'
#' @name checkpoint-package
#' @docType package
Expand Down
41 changes: 31 additions & 10 deletions R/checkpoint.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
#' * Scan your project folder for all required packages and install them from the snapshot using [utils::install.packages()]
#'
#' @section Resetting the checkpoint:
#'
#' To reset the checkpoint, simply restart your R session.
#'
#' You can also use the experimental function [unCheckpoint()]
#'
#' @section Changing the default MRAN url:
#'
#' `checkpoint` uses https by default to download packages (see \url{https://www.r-consortium.org/blog/2015/08/17/best-practices-for-using-r-securely}).
#' `checkpoint` Defaults to \url{https://mran.microsoft.com/snapshot} by default in R versions 3.2.0 and later, if https support is enabled.
#' By default, `checkpoint()` uses https to download packages (see \url{https://www.r-consortium.org/blog/2015/08/17/best-practices-for-using-r-securely}). The default MRAN snapshot defaults to \url{https://mran.microsoft.com/snapshot} in R versions 3.2.0 and later, if https support is enabled.
#'
#' You can modify the default URL. To change the URL, use `options(checkpoint.mranUrl = ...)`
#' You can modify the default URL. To change the URL, use `options(checkpoint.mranUrl = ...)`.
#'
#' @section Log file:
#'
Expand All @@ -32,12 +34,16 @@
#' * `snapshotDate`
#' * `pkg`
#' * `bytes`
#'
#' @section Last accessed date:
#'
#' The [checkpoint()] function stores a marker in the snapshot folder every time the function gets called. This marker contains the system date, thus indicating the the last time the snapshot was accessed. See also [getAccessDate()]. To remove snapshots that have not been used since a given date, use [checkpointRemove()]
#'
#' @param snapshotDate Date of snapshot to use in `YYYY-MM-DD` format,e.g. `"2014-09-17"`. Specify a date on or after `"2014-09-17"`. MRAN takes one snapshot per day.
#' @param snapshotDate Date of snapshot to use in `YYYY-MM-DD` format, e.g. `"2014-09-17"`. Specify a date on or after `"2014-09-17"`. MRAN takes one snapshot per day. To list all valid snapshot dates on MRAN use [getValidSnapshots()]
#'
#' @param project A project path. This is the path to the root of the project that references the packages to be installed from the MRAN snapshot for the date specified for `snapshotDate`. Defaults to current working directory using [getwd()].
#'
#' @param R.version Optional character string, e.g. "3.1.2". If specified, compares the current [R.version] to the specified R.version. If these differ, stops processing with an error, making no changes to the system. Specifically, if the check fails, the library path is NOT modified. This argument allows the original script author to specify a specific version of R to obtain the desired results.
#' @param R.version Optional character string, e.g. `"3.1.2"`. If specified, compares the current [R.version] to the specified R.version. If these differ, stops processing with an error, making no changes to the system. Specifically, if the check fails, the library path is NOT modified. This argument allows the original script author to specify a specific version of R to obtain the desired results.
#'
#' @param scanForPackages If `TRUE`, scans for packages in project folder (see details). If FALSE, skips the scanning process. A use case for `scanForPackages = FALSE` is to skip the scanning and installation process, e.g. in production environments with a large number of R scripts in the project. Only set `scanForPackages = FALSE` if you are certain that all package dependencies are already in the checkpoint folder.
#'
Expand All @@ -54,7 +60,6 @@
#' @param forceInstall If `TRUE`, forces the re-installation of all discovered packages and their dependencies. This is useful if, for some reason, the checkpoint archive becomes corrupted.
#'
#' @param forceProject If `TRUE`, forces the checkpoint process, even if the provided project folder doesn't look like an R project. A commonly reported user problem is that they accidentally trigger the checkpoint process from their home folder, resulting in scanning many R files and downloading many packages. To prevent this, we use a heuristic to determine if the project folder looks like an R project. If the project folder is the home folder, and also contains no R files, then `checkpoint()` asks for confirmation to continue.

#'
#' @return Checkpoint is called for its side-effects (see the details section), but invisibly returns a list with elements:
#' * `files_not_scanned`
Expand Down Expand Up @@ -84,7 +89,12 @@ checkpoint <- function(snapshotDate, project = getwd(),

if(interactive()) validateProjectFolder(project)

stopIfInvalidDate(snapshotDate)
stopIfInvalidDate(snapshotDate, online = scanForPackages)
if(!scanForPackages){
mssg(verbose, "Skipping package scanning")
if(!snapshotDate %in% localSnapshots(checkpointLocation = checkpointLocation))
stop("Local snapshot location does not exist")
}

if(!missing("R.version") && !is.null(R.version)){
if(!correctR(as.character(R.version))){
Expand All @@ -109,9 +119,8 @@ checkpoint <- function(snapshotDate, project = getwd(),


mran <- mranUrl()
snapshoturl <- getSnapshotUrl(snapshotDate = snapshotDate)


snapshoturl <- getSnapshotUrl(snapshotDate = snapshotDate, online = scanForPackages)

compiler.path <- system.file(package = "compiler", lib.loc = .Library[1])

libPath <- checkpointPath(snapshotDate, type = "lib",
Expand Down Expand Up @@ -257,6 +266,18 @@ setLibPaths <- function(checkpointLocation, libPath){
envir = environment(.libPaths))
}

#' Undo the effect of checkpoint by resetting .libPath to user library location.
#'
#' This is an experimental solution to the situation where a user no longer wants to work in the checkpointed environment. The function resets [.libPaths] to the environment variable `R_Libs_User`.
#'
#' @param new The new user library location. Defaults to `Sys.getenv("R_Libs_User")`
#'
#' @export
#' @family checkpoint functions
unCheckpoint <- function(new = Sys.getenv("R_Libs_User")){
assign(".lib.loc", new,
envir = environment(.libPaths))
}



Expand Down
5 changes: 5 additions & 0 deletions R/checkpoint_paths.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
localSnapshots <- function(checkpointLocation = "~/"){
cp <- checkpointPath(snapshotDate = "", checkpointLocation = checkpointLocation, type = "snapshot")
ptn <- "\\d{4}-\\d{2}-\\d{2}"
dir(cp, pattern = ptn)
}

checkpointPath <- function(snapshotDate, checkpointLocation,
type = c("lib", "src", "snapshot", "root", "base")){
Expand Down
46 changes: 39 additions & 7 deletions R/checkpoint_remove.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,53 @@ checkpointArchives <- function(checkpointLocation = "~/"){

#' Remove checkpoint archive from disk.
#'
#' This function enables you to delete a snapshot archive folder from disk, thus releasing storage space.
#'
#' @inheritParams checkpoint
#' @param allSinceSnapshot If `TRUE`, removes all snapshot archives since the `snapshotDate`
#' @param allUntilSnapshot If `TRUE`, removes all snapshot archives before the `snapshotDate`
#' @param notUsedSince If `TRUE`, removes all snapshot archives that have not been accessed since the `snapshotDate`. See [getAccessDate()]
#' @export
#' @family checkpoint functions
#' @seealso [getAccessDate()]
#' @example inst/examples/example_remove.R
checkpointRemove <- function(snapshotDate, checkpointLocation = "~/"){
z <- list.files(path = paste0(normalizePath(checkpointLocation), ".checkpoint"),
pattern = paste0(snapshotDate, "$"),
full.names = TRUE)
to_delete <- normalizePath(z, winslash = "/")
if(length(to_delete) == 0) {
warning("archive not found")
checkpointRemove <- function(snapshotDate, checkpointLocation = "~/",
allSinceSnapshot = FALSE,
allUntilSnapshot = FALSE,
notUsedSince = FALSE){
if(!missing(snapshotDate) && !is.null(snapshotDate)){
to_delete <- checkpointPath(snapshotDate, checkpointLocation,
type = "snapshot")
}
if(allSinceSnapshot){
archives <- checkpointArchives(checkpointLocation = checkpointLocation)
archiveDates <- basename(archives)
to_delete <- checkpointPath(archiveDates[archiveDates >= snapshotDate],
checkpointLocation, type = "snapshot")

}
if(allUntilSnapshot){
archives <- checkpointArchives(checkpointLocation = checkpointLocation)
archiveDates <- basename(archives)
to_delete <- checkpointPath(archiveDates[archiveDates <= snapshotDate],
checkpointLocation, type = "snapshot")

}
if(notUsedSince){
archiveDates <- getAccessDate(checkpointLocation = checkpointLocation)
archiveDates <- archiveDates[!is.na(archiveDates)]
archiveDates <- archiveDates[archiveDates >= snapshotDate]
to_delete <- checkpointPath(basename(archiveDates),
checkpointLocation, type = "snapshot")

}
if(length(to_delete) ==0 || !dir.exists(to_delete)) {
message("no archives removed")
invisible(NULL)
} else {
res <- unlink(to_delete, recursive = TRUE)
if(res == 0) message("successfully removed archive")
invisible(res)
}
}

Expand Down
18 changes: 14 additions & 4 deletions R/mranUrl.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

stopIfInvalidDate <- function(snapshotDate, verbose = TRUE){
stopIfInvalidDate <- function(snapshotDate, verbose = TRUE, online = TRUE){
if(missing(snapshotDate) || is.null(snapshotDate))
stop("You have to specify a snapshotDate", call. = FALSE)
if(!grepl("^\\d{4}-\\d{2}-\\d{2}$", snapshotDate))
Expand All @@ -10,6 +10,7 @@ stopIfInvalidDate <- function(snapshotDate, verbose = TRUE){
if(as.Date(snapshotDate) > Sys.Date())
stop("snapshotDate can not be in the future!", call. = FALSE)

if(!online) return()
validSnapshots <- tryCatch(as.Date(getValidSnapshots()), error=function(e)e)
if(inherits(validSnapshots, "error")){
mssg(verbose, "Unable to connect to MRAN. Skipping some date validations.")
Expand Down Expand Up @@ -49,12 +50,19 @@ isHttpsUrl <- function(url){

#' Returns MRAN URL by querying options and defaults.
#'
#' The default MRAN URL is `http(s)://mran.microsoft.com/`, but you can override this by setting the `checkpoint.mranUrl` option.
#' This function returns the current MRAN URL. The default for this is `http(s)://mran.microsoft.com/`, and is defined by setting the `checkpoint.mranUrl` option.
#'
#' @section Defining a new MRAN URL:
#'
#' To force [checkpoint()] to point to a differt URL, you can set the `checkpoint.mranUrl` option.
#'
#' `options(checkpoint.mranUrl = "new_url")`
#'
#'
#' @export
#' @return Character string with URL
#' @family checkpoint functions
#' @example inst/examples/example_mranurl.R
mranUrl <- function(){
url <- getOption("checkpoint.mranUrl")
url <- if(is.null(url)) mranUrlDefault() else url
Expand Down Expand Up @@ -158,13 +166,15 @@ is.404 <- function(mran, warn = TRUE){
}
}

getSnapshotUrl <- function(snapshotDate, mranRootUrl = mranUrl()){
getSnapshotUrl <- function(snapshotDate, mranRootUrl = mranUrl(), online = TRUE){

snapshot.url = paste(gsub("/$", "", mranRootUrl), snapshotDate, sep = "/")
if(!online) return(snapshot.url)
if(is.404(mranRootUrl)){
warning("Unable to reach MRAN root at ", mranRootUrl, call. = FALSE)
return(snapshot.url)
}

snapshot.url = paste(gsub("/$", "", mranRootUrl), snapshotDate, sep = "/")
if(is.404(snapshot.url)){
warning("Unable to find snapshot on MRAN at ", snapshot.url, call. = FALSE)
}
Expand Down
5 changes: 3 additions & 2 deletions R/setSnapshot.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
#' Set default CRAN repository to MRAN snapshot date.
#'
#' @inheritParams checkpoint
#' @param online If TRUE, performs online validation checks. This can be set to FALSE for programming purposes. Internally, [checkpoint()] sets this value to FALSE when not scanning for packages.
#'
#' @export
#' @example /inst/examples/example_setSnapshot.R
#'
#' @family checkpoint functions
#'
setSnapshot <- function(snapshotDate){
setSnapshot <- function(snapshotDate, online = TRUE){
if (missing(snapshotDate) || is.null(snapshotDate)) return(getOption("repos"))
mran <- mranUrl()
repoDate <- paste0(mran, snapshotDate)

if(is.404(repoDate)) stop(paste0("Invalid snapshot date."))
if(online) if(is.404(repoDate)) stop(paste0("Invalid snapshot date."))
options(repos = c(CRAN = repoDate))
message(paste("Using CRAN mirror at", repoDate))
}
Expand Down
16 changes: 16 additions & 0 deletions inst/examples/example_mranurl.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mranUrl()

\dontrun{

# Store the existing options
old_opts <- getOption("checkpoint.mranUrl")

# Set MRAN URL to different http address
options(checkpoint.mranUrl = "https://foobah")

# Set MRAN URL to local file address
options(checkpoint.mranUrl = "file:///~")

# Reset the original options
options(checkpoint.mranUrl = old_opts)
}
13 changes: 10 additions & 3 deletions man/checkpoint-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit eee7ee4

Please sign in to comment.