Skip to content

Commit

Permalink
Add JSON lockfile schema and validation function (#1889)
Browse files Browse the repository at this point in the history
* Add JSON schema and validation function

* Update comment wording

Co-authored-by: Kevin Ushey <kevin@posit.co>

* Add `jsonvalidate` to `DESCRIPTION` `Suggests`

---------

Co-authored-by: Kevin Ushey <kevin@posit.co>
  • Loading branch information
jrdnbradford and kevinushey authored May 18, 2024
1 parent 5d0d52c commit 568e8cc
Show file tree
Hide file tree
Showing 7 changed files with 653 additions and 3 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ License: MIT + file LICENSE
URL: https://rstudio.github.io/renv/, https://github.com/rstudio/renv
BugReports: https://github.com/rstudio/renv/issues
Imports: utils
Suggests: BiocManager, cli, covr, cpp11, devtools, gitcreds, jsonlite, knitr, miniUI,
packrat, pak, R6, remotes, reticulate, rmarkdown, rstudioapi, shiny, testthat,
Suggests: BiocManager, cli, covr, cpp11, devtools, gitcreds, jsonlite, jsonvalidate, knitr,
miniUI, packrat, pak, R6, remotes, reticulate, rmarkdown, rstudioapi, shiny, testthat,
uuid, waldo, yaml, webfakes
Encoding: UTF-8
RoxygenNote: 7.3.1
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export(load)
export(lockfile_create)
export(lockfile_modify)
export(lockfile_read)
export(lockfile_validate)
export(lockfile_write)
export(migrate)
export(modify)
Expand Down
75 changes: 75 additions & 0 deletions R/lockfile-validate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

#' Validate an renv lockfile against a schema
#'
#' @description
#' `renv::lockfile_validate()` can be used to validate your `renv.lock`
#' against a default or custom schema. It can be used to automate checks,
#' check for obvious errors, and ensure that any custom fields you add fit
#' your specific needs.
#'
#' @details
#' See the [JSON Schema docs](https://json-schema.org/) for more information
#' on JSON schemas, their use in validation, and how to write your own schema.
#'
#' `renv::lockfile_validate()` wraps ROpenSci's
#' [`jsonvalidate`](https://docs.ropensci.org/jsonvalidate/) package, passing
#' many of its parameters to that package's `json_validate()` function. Use
#' `?jsonvalidate::json_validate` for more information.
#'
#' @param lockfile Contents of the lockfile, or a filename containing one.
#' If not provided, it defaults to the project's lockfile.
#'
#' @param schema Contents of a renv schema, or a filename containing a schema.
#' If not provided, renv's default schema is used.
#'
#' @param greedy Boolean. Continue after first error?
#'
#' @param error Boolean. Throw an error on parse failure?
#'
#' @param verbose Boolean. If `TRUE`, then an attribute `errors` will list validation failures as a `data.frame`.
#'
#' @param strict Boolean. Set whether the schema should be parsed strictly or not.
#' If in strict mode schemas will error to "prevent any unexpected behaviours or silently ignored mistakes in user schema".
#' For example it will error if encounters unknown formats or unknown keywords.
#' See https://ajv.js.org/strict-mode.html for details.
#'
#' @return Boolean. `TRUE` if validation passes. `FALSE` if validation fails.
#'
#' @examples
#' \dontrun{
#'
#' # validate the project's lockfile
#' renv::lockfile_validate()
#'
#' # validate the project's lockfile using a non-default schema
#' renv::lockfile_validate(schema = "/path/to/your/custom/schema.json")
#'
#' # validate a lockfile using its path
#' renv::lockfile_validate(lockfile = "/path/to/your/renv.lock")
#' }
#' @export
lockfile_validate <- function(project = NULL,
lockfile = NULL, # Use default project lockfile if not provided
schema = NULL, # Use default schema if not provided
greedy = FALSE,
error = FALSE,
verbose = FALSE,
strict = FALSE)
{

project <- renv_project_resolve(project)
lockfile <- lockfile %||% renv_lockfile_path(project = project)
schema <- schema %||% system.file("schema",
"draft-07.renv.lock.schema.json",
package = "renv",
mustWork = TRUE)

# "ajv" engine required for schema specifications later than draft-04
jsonvalidate::json_validate(lockfile,
schema,
engine = "ajv",
greedy = greedy,
error = error,
verbose = verbose,
strict = strict)
}
199 changes: 199 additions & 0 deletions inst/schema/draft-07.renv.lock.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$comment": "See https://github.com/rstudio/renv",
"title": "renv.lock file",
"description": "A schema for renv.lock files generated by {renv}",
"type": "object",
"properties": {
"R": {
"description": "Version of R used in the project",
"type": "object",
"properties": {
"Version": {
"description": "The version of R used",
"type": "string",
"examples": ["4.2.3"]
},
"Repositories": {
"description": "The R repositories used in this project",
"type": "array",
"items": {
"type": "object",
"properties": {
"Name": {
"description": "Name of the repository",
"type": "string",
"examples": ["CRAN"]
},
"URL": {
"description": "URL of the repository",
"type": "string",
"format": "uri",
"examples": ["https://cloud.r-project.org"]
}
},
"required": ["Name", "URL"]
}
}
},
"required": ["Version", "Repositories"]
},
"Bioconductor": {
"description": "",
"type": "object",
"properties": {
"Version": {
"description": "Release of Bioconductor",
"type": "string",
"examples": ["3.18"]
}
},
"required": ["Version"]
},
"Python": {
"description": "Version of Python used in the project",
"type": "object",
"properties": {
"Name": {
"description": "Path to the Python environment",
"type": "string",
"examples": [
".venv",
"./renv/python/virtualenvs/renv-python-3.10"
]
},
"Type": {
"description": "Type of Python environment",
"type": "string",
"examples": ["virtualenv"]
},
"Version": {
"description": "Version of Python required",
"type": "string",
"examples": ["3.10.12", "3.9.0"]
}
},
"required": ["Name", "Type", "Version"]
},
"Packages": {
"description": "Packages required by the R project",
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"Package": {
"description": "The package name",
"type": "string",
"examples": ["ggplot2", "dplyr"]
},
"Version": {
"description": "The package version",
"type": "string",
"examples": ["1.0.0", "3.4.6"]
},
"Source": {
"description": "The location from which this package was retrieved",
"type": "string",
"examples": [
"Repository",
"Bioconductor",
"/mnt/r/pkg/package_name_1.0.1.tar.gz"
]
},
"Repository": {
"description": "The name of the repository (if any) from which this package was retrieved",
"type": "string",
"examples": ["CRAN"]
},
"Hash": {
"description": "A unique hash for this package, used for package caching",
"type": "string",
"pattern": "^[a-fA-F0-9]{32}$",
"examples": ["06230136b2d2b9ba5805e1963fa6e890"]
},
"biocViews": {
"description": "Bioconductor package dependencies",
"type": "string"
},
"RemoteType": {
"description": "Type of the remote, typically written for packages installed by the devtools, remotes and pak packages",
"type": "string",
"examples": ["standard", "github"]
},
"RemoteHost": {
"description": "Host for the remote",
"type": "string",
"format": "hostname",
"examples": ["api.github.com"]
},
"RemoteUsername": {
"description": "Username for the remote",
"type": "string"
},
"RemoteRepo": {
"description": "Repositories for the package",
"type": "string",
"examples": [
"https://cran.rstudio.com",
"https://cloud.r-project.org"
]
},
"RemoteRepos": {
"description": "Repositories for the package",
"type": "string",
"format": "uri",
"examples": [
"https://cran.rstudio.com",
"https://cloud.r-project.org"
]
},
"RemoteRef": {
"description": "Ref of the package",
"type": "string",
"examples": ["renv", "main"]
},
"RemotePkgRef": {
"description": "Name of the packages",
"type": "string"
},
"RemotePkgPlatform": {
"description": "Architecture/platform of the remote",
"type": "string",
"examples": ["aarch64-apple-darwin20"]
},
"RemoteSha": {
"description": "Version number of the package",
"type": "string",
"examples": ["1763e0dcb72fb58d97bab97bb834fc71f1e012bc"]
},
"Requirements": {
"description": "Dependencies of the package",
"type": "array",
"items": {
"type": "string"
},
"examples": [
[
"R",
"jsonlite",
"lifecycle",
"magrittr",
"stringi"
],
[
"R6",
"Rcpp",
"later",
"magrittr",
"rlang",
"stats"
]
]
}
},
"required": ["Package"]
}
}
},
"required": ["R", "Packages"]
}
65 changes: 65 additions & 0 deletions man/lockfile_validate.Rd

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

Loading

0 comments on commit 568e8cc

Please sign in to comment.