Skip to content

Commit

Permalink
Merge pull request #40 from wlandau/interpret
Browse files Browse the repository at this point in the history
Add intepret_issue()
  • Loading branch information
wlandau authored Nov 22, 2024
2 parents c967511 + 0350907 commit b4434ef
Show file tree
Hide file tree
Showing 16 changed files with 487 additions and 28 deletions.
6 changes: 4 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ Package: multiverse.internals
Title: Internal Infrastructure for R-multiverse
Description: R-multiverse requires this internal infrastructure package to
automate contribution reviews and populate universes.
Version: 0.2.14.9000
Version: 0.2.15
License: MIT + file LICENSE
URL: https://github.com/r-multiverse/multiverse.internals
URL:
https://r-multiverse.org/multiverse.internals,
https://github.com/r-multiverse/multiverse.internals
BugReports: https://github.com/r-multiverse/multiverse.internals/issues
Authors@R: c(
person(
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export(assert_cran_url)
export(assert_package)
export(assert_release_exists)
export(get_current_versions)
export(interpret_issue)
export(issues_checks)
export(issues_dependencies)
export(issues_descriptions)
Expand Down
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# multiverse.internals 0.2.14.9000 (development)
# multiverse.internals 0.2.16

* Add `interpret_issue()` to help create RSS feeds.
* Add `record_nonstandard_licenses()`

# multiverse.internals 0.2.14
Expand Down
151 changes: 151 additions & 0 deletions R/interpret_issue.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#' @title Interpret a set of package issues
#' @export
#' @family issues
#' @description Summarize the issues of a package in human-readable text.
#' @return A character string summarizing the issues of a package in prose.
#' @param path Character string, file path to a JSON issue file
#' of a package.
interpret_issue <- function(path) {
package <- basename(path)
if (!file.exists(path)) {
return(paste("Package", package, "has no recorded issues."))
}
issue <- jsonlite::read_json(path, simplifyVector = TRUE)
paste0(
interpret_title(issue, package),
interpret_advisories(issue),
interpret_checks(issue),
interpret_dependencies(issue, package),
interpret_licenses(issue, package),
interpret_remotes(issue),
interpret_versions(issue)
)
}

interpret_title <- function(issue, package) {
title <- paste0(
"R-multiverse found issues with package ",
package
)
if (is.character(issue$version)) {
title <- paste(title, "version", issue$version)
}
if (is.character(issue$remote_hash)) {
title <- paste(title, "remote hash", issue$remote_hash)
}
paste0(title, " on ", issue$date, ".")
}

interpret_advisories <- function(issue) {
advisories <- issue$descriptions$advisories
if (is.null(advisories)) {
return(character(0L))
}
paste0(
"\n\nFound the following advisories in the ",
"R Consortium Advisory Database:\n\n",
as.character(yaml::as.yaml(advisories))
)
}

interpret_checks <- function(issue) {
checks <- issue$checks
if (is.null(checks)) {
return(character(0L))
}
paste0(
"\n\nNot all checks succeeded on R-universe. ",
"The following output shows the check status on each platform, ",
"the overall build status, and the ",
"build URL. Visit the build URL for specific details ",
"on the check failures.\n\n",
as.character(yaml::as.yaml(checks))
)
}

interpret_dependencies <- function(issue, package) {
dependencies <- issue$dependencies
if (is.null(dependencies)) {
return(character(0L))
}
direct <- names(dependencies)[lengths(dependencies) < 1L]
indirect <- setdiff(names(dependencies), direct)
text <- paste0(
"\n\nOne or more dependencies have issues. Packages ",
paste(names(dependencies), collapse = ", "),
" are causing problems upstream. "
)
if (length(direct)) {
text <- paste0(
text,
ifelse(length(direct) == 1L, "Dependency ", "Dependencies "),
paste(direct, collapse = ", "),
ifelse(length(direct) == 1L, " is ", " are "),
"explicitly mentioned in 'Depends:', 'Imports:', or 'LinkingTo:' ",
"in the DESCRIPTION of ",
package,
". "
)
}
if (length(indirect)) {
text <- paste0(
text,
ifelse(length(indirect) == 1L, "Package ", "Packages "),
paste(indirect, collapse = ", "),
ifelse(length(indirect) == 1L, " is ", " are "),
"not part of 'Depends:', 'Imports:', or 'LinkingTo:' ",
"in the DESCRIPTION of ",
package,
", but ",
ifelse(length(indirect) == 1L, "it is", "they are"),
" upstream of one or more direct dependencies:\n\n",
as.character(yaml::as.yaml(dependencies[indirect]))
)
}
text
}

interpret_licenses <- function(issue, package) {
license <- issue$descriptions$license
if (is.null(license)) {
return(character(0L))
}
paste(
"\n\nPackage",
package,
"declares license",
shQuote(license),
"in its DESCRIPTION file. R-multiverse cannot verify that",
"this license is a valid free and open-source license",
"(c.f. https://en.wikipedia.org/wiki/Free_and_open-source_software).",
"Each package contributed to R-multiverse must have a valid",
"open-source license to protect the intellectual property",
"rights of the package owners."
)
}

interpret_remotes <- function(issue) {
remotes <- issue$descriptions$remotes
if (is.null(remotes)) {
return(character(0L))
}
paste0(
"\n\nPackage releases should not use the 'Remotes:' field. Found:",
as.character(yaml::as.yaml(remotes))
)
}

interpret_versions <- function(issue) {
versions <- issue$versions
if (is.null(versions)) {
return(character(0L))
}
paste0(
"\n\nThe version number of the current release ",
"should be highest version of all the releases so far. ",
"Here is the current version of the package, ",
"the highest version number ever recorded by R-multiverse, ",
"and the latest remote hash of each:\n\n",
as.character(yaml::as.yaml(versions))
)
}
26 changes: 16 additions & 10 deletions R/issues_descriptions.R
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#' @title Report `DESCRIPTION` file issues.
#' @title Report `DESCRIPTION`-level issues.
#' @export
#' @family issues
#' @description Report issues with the `DESCRIPTION` files of packages.
#' @details [issues_descriptions()] scans downloaded metadata from the
#' `PACKAGES.json` file of an R universe and scans for specific issues in a
#' package's description file:
#' 1. The presence of a `"Remotes"` field.
#' 2. There is a security advisory at
#' <https://github.com/RConsortium/r-advisory-database>
#' for the given package version.
#' @description Report issues with `DESCRIPTION`-level metadata of packages.
#' @details [issues_descriptions()] reports specific issues in the
#' `DESCRIPTION`-level metadata of packages:
#' 1. Security advisories at
#' <https://github.com/RConsortium/r-advisory-database>.
#' 2. Licenses that cannot be verified as free and open-source.
#' 3. The presence of a `"Remotes"` field.
#' @inheritSection record_issues Package issues
#' @return A named list of information about packages which do not comply
#' with `DESCRPTION` checks. Each name is a package name,
Expand All @@ -23,9 +22,10 @@
issues_descriptions <- function(meta = meta_packages()) {
meta$issue <- FALSE
meta <- issues_descriptions_advisories(meta)
meta <- issues_descriptions_licenses(meta)
meta <- issues_descriptions_remotes(meta)
meta <- meta[meta$issue,, drop = FALSE] # nolint
issues_list(meta[, c("package", "advisories", "remotes")])
issues_list(meta[, c("package", "advisories", "license", "remotes")])
}

issues_descriptions_advisories <- function(meta) {
Expand All @@ -41,6 +41,12 @@ issues_descriptions_advisories <- function(meta) {
meta
}

issues_descriptions_licenses <- function(meta) {
meta$license[meta$foss] <- NA_character_
meta$issue <- meta$issue | !meta$foss
meta
}

issues_descriptions_remotes <- function(meta) {
meta[["remotes"]] <- meta[["remotes"]] %||% replicate(nrow(meta), NULL)
meta$remotes <- lapply(meta$remotes, function(x) x[nzchar(x)])
Expand Down
8 changes: 6 additions & 2 deletions R/meta_packages.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
#' @examples
#' meta_packages(repo = "https://wlandau.r-universe.dev")
meta_packages <- function(repo = "https://community.r-multiverse.org") {
fields <- c("Version", "Remotes", "RemoteSha")
repo <- trim_url(repo)
fields <- c("Version", "License", "Remotes", "RemoteSha")
listing <- file.path(
utils::contrib.url(trim_url(repo), type = "source"),
utils::contrib.url(repos = repo, type = "source"),
paste0("PACKAGES.json?fields=", paste(fields, collapse = ","))
)
out <- jsonlite::stream_in(
Expand All @@ -22,5 +23,8 @@ meta_packages <- function(repo = "https://community.r-multiverse.org") {
)
colnames(out) <- tolower(colnames(out))
rownames(out) <- out$package
foss <- utils::available.packages(repos = repo, filters = "license/FOSS")
out$foss <- FALSE
out[as.character(foss[, "Package"]), "foss"] <- TRUE
out
}
3 changes: 3 additions & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ reference:
- record_issues
- record_nonstandard_licenses
- record_versions
- title: Communicate issues
contents:
- interpret_issue
- title: Staging
contents:
- update_staging
Expand Down
26 changes: 26 additions & 0 deletions man/interpret_issue.Rd

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

1 change: 1 addition & 0 deletions man/issues_checks.Rd

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

1 change: 1 addition & 0 deletions man/issues_dependencies.Rd

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

16 changes: 8 additions & 8 deletions man/issues_descriptions.Rd

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

1 change: 1 addition & 0 deletions man/issues_versions.Rd

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

10 changes: 6 additions & 4 deletions tests/testthat/helper-mock.R
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ mock_meta_packages <- structure(
"BSD_3_clause + file LICENSE", "MIT + file LICENSE", "GPL (>= 2)",
"GPL (>= 3)", "GPL (>= 3)", "MIT + file LICENSE", "MIT + file LICENSE",
"GPL (>= 3)", "MIT + file LICENSE", "GPL (>= 3)", "MIT + file LICENSE",
"MIT + file LICENSE", "MIT + file LICENSE", "MIT + file LICENSE",
"MIT + file LICENSE", "non-standard", "MIT + file LICENSE",
"MIT + file LICENSE", "GPL-3", "MIT + file LICENSE"
),
remotesha = c(
Expand Down Expand Up @@ -574,7 +574,7 @@ mock_meta_packages <- structure(
)
),
distro = c(
"noble", "noble", "noble", "noble", "noble",
"noble", "noble", "noble", "noble", "nomock_meta_packages_graphble",
"noble", "noble", "noble", "noble", "noble", "noble", "noble",
"noble", "noble", "noble", "noble", "noble", "noble", "noble",
"noble"
Expand All @@ -584,7 +584,8 @@ mock_meta_packages <- structure(
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
c("hyunjimoon/SBC", "stan-dev/cmdstanr", ""), NULL, NULL,
"markvanderloo/tinytest/pkg", NULL, NULL, NULL
)
),
foss = c(rep(TRUE, 15L), FALSE, rep(TRUE, 4L))
),
class = "data.frame",
row.names = c(NA, 20L)
Expand Down Expand Up @@ -714,7 +715,8 @@ mock_meta_packages_graph <- structure(
row.names = c(NA, 5L)
)
),
distro = c("noble", "noble", "noble", "noble", "noble")
distro = c("noble", "noble", "noble", "noble", "noble"),
foss = rep(TRUE, 5L)
),
class = "data.frame",
row.names = c(NA, 5L)
Expand Down
Loading

0 comments on commit b4434ef

Please sign in to comment.