Building of a snapshot completed and included
This commit is contained in:
162
R/compile_imports.R
Normal file
162
R/compile_imports.R
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# Requires utils.R
|
||||||
|
# Requires request_package_source.R
|
||||||
|
# Requires list_imports.R
|
||||||
|
# Requires list_depends.R
|
||||||
|
# Requires fetch_available_packages.R
|
||||||
|
# Requires fetch_available_archives.R
|
||||||
|
|
||||||
|
.solve_missing_imports <- function(missingImports, installOpts = list(Ncpus = parallel::detectCores())) {
|
||||||
|
importsFormat <- data.frame(list(
|
||||||
|
package_name = unlist(lapply(missingImports$package, format_str, width = 35)),
|
||||||
|
req_ver = unlist(lapply(missingImports$req_version, format_str, width = 12)),
|
||||||
|
cran_ver = unlist(lapply(missingImports$cran_version, format_str, width = 12))
|
||||||
|
))
|
||||||
|
|
||||||
|
cat("\n")
|
||||||
|
cat("There are", nrow(missingImports), "package versions that could not be found on CRAN:\n")
|
||||||
|
cat("| Package | Req'd Version | CRAN Version |\n")
|
||||||
|
cat("|=====================================|===============|==============|\n")
|
||||||
|
cat(
|
||||||
|
paste("|", importsFormat$package_name, "|", importsFormat$req_ver, " |", importsFormat$cran_ver, "|\n"),
|
||||||
|
sep = ""
|
||||||
|
)
|
||||||
|
cat("\n")
|
||||||
|
cat("What would you like to do?\n")
|
||||||
|
repeat {
|
||||||
|
actions <- select_menu(c("Specify package sources [default]", "Update to latest CRAN version", "Cancel"))
|
||||||
|
if (length(actions) == 1 || length(actions) == nrow(missingImports)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cat("Number of actions must equal 1 or number of missing packages.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("Cancel" %in% actions) {
|
||||||
|
stop_quietly()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("Update to latest CRAN version" %in% actions) {
|
||||||
|
newPackages <- which("Update to latest CRAN version" == actions)
|
||||||
|
newPackages <- unique(missingImports[newPackages,"package"])
|
||||||
|
do.call(install.packages, c(list(newPackages), installOpts))
|
||||||
|
cat("\n")
|
||||||
|
cat("One or more new packages were installed. Please re-run the command.\n")
|
||||||
|
stop_quietly()
|
||||||
|
}
|
||||||
|
|
||||||
|
fixedImports <- list()
|
||||||
|
for (row in 1:nrow(missingImports)) {
|
||||||
|
cat("\n")
|
||||||
|
cat("=========================================\n")
|
||||||
|
cat("Package:", missingImports$package[[row]], "\n")
|
||||||
|
source <- request_package_source(missingImports$package[[row]])
|
||||||
|
|
||||||
|
if (is.null(source)) {
|
||||||
|
# Cancelled
|
||||||
|
return(invisible(NULL))
|
||||||
|
}
|
||||||
|
|
||||||
|
fixedImports <- c(fixedImports, list(c(
|
||||||
|
list(
|
||||||
|
package = missingImports$package[[row]],
|
||||||
|
version = missingImports$req_version[[row]]
|
||||||
|
),
|
||||||
|
source
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
do.call(rbind, fixedImports)
|
||||||
|
}
|
||||||
|
|
||||||
|
#' Compile a structure containing the current state of the imports for the
|
||||||
|
#' project.
|
||||||
|
#'
|
||||||
|
#' @param custom_sources datatable A table containing any previously known
|
||||||
|
#' custom data sources
|
||||||
|
#' @param installOpts list Installer options for use when installing CRAN
|
||||||
|
#' packages
|
||||||
|
#'
|
||||||
|
#' @return A list containing R base imports, directly imported/used packages,
|
||||||
|
#' indirectly imported/used packages (aka dependencies) and any custom
|
||||||
|
#' import sources.
|
||||||
|
#' @export
|
||||||
|
compile_imports <- function(custom_sources = empty_sources(), installOpts = list(Ncpus = parallel::detectCores())) {
|
||||||
|
CORE_PACKAGES <- c(
|
||||||
|
"base", "compiler", "datasets", "graphics", "grDevices", "grid", "methods",
|
||||||
|
"parallel", "splines", "stats", "stats4", "tcltk", "tools", "utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
imports <- list_imports()
|
||||||
|
depends <- list_depends(unique(imports$package))
|
||||||
|
|
||||||
|
cranPackages <- fetch_available_packages()
|
||||||
|
archivePackages <- fetch_available_archives()
|
||||||
|
|
||||||
|
dependsSources <- depends
|
||||||
|
dependsSources <- merge(
|
||||||
|
dependsSources,
|
||||||
|
c(cranPackages, in_CRAN = TRUE),
|
||||||
|
by = c("package", "version"),
|
||||||
|
all.x = TRUE
|
||||||
|
)
|
||||||
|
dependsSources <- merge(
|
||||||
|
dependsSources,
|
||||||
|
c(archivePackages, in_archive = TRUE),
|
||||||
|
by = c("package", "version"),
|
||||||
|
all.x = TRUE
|
||||||
|
)
|
||||||
|
if (nrow(custom_sources) > 0) {
|
||||||
|
dependsSources <- merge(
|
||||||
|
dependsSources,
|
||||||
|
c(custom_sources[,c("package", "version")], in_custom = TRUE),
|
||||||
|
by = c("package", "version"),
|
||||||
|
all.x = TRUE
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
dependsSources$in_custom <- NA
|
||||||
|
}
|
||||||
|
|
||||||
|
dependsSources$is_direct <- dependsSources$package %in% imports$package
|
||||||
|
dependsSources$in_core <- dependsSources$package %in% CORE_PACKAGES
|
||||||
|
dependsSources$in_CRAN <- !is.na(dependsSources$in_CRAN)
|
||||||
|
dependsSources$in_archive <- !is.na(dependsSources$in_archive)
|
||||||
|
dependsSources$in_custom <- !is.na(dependsSources$in_custom)
|
||||||
|
|
||||||
|
missingImports <- !(
|
||||||
|
dependsSources$in_core |
|
||||||
|
dependsSources$in_CRAN |
|
||||||
|
dependsSources$in_archive |
|
||||||
|
dependsSources$in_custom
|
||||||
|
)
|
||||||
|
|
||||||
|
customImports <- merge(
|
||||||
|
dependsSources[dependsSources$in_custom, c("package", "version")],
|
||||||
|
custom_sources,
|
||||||
|
by = c("package", "version")
|
||||||
|
)
|
||||||
|
if (sum(missingImports) > 0) {
|
||||||
|
extraImports <- merge(
|
||||||
|
dependsSources[missingImports,c("package", "version")],
|
||||||
|
cranPackages,
|
||||||
|
by = "package",
|
||||||
|
all.x = TRUE
|
||||||
|
)
|
||||||
|
colnames(extraImports) <- c("package", "req_version", "cran_version")
|
||||||
|
extraImports <- .solve_missing_imports(extraImports, installOpts = installOpts)
|
||||||
|
if (is.null(extraImports)) {
|
||||||
|
# Cancelled
|
||||||
|
return(invisible(NULL))
|
||||||
|
}
|
||||||
|
customImports <- rbind(customImports, extraImports)
|
||||||
|
}
|
||||||
|
|
||||||
|
coreDeps <- dependsSources[dependsSources$in_core,]
|
||||||
|
directDeps <- dependsSources[dependsSources$is_direct & !dependsSources$in_core & !missingImports,]
|
||||||
|
indirectDeps <- dependsSources[!dependsSources$is_direct & !dependsSources$in_core & !missingImports,]
|
||||||
|
|
||||||
|
return(list(
|
||||||
|
core_depends = coreDeps[,c("package", "version")],
|
||||||
|
direct_depends = directDeps[,c("package", "version")],
|
||||||
|
indirect_depends = indirectDeps[,c("package", "version")],
|
||||||
|
custom_sources = customImports
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -1,21 +1,10 @@
|
|||||||
# Requires utils.R
|
# Requires utils.R
|
||||||
|
|
||||||
.fetch_available_archives <- function(refetch) {
|
.process_dist_archive <- function(dist_fn, fn) {
|
||||||
check_pacman_dir()
|
dist_con <- gzfile(dist_fn, "rb")
|
||||||
fn <- ".pacman/archive.rds"
|
on.exit(close(dist_con), add = TRUE)
|
||||||
if (file.exists(fn)) {
|
archive <- readRDS(dist_con)
|
||||||
ts <- as.POSIXct("1970-01-01 00:00:00", tz = "GMT") + as.numeric(Sys.Date() + 1) * 86400
|
archive <- lapply(archive, function(pkg) {
|
||||||
if (refetch || (ts < file.mtime(fn))) {
|
|
||||||
unlink(fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!file.exists(fn)) {
|
|
||||||
download.file(sprintf("%s/src/contrib/Meta/archive.rds", get_cran_repo()), fn)
|
|
||||||
}
|
|
||||||
con <- gzfile(fn, "rb")
|
|
||||||
on.exit(close(con), add = TRUE)
|
|
||||||
archive <- readRDS(con)
|
|
||||||
archive <-lapply(archive, function(pkg) {
|
|
||||||
packageVersions <- gsub("^([^/]+)/[^_]+_(.+).tar.gz", "\\1%%\\2", rownames(pkg))
|
packageVersions <- gsub("^([^/]+)/[^_]+_(.+).tar.gz", "\\1%%\\2", rownames(pkg))
|
||||||
packageVersions <- strsplit(packageVersions, "%%")
|
packageVersions <- strsplit(packageVersions, "%%")
|
||||||
packageVersions <- do.call(rbind, packageVersions)
|
packageVersions <- do.call(rbind, packageVersions)
|
||||||
@@ -25,6 +14,29 @@
|
|||||||
})
|
})
|
||||||
archive <- do.call(rbind, archive)
|
archive <- do.call(rbind, archive)
|
||||||
rownames(archive) <- 1:nrow(archive)
|
rownames(archive) <- 1:nrow(archive)
|
||||||
|
write_con <- gzfile(fn, "wb")
|
||||||
|
saveRDS(archive, write_con)
|
||||||
|
close(write_con)
|
||||||
|
}
|
||||||
|
|
||||||
|
.fetch_available_archives <- function(refetch) {
|
||||||
|
check_pacman_dir()
|
||||||
|
fn <- ".pacman/archive.rds"
|
||||||
|
dist_fn <- ".pacman/archive_dist.rds"
|
||||||
|
if (file.exists(dist_fn)) {
|
||||||
|
ts <- as.POSIXct("1970-01-01 00:00:00", tz = "GMT") + as.numeric(Sys.Date() + 1) * 86400
|
||||||
|
if (refetch || (ts < file.mtime(dist_fn))) {
|
||||||
|
unlink(dist_fn)
|
||||||
|
unlink(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file.exists(dist_fn)) {
|
||||||
|
download.file(sprintf("%s/src/contrib/Meta/archive.rds", get_cran_repo()), dist_fn)
|
||||||
|
.process_dist_archive(dist_fn, fn)
|
||||||
|
}
|
||||||
|
con <- gzfile(fn, "rb")
|
||||||
|
on.exit(close(con), add = TRUE)
|
||||||
|
archive <- readRDS(con)
|
||||||
return(archive)
|
return(archive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
R/install_package_source.R
Normal file
42
R/install_package_source.R
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
#' Installs a package from a custom source
|
||||||
|
#'
|
||||||
|
#' @param packageSource The custom source information for a package
|
||||||
|
#' @param installOpts list Installer options for use when installing CRAN
|
||||||
|
#' packages
|
||||||
|
#'
|
||||||
|
#' @return invisible return of the current snapshot
|
||||||
|
#' @export
|
||||||
|
install_package_source <- function(packageSource, installOpts = list(Ncpus = parallel::detectCores())) {
|
||||||
|
packageRemote <- switch(
|
||||||
|
packageSource$type,
|
||||||
|
git_url = remotes:::git_remote(packageSource$src, ref = packageSource$ref),
|
||||||
|
github = remotes:::github_remote(packageSource$src, ref = packageSource$ref),
|
||||||
|
gitlab = remotes:::gitlab_remote(packageSource$src),
|
||||||
|
svn = remotes:::svn_remote(packageSource$src, revision = packageSource$ref),
|
||||||
|
bitbucket = remotes:::bitbucket_remote(packageSource$src, ref = packageSource$ef),
|
||||||
|
Bioconductor = remotes:::bio_remote(packageSource$src)
|
||||||
|
)
|
||||||
|
|
||||||
|
print(dput(c(list(packageRemote), dependencies = NA, upgrade = "default",
|
||||||
|
force = FALSE, quiet = FALSE, build = TRUE, build_opts = c("--no-resave-data", "--no-manual",
|
||||||
|
"--no-build-vignettes"),
|
||||||
|
build_manual = FALSE, build_vignettes = FALSE,
|
||||||
|
repos = getOption("repos"), type = getOption("pkgType"), installOpts)))
|
||||||
|
|
||||||
|
remotes:::install_remote(packageRemote, dependencies = NA, upgrade = "default",
|
||||||
|
force = FALSE, quiet = FALSE, build = TRUE, build_opts = c("--no-resave-data", "--no-manual",
|
||||||
|
"--no-build-vignettes"),
|
||||||
|
build_manual = FALSE, build_vignettes = FALSE,
|
||||||
|
repos = getOption("repos"), type = getOption("pkgType"), installOpts)
|
||||||
|
|
||||||
|
allPackages <- installed.packages()
|
||||||
|
allPackages <- as.data.frame(allPackages)
|
||||||
|
allPackages <- allPackages[allPackages$Package %in% packageSource$package,]
|
||||||
|
package <- data.frame(
|
||||||
|
package = allPackages$Package,
|
||||||
|
version = allPackages$Version
|
||||||
|
)
|
||||||
|
|
||||||
|
return(package)
|
||||||
|
}
|
||||||
23
R/snapshot.R
Normal file
23
R/snapshot.R
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Requires snapshot_create.R
|
||||||
|
# Requires snapshot_history.R
|
||||||
|
|
||||||
|
#' Save the current state of the project into the lock file as a snapshot
|
||||||
|
#'
|
||||||
|
#' @param installOpts list Installer options for use when installing CRAN
|
||||||
|
#' packages
|
||||||
|
#'
|
||||||
|
#' @return invisible return of the current snapshot
|
||||||
|
#' @export
|
||||||
|
snapshot_save <- function(installOpts = list(Ncpus = parallel::detectCores()), snapshot = NULL) {
|
||||||
|
if (is.null(snapshot)) {
|
||||||
|
snapshot <- snapshot_create(installOpts)
|
||||||
|
}
|
||||||
|
history <- snapshot_history()
|
||||||
|
history <- c(list(snapshot), history)
|
||||||
|
snapshot$history <- history
|
||||||
|
jsonlite::write_json(snapshot, "pacman.lock")
|
||||||
|
snapshot$history <- NULL
|
||||||
|
return(snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
42
R/snapshot_create.R
Normal file
42
R/snapshot_create.R
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Requires snapshot_latest.R
|
||||||
|
# Requires utils.R
|
||||||
|
# Requires compile_imports.R
|
||||||
|
|
||||||
|
#' Create a snapshot of the project packages. Does not save the snapshot (see snapshot_save)
|
||||||
|
#'
|
||||||
|
#' @param installOpts list Installer options for use when installing CRAN
|
||||||
|
#' packages
|
||||||
|
#'
|
||||||
|
#' @return A list containing the current state of the project packages
|
||||||
|
#' @export
|
||||||
|
snapshot_create <- function(installOpts = list(Ncpus = parallel::detectCores())) {
|
||||||
|
lastSnapshot <- snapshot_latest()
|
||||||
|
customSources <- empty_sources()
|
||||||
|
if (!is.null(lastSnapshot) && "custom" %in% names(lastSnapshot$packages)) {
|
||||||
|
customSources <- rbind(customSources, lastSnapshot$packages$custom)
|
||||||
|
}
|
||||||
|
packages <- compile_imports(customSources, installOpts)
|
||||||
|
names(packages) <- c("core", "direct", "indirect", "custom")
|
||||||
|
if (nrow(packages$core) == 0) {
|
||||||
|
packages$core <- NULL
|
||||||
|
}
|
||||||
|
if (nrow(packages$direct) == 0) {
|
||||||
|
packages$direct <- NULL
|
||||||
|
}
|
||||||
|
if (nrow(packages$indirect) == 0) {
|
||||||
|
packages$indirect <- NULL
|
||||||
|
}
|
||||||
|
if (nrow(packages$custom) == 0) {
|
||||||
|
packages$custom <- NULL
|
||||||
|
}
|
||||||
|
description <- trimws(readline("Please provide a description for the snapshot (optional): "))
|
||||||
|
if (nchar(description) == 0) {
|
||||||
|
description <- NULL
|
||||||
|
}
|
||||||
|
return(list(
|
||||||
|
timestamp = Sys.time(),
|
||||||
|
description = description,
|
||||||
|
R_version = paste(R.version$major, R.version$minor, sep = "."),
|
||||||
|
packages = packages
|
||||||
|
))
|
||||||
|
}
|
||||||
12
R/snapshot_history.R
Normal file
12
R/snapshot_history.R
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
#' Load the existing snapshot history of the project
|
||||||
|
#'
|
||||||
|
#' @return A list containing the previous snapshots
|
||||||
|
#' @export
|
||||||
|
snapshot_history <- function() {
|
||||||
|
if (!file.exists("pacman.lock")) {
|
||||||
|
return(list())
|
||||||
|
}
|
||||||
|
state <- jsonlite::read_json("pacman.lock")
|
||||||
|
return(state$history)
|
||||||
|
}
|
||||||
13
R/snapshot_latest.R
Normal file
13
R/snapshot_latest.R
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
#' Load the latest snapshot state from the lock file
|
||||||
|
#'
|
||||||
|
#' @return A list containing the last saved snapshot of the project state
|
||||||
|
#' @export
|
||||||
|
snapshot_latest <- function() {
|
||||||
|
if (!file.exists("pacman.lock")) {
|
||||||
|
return(NULL)
|
||||||
|
}
|
||||||
|
state <- jsonlite::read_json("pacman.lock")
|
||||||
|
state$history <- NULL
|
||||||
|
return(state)
|
||||||
|
}
|
||||||
45
R/utils.R
45
R/utils.R
@@ -13,6 +13,13 @@ check_pacman_dir <- function() {
|
|||||||
return(invisible(FALSE))
|
return(invisible(FALSE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (file.exists(".gitignore")) {
|
||||||
|
if (length(grep(".pacman/", ".gitignore")) == 0) {
|
||||||
|
con <- file(".gitignore", "a")
|
||||||
|
writeLines("\n# pacman - package management folder\n.pacman/", con)
|
||||||
|
close(con)
|
||||||
|
}
|
||||||
|
}
|
||||||
invisible(TRUE)
|
invisible(TRUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,3 +65,41 @@ select_menu <- function(choices, title = NULL, msg = "Enter a number from the me
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#' Structure of the imports data table
|
||||||
|
#'
|
||||||
|
#' @return datatable Empty table with the imports column structure
|
||||||
|
empty_sources <- function() {
|
||||||
|
data.frame(
|
||||||
|
package = character(0),
|
||||||
|
version = character(0),
|
||||||
|
type = character(0),
|
||||||
|
src = character(0),
|
||||||
|
ref = character(0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#' Re-formats a string to fix the width by padding with spaces
|
||||||
|
#'
|
||||||
|
#' @param str A character vector of strings to format
|
||||||
|
#' @param width An optional width of the string to pad to
|
||||||
|
#' @param justify "left" or "right" justification
|
||||||
|
#'
|
||||||
|
#' @return A formatted (padded) character vector
|
||||||
|
format_str <- function (str, width = NULL, justify = "left", ...) {
|
||||||
|
x <- format(str, justify = justify, width = width, ...)
|
||||||
|
if (!is.null(width)) {
|
||||||
|
str_width <- nchar(str, "width")
|
||||||
|
too_wide <- str_width > width
|
||||||
|
if (any(too_wide)) {
|
||||||
|
x[too_wide] <- paste0(substr(x[too_wide], 1, width - 3), "...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
#' Calls stop() returning to the top level but masks the message
|
||||||
|
stop_quietly <- function(...) {
|
||||||
|
blankMsg <- sprintf("\r%s\r", paste(rep(" ", getOption("width") - 1L), collapse = " "))
|
||||||
|
stop(simpleError(blankMsg))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user