Skip to content

Instantly share code, notes, and snippets.

@briatte
Created January 22, 2019 06:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briatte/14e47fb0cfb8801f25c889edea3fcd9b to your computer and use it in GitHub Desktop.
Save briatte/14e47fb0cfb8801f25c889edea3fcd9b to your computer and use it in GitHub Desktop.
# ==============================================================================
# SESSION_INFO
#
# A script to deal with package dependencies that will
#
# - detach all packages except base ones
# - install its own package dependencies
# - look for session_info.txt and parse it for packages
# - ensure the packages are installed and up to date
#
# Input:
#
# - a session_info.txt file must be present in the working directory
# - it should be formatted like the results of sessioninfo::session_info()
#
# Output:
#
# - packages listed in session_info.txt will be installed or updated if needed
#
# Limitations:
#
# - works only on CRAN and GitHub packages
#
# ==============================================================================
cran_link <- "https://cran.rstudio.com/"
installed <- utils::installed.packages()[, c("Version") ]
# ==============================================================================
# DETACH ALL NON-BASE PACKAGES
#
# Adapted from code by Romain François [1] and Zach Burchill [2]
# [1]: https://github.com/romainfrancois/nothing
# [2]: https://github.com/burchill/nothing
#
# ==============================================================================
suppressMessages(
repeat{
p <- setdiff(loadedNamespaces(), sessionInfo()$basePkgs)
if (!length(p)) break
for (i in p) {
try(unloadNamespace(i), silent = TRUE)
}
}
)
# ==============================================================================
# INSTALL DEPENDENCIES FROM CRAN
#
# - remotes lighter version of devtools::install_*
# needed to run this script ................................ yes
#
# - sessioninfo more complete version of utils::sessionInfo
# needed to run this script ................................ no
#
# ==============================================================================
for (i in c("remotes", "sessioninfo")) {
if (!(i %in% names(installed))) {
install.packages(i, repos = cran_link)
}
}
# ==============================================================================
# PARSE SESSION INFO
# ==============================================================================
f <- c("session_info.txt")
if (file.exists(f)) {
f <- readLines(f)
p <- f[ grep("package(.*)version(.*)source", f) ]
stopifnot(length(p) == 1)
find_column <- function(pattern, x) {
x <- regexpr(paste0(pattern, "\\s+"), x)
c(x, x + attr(x, "match.length") - 1)
}
find_packages <- find_column("package", p)
find_versions <- find_column("version", p)
find_sources <- find_column("source", p)
# trim beginning
f <- f[ which(f == p):length(f) ]
# trim end
f <- f[ 2:(grep("^$", f) - 1) ]
find_string <- function(x, positions) {
x <- substr(x, start = positions[1], stop = positions[2])
gsub("(.*?)\\s+$", "\\1", x)
}
f <- data.frame(
package = vapply(f, find_string, character(1), find_packages),
version = vapply(f, find_string, character(1), find_versions),
source = vapply(f, find_string, character(1), find_sources),
stringsAsFactors = FALSE,
row.names = NULL
)
# ============================================================================
# FIND IF PACKAGES NEED TO BE INSTALLED OR UPDATED
# ============================================================================
# is package missing? if so, flag it for installation
f$install <- !f$package %in% names(installed)
# if not, get package version
f$update <- 0L
f$update[ !f$install ] <- vapply(f$package[ !f$install ], function(x) {
as.character(packageVersion(x))
}, character(1))
# now, is it at least as recent as the one from session_info.txt?
f$update[ !f$install ] <- mapply(
compareVersion,
f$version[ !f$install ],
f$update[ !f$install ]
)
# if not, flag it for installation
f$install[ !f$install ] <- f$update[ !f$install ] > 0
rm(find_column, find_string, find_packages, find_versions, find_sources)
# ============================================================================
# INSTALL WHATEVER PACKAGES NEED TO BE
# ============================================================================
p <- which(f$install & grepl("^(CRAN|[Gg]it[Hh]ub)", f$source))
if (length(p) > 0) {
message(
"Installing or updating ", length(p),
" package(s) out of ", nrow(f), " checked"
)
for (i in p) {
if(grepl("^CRAN", f$source[i])) {
remotes::install_cran(f$package[i], repos = cran_link, upgrade = TRUE)
}
else if (grepl("^[Gg]it[Hh]ub", f$source[i])) {
i <- gsub("[Gg]it[Hh]ub\\s\\(|@(.*)$", "", f$source[i])
remotes::install_github(i, upgrade = TRUE)
}
else {
message("Skipping package ", i, " (unsupported remote source)")
}
}
} else {
message(
"All ", nrow(f), " package(s) from session_info.txt are installed ",
"and up to date."
)
}
if (any(f$update < 0)) {
p <- f$package[ f$update < 0 ]
warning(
length(p), " package(s) in session_info.txt is/are older ",
"than those installed:\n ", paste0(p, collapse = ", ")
)
}
} else {
warning(
"No session_info.txt found in current working directory.\n ",
"No checks performed."
)
}
rm(i, f, p)
rm(cran_link, installed)
gc(verbose = FALSE)
if (!interactive())
q("no")
# kthxbye
# ==============================================================================
# SESSION_SAVE
#
# Saves session information to session_info.txt
# Requires the sessioninfo package
#
# ==============================================================================
sink("session_info.txt", append = FALSE)
# print() needed to work when used with 'Source on Save' in RStudio
# without it, sink() will save an empty file
print(sessioninfo::session_info())
sink()
if (!interactive())
q("no")
# kthxbye
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment