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

add support for renv lockfiles (#55) #80

Merged
merged 9 commits into from
Feb 26, 2023
3 changes: 3 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
^CRAN-SUBMISSION$
^.editorconfig$
^.codecov.yml$
^.*\.Rproj$
^\.Rproj\.user$
tests/.renvignore
44 changes: 44 additions & 0 deletions R/as_pkgrefs.R
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ as_pkgrefs.default <- function(x, ...) {
#' @rdname as_pkgrefs
#' @export
as_pkgrefs.character <- function(x, bioc_version = NULL, ...) {
if(.detect_renv_lockfile(x)){
return(.extract_pkgref_renv_lockfile(path = x))
}
return(.normalize_pkgs(pkgs = x, bioc_version = bioc_version))
}

Expand All @@ -44,6 +47,26 @@ as_pkgrefs.sessionInfo <- function(x, ...) {
vapply(X = x$otherPkgs, FUN = .extract_pkgref_packageDescription, FUN.VALUE = character(1), USE.NAMES = FALSE)
}

.extract_pkgref_renv_lockfile <- function(path){
lockfile <- .parse_renv_lockfile(path)
sources <- vapply(lockfile[["Packages"]],`[[`,character(1),"Source",USE.NAMES = FALSE)
pkgs <- c()
Copy link
Collaborator

Choose a reason for hiding this comment

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

a bit better to name this pkgrefs

if("Repository"%in%sources){
pkgs <- c(pkgs, paste0("cran::",vapply(lockfile[["Packages"]][sources=="Repository"],`[[`,character(1),"Package",USE.NAMES = FALSE)))
}
if("Bioconductor"%in%sources){
pkgs <- c(pkgs,paste0("bioc::",vapply(lockfile[["Packages"]][sources=="Bioconductor"],`[[`,character(1),"Package",USE.NAMES = FALSE)))
}
if("GitHub"%in%sources){
pkgs <- c(pkgs,
paste0("github::",
vapply(lockfile[["Packages"]][sources=="GitHub"],`[[`,character(1), "RemoteUsername", USE.NAMES = FALSE),"/",
vapply(lockfile[["Packages"]][sources=="GitHub"],`[[`,character(1), "Package", USE.NAMES = FALSE))
)
}
return(pkgs)
}

.extract_pkgref_packageDescription <- function(packageDescription) {
handle <- packageDescription[['Package']]
if ("GithubRepo" %in% names(packageDescription)) {
Expand All @@ -62,3 +85,24 @@ as_pkgrefs.sessionInfo <- function(x, ...) {
return(paste0("cran::", handle))
## }
}

.detect_renv_lockfile <- function(path){
# assuming all renv lockfiles are called renv.lock and path is only length 1
if(length(path)!=1){
return(FALSE)
}
if(isFALSE(file.exists(path))){
return(FALSE)
}
if(grepl("renv.lock$",path)){
Copy link
Collaborator

Choose a reason for hiding this comment

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

if (isFALSE(grepl("renv.lock$", path))) {
    return(FALSE)
}
TRUE

Copy link
Collaborator

@chainsawriot chainsawriot Feb 26, 2023

Choose a reason for hiding this comment

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

A slightly more robust method is basename(path) == "renv.lock"

It prevent the matching of hello_1231212renv.lock

return(TRUE)
} else{
return(FALSE)
}
}

.parse_renv_lockfile <- function(path){
lockfile <- jsonlite::fromJSON(path, simplifyVector = FALSE)
# class(lockfile) <- "renv_lockfile"
lockfile
}
2 changes: 1 addition & 1 deletion R/resolve.R
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
#'
#' This function recursively queries dependencies of R packages at a specific snapshot time. The dependency graph can then be used to recreate the computational environment. The data on dependencies are provided by R-hub.
#'
#' @param pkgs `pkgs` can be 1) a character vector of R packages to resolve, or 2) a data structure that [as_pkgrefs()] can convert to a character vector of package references. For 1) `pkgs` can be either in shorthands, e.g. "rtoot", "ropensci/readODS", or in package references, e.g. "cran::rtoot", "github::ropensci/readODS". Please refer to the [Package References documentation](https://r-lib.github.io/pkgdepends/reference/pkg_refs.html) of `pak` for details. Currently, this package supports only cran and github packages. For 2) [as_pkgrefs()] support the output of [sessionInfo()].
#' @param pkgs `pkgs` can be 1) a character vector of R packages to resolve, 2) a path to a [`renv` lockfile](https://rstudio.github.io/renv/articles/lockfile.html), or 3) a data structure that [as_pkgrefs()] can convert to a character vector of package references. For 1) `pkgs` can be either in shorthands, e.g. "rtoot", "ropensci/readODS", or in package references, e.g. "cran::rtoot", "github::ropensci/readODS". Please refer to the [Package References documentation](https://r-lib.github.io/pkgdepends/reference/pkg_refs.html) of `pak` for details. Currently, this package supports only cran and github packages. For 2) [as_pkgrefs()] support the output of [sessionInfo()].
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be better to add an integration test to test_resolve.R

testthat("integration of renv to resolve", {
    expect_error(X <- resolve("../testdata/renv.lock", snapshot_date = "2023-01-01"), NA)
    ### more things
})

Copy link
Member Author

Choose a reason for hiding this comment

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

@chainsawriot should I move all tests there or do a new set of tests?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just this test. In general, the last part of test_resolve.R is for all tests that require internet.

#' @param snapshot_date Snapshot date, if not specified, assume to be a month ago
#' @param no_enhances logical, whether to ignore packages in the "Enhances" field
#' @param no_suggests logical, whether to ignore packages in the "Suggests" field
Expand Down
2 changes: 1 addition & 1 deletion man/resolve.Rd

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

1 change: 1 addition & 0 deletions tests/.renvignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testdata
56 changes: 56 additions & 0 deletions tests/testdata/renv.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"R": {
"Version": "4.2.2",
"Repositories": [
{
"Name": "CRAN",
"URL": "https://cloud.r-project.org"
}
]
},
"Packages": {
"BiocGenerics": {
"Package": "BiocGenerics",
"Version": "0.44.0",
"Source": "Bioconductor",
"git_url": "https://git.bioconductor.org/packages/BiocGenerics",
"git_branch": "RELEASE_3_16",
"git_last_commit": "d7cd9c1",
"git_last_commit_date": "2022-11-01",
"Hash": "0de19224c2cd94f48fbc0d0bc663ce3b",
"Requirements": []
},
"levelnet": {
"Package": "levelnet",
"Version": "0.5.0",
"Source": "GitHub",
"RemoteType": "github",
"RemoteHost": "api.github.com",
"RemoteRepo": "levelnet",
"RemoteUsername": "schochastics",
"RemoteRef": "HEAD",
"RemoteSha": "775cf5e91b83cb73fe35e378ed1d7facb1d741eb",
"Hash": "29eed562ec1c9bb7e31ce87e321b9252",
"Requirements": [
"Matrix",
"Rcpp",
"igraph"
]
},
"rtoot": {
"Package": "rtoot",
"Version": "0.3.0",
"Source": "Repository",
"Repository": "CRAN",
"Hash": "06eb72de42a3f8fcb252badc58f92b2b",
"Requirements": [
"clipr",
"curl",
"dplyr",
"httr",
"jsonlite",
"tibble"
]
}
}
}
5 changes: 5 additions & 0 deletions tests/testthat/test_pkgref.R
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,8 @@ test_that("as_pkgrefs_packageDescription", {
## expect_equal(res, c("github::chainsawriot/grafzahl", "cran::rtoot", "local::/home/chainsawriot/dev/rang", "cran::testthat")
expect_equal(res, c("github::chainsawriot/grafzahl", "cran::rtoot", "cran::rang", "cran::testthat"))
})

test_that("as_pkgrefs renv_lockfile", {
res <- as_pkgrefs("../testdata/renv.lock")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

Add one more test

expect_false(.detect_renv_lockfile("./testdata/graph.RDS"))

Copy link
Collaborator

Choose a reason for hiding this comment

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

expect_false(.detect_renv_lockfile(c("./testdata/graph.RDS", "../testdata/renv.lock")))

Should this be false?

Copy link
Member Author

Choose a reason for hiding this comment

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

at least from the logic I am using. Which is that a renv lockfile occurs as a the only imput. Should we accept vector input for renv lock files, i.e. mixing types?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@schochastics I actually agree that this should be FALSE. A vector of lock files is too arcane a situation.

Copy link
Member Author

Choose a reason for hiding this comment

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

jep thats what I thought

expect_equal(res, c("cran::rtoot", "bioc::BiocGenerics", "github::schochastics/levelnet"))
})