|
# ============================================================================== |
|
# 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 |