Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support GitHub sha as version #1327

Merged
merged 18 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

# renv 0.18.0 (UNRELEASED)

* Development versions of renv are now tracked using the Git SHA of the
current commit, rather than a version number that's incremented on every
change (#1327). This shouldn't have any user facing impact, but makes
renv maintenance a little easier.

* Fixed an issue causing "restarting interrupted promise evaluation" warnings
to be displayed when querying available packages failed. (#1260)

Expand Down
4 changes: 3 additions & 1 deletion R/activate.R
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ renv_activate_version_lockfile <- function(project) {
return(NULL)

lockfile <- renv_lockfile_read(path)
lockfile$Packages[["renv"]]$Version %||% lockfile[["renv"]]$Version
lockfile$Packages[["renv"]]$RemoteSha %||%
lockfile$Packages[["renv"]]$Version %||%
lockfile[["renv"]]$Version

}

Expand Down
130 changes: 106 additions & 24 deletions R/bootstrap.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

startswith <- function(string, prefix) {
substring(string, 1, nchar(prefix)) == prefix
}

`%||%` <- function(x, y) {
if (is.null(x)) y else x
}
Expand Down Expand Up @@ -113,19 +117,11 @@ renv_bootstrap_repos_lockfile <- function() {

renv_bootstrap_download <- function(version) {

# if the renv version number has 4 components, assume it must
# be retrieved via github
nv <- numeric_version(version)
components <- unclass(nv)[[1]]

# if this appears to be a development version of 'renv', we'll
# try to restore from github
dev <- length(components) == 4L

# begin collecting different methods for finding renv
methods <- c(
renv_bootstrap_download_tarball,
if (dev)
# dev versions can only come from GitHub
if (renv_bootstrap_version_is_dev(version))
renv_bootstrap_download_github
else c(
renv_bootstrap_download_cran_latest,
Expand Down Expand Up @@ -375,11 +371,78 @@ renv_bootstrap_download_github <- function(version) {
return(FALSE)
}

renv_bootstrap_download_augment(destfile)

catf("OK")
return(destfile)

}

# Add Sha to DESCRIPTION. This is stop gap until #890, after which we
# can use renv::install() to fully capture metadata.
renv_bootstrap_download_augment <- function(destfile) {
sha <- renv_bootstrap_git_extract_sha1_tar(destfile)
if (is.null(sha)) {
return()
}

# Untar
tempdir <- tempfile("renv-github-")
on.exit(unlink(tempdir, recursive = TRUE), add = TRUE)
untar(destfile, exdir = tempdir)
pkgdir <- dir(tempdir, full.names = TRUE)[[1]]

# Modify description
desc_path <- file.path(pkgdir, "DESCRIPTION")
desc_lines <- readLines(desc_path)
remotes_fields <- c(
"RemoteType: github",
"RemoteHost: api.github.com",
"RemoteRepo: renv",
"RemoteUsername: rstudio",
"RemotePkgRef: rstudio/renv",
paste("RemoteRef: ", sha),
paste("RemoteSha: ", sha)
)
writeLines(c(desc_lines[desc_lines != ""], remotes_fields), desc_path)

# Re-tar
local({
old <- setwd(tempdir)
on.exit(setwd(old), add = TRUE)

tar(destfile, compression = "gzip")
})
invisible()
}

# Extract the commit hash from a git archive. Git archives include the SHA1
# hash as the comment field of the tarball pax extended header
# (see https://www.kernel.org/pub/software/scm/git/docs/git-archive.html)
# For GitHub archives this should be the first header after the default one
# (512 byte) header.
renv_bootstrap_git_extract_sha1_tar <- function(bundle) {

# open the bundle for reading
# We use gzcon for everything because (from ?gzcon)
# > Reading from a connection which does not supply a ‘gzip’ magic
# > header is equivalent to reading from the original connection
conn <- gzcon(file(bundle, open = "rb", raw = TRUE))
on.exit(close(conn))

# The default pax header is 512 bytes long and the first pax extended header
# with the comment should be 51 bytes long
# `52 comment=` (11 chars) + 40 byte SHA1 hash
len <- 0x200 + 0x33
res <- rawToChar(readBin(conn, "raw", n = len)[0x201:len])

if (grepl("^52 comment=", res)) {
sub("52 comment=", "", res)
} else {
NULL
}
}

renv_bootstrap_install <- function(version, tarball, library) {

# attempt to install it into project library
Expand Down Expand Up @@ -628,29 +691,37 @@ renv_bootstrap_library_root_impl <- function(project) {

}

renv_bootstrap_validate_version <- function(version) {
renv_bootstrap_validate_version <- function(version, description = NULL) {
version_is_version <- grepl("[.-]", version)
description <- description %||% utils::packageDescription("renv")

loadedversion <- utils::packageDescription("renv", fields = "Version")
if (version == loadedversion)
return(TRUE)
if (version_is_version) {
loaded <- description$Version
if (identical(loaded, version)) {
return(TRUE)
}
} else {
loaded <- description$RemoteSha
if (startswith(loaded, version)) {
return(TRUE)
}
}

# assume four-component versions are from GitHub;
# three-component versions are from CRAN
components <- strsplit(loadedversion, "[.-]")[[1]]
remote <- if (length(components) == 4L)
paste("rstudio/renv", loadedversion, sep = "@")
else
paste("renv", loadedversion, sep = "@")
remote <- if (renv_bootstrap_version_is_dev(version)) {
paste("rstudio/renv", loaded, sep = "@")
} else {
paste("renv", loaded, sep = "@")
}

fmt <- paste(
"renv %1$s was loaded from project library, but this project is configured to use renv %2$s.",
"Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.",
"Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.",
"* Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.",
"* Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.",
sep = "\n"
)

msg <- sprintf(fmt, loadedversion, version, remote)
warning(msg, call. = FALSE)
catf(fmt, loaded, version, remote)

FALSE

Expand Down Expand Up @@ -820,3 +891,14 @@ renv_bootstrap_user_dir_impl <- function() {

}

renv_bootstrap_version_is_dev <- function(version) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should bring in R/version.R here:

renv/R/zzz.R

Lines 113 to 116 in 8b145e8

# read the necessary bootstrap scripts
scripts <- c("R/bootstrap.R", "R/json-read.R")
contents <- map(scripts, readLines)
bootstrap <- unlist(contents)

and use those utilities here? I'm not sure what we would call this function though; maybe it suffices to just check renv_version_length(version) == 3L to infer that it's a release version of renv?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still needs slightly different code because it's slightly different test to figure out if we're using a sha or a version number. So IMO, it's not worth it.

# if the renv version number is a sha, or has 4 components, it must
# be retrieved via github
if (!grepl("[.-]", version)) {
# not . or -, so must be a sha
TRUE
} else {
components <- strsplit(version, "[.-]")[[1]]
length(components) != 3
}
}
10 changes: 4 additions & 6 deletions R/init.R
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ init <- function(project = NULL,

# for bare inits, just activate the project
if (bare)
return(renv_init_fini(project, profile, version, restart, quiet))
return(renv_init_fini(project, profile, restart, quiet))

# collect dependencies
renv_dependencies_scope(project, action = "init")
Expand All @@ -131,18 +131,16 @@ init <- function(project = NULL,
}

# activate the newly-hydrated project
renv_init_fini(project, profile, version, restart, quiet)
renv_init_fini(project, profile, restart, quiet)

}

renv_init_fini <- function(project, profile, version, restart, quiet) {

version <- renv_metadata_version()
renv_init_fini <- function(project, profile, restart, quiet) {

renv_activate_impl(
project = project,
profile = profile,
version = version,
version = renv_metadata_version(),
restart = restart,
quiet = quiet
)
Expand Down
13 changes: 11 additions & 2 deletions R/metadata.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@

renv_metadata_version <- function() {
`_renv_metadata`[["version"]]
`_renv_metadata`$sha %||% `_renv_metadata`$version
}

renv_metadata_is_dev <- function(metadata = `_renv_metadata`) {
if (!is.null(metadata$sha)) {
TRUE
} else {
renv_version_length(metadata$version) != 3L
}
}

renv_metadata_embedded <- function() {
Expand All @@ -16,7 +24,8 @@ renv_metadata_init <- function() {
# set up metadata
metadata <- list(
embedded = FALSE,
version = renv_namespace_version("renv")
version = renv_namespace_version("renv"),
sha = packageDescription("renv")[["RemoteSha"]]
)

# create in namespace
Expand Down
5 changes: 5 additions & 0 deletions R/patch.R
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ renv_patch_repos <- function() {
if (identical(name, "renv"))
return()

# presumably this will never happen when the dev version of renv is
# installed, so we skip to avoid parsing a sha as version
if (renv_metadata_is_dev())
return()

# nothing to do if this version of 'renv' is already available
version <- renv_metadata_version()
entry <- catch(renv_available_packages_entry("renv", filter = version, quiet = TRUE))
Expand Down
3 changes: 1 addition & 2 deletions R/snapshot.R
Original file line number Diff line number Diff line change
Expand Up @@ -1108,8 +1108,7 @@ renv_snapshot_fixup_renv <- function(records) {

# no valid record available; construct a synthetic one
version <- renv_metadata_version()
components <- unclass(numeric_version(version))[[1]]
remote <- if (length(components) == 4L)
remote <- if (renv_metadata_is_dev())
paste("rstudio/renv", version, sep = "@")
else
paste("renv", version, sep = "@")
Expand Down
3 changes: 1 addition & 2 deletions R/upgrade.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
#' When `NULL` (the default), the latest version of renv will be installed as
#' available from CRAN (or whatever active package repositories are active)
#' Alternatively, you can install the latest development version with
#' `"main"`, or a specific version from GitHub with (e.g.) `"0.17.3"` or
#' `"0.17.3-29"`.
#' `"main"`, or a specific commit with a SHA, e.g. `"5049cef8a"`.
#'
#' @param prompt Boolean; prompt upgrade before proceeding?
#'
Expand Down
4 changes: 0 additions & 4 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,6 @@ trunc <- function(text, n = 78) {
text
}

startswith <- function(string, prefix) {
substring(string, 1, nchar(prefix)) == prefix
}

endswith <- function(string, suffix) {
substring(string, nchar(string) - nchar(suffix) + 1) == suffix
}
Expand Down
5 changes: 5 additions & 0 deletions R/version.R
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,8 @@ renv_version_components <- function(version, n) {
parts[n]

}

renv_version_length <- function(version) {
nv <- numeric_version(version)
length(unclass(nv)[[1]])
}
Loading