diff --git a/.travis.yml b/.travis.yml index 29e4cfbb..e433c511 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,3 +8,5 @@ apt_packages: after_success: - Rscript -e 'covr::codecov()' + +r_github_packages: ropensci/jsonld diff --git a/DESCRIPTION b/DESCRIPTION index 671dcf18..0b2bcbf8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,50 +1,77 @@ Package: codemetar Type: Package Title: Generate 'CodeMeta' Metadata for R Packages -Version: 0.1.5 -Authors@R: c(person("Carl", "Boettiger", - role=c("aut", "cre", "cph"), - email="cboettig@gmail.com", - comment=c(ORCID = "0000-0002-1642-628X")), - person("Anna", "Krystalli", - role = "rev", - comment=c(ORCID = "0000-0002-2378-4915")), - person("Toph", "Allen", - role = "rev", - comment=c(ORCID = "0000-0003-4580-091X"))) +Version: 0.1.6 +Authors@R: + c(person(given = "Carl", + family = "Boettiger", + role = c("aut", "cre", "cph"), + email = "cboettig@gmail.com", + comment = structure("0000-0002-1642-628X", .Names = "ORCID")), + person(given = "Anna", + family = "Krystalli", + role = c("rev", "ctb"), + comment = structure("0000-0002-2378-4915", .Names = "ORCID")), + person(given = "Toph", + family = "Allen", + role = "rev", + comment = structure("0000-0003-4580-091X", .Names = "ORCID")), + person(given = "Maëlle", + family = "Salmon", + role = c("ctb", "aut"), + comment = structure("0000-0002-2815-0399", .Names = "ORCID")), + person(given = "rOpenSci", + role = "fnd", + comment = "https://ropensci.org/"), + person(given = "Katrin", + family = "Leinweber", + role = "ctb"), + person(given = "Noam", + family = "Ross", + role = "ctb"), + person(given = "Arfon", + family = "Smith", + role = "ctb")) Description: The 'Codemeta' Project defines a 'JSON-LD' format for describing software metadata, as detailed at . This package provides utilities to generate, parse, and modify 'codemeta.json' files automatically for R packages, as well as tools and examples for working with 'codemeta.json' 'JSON-LD' more generally. -License: MIT + file LICENSE -URL: https://github.com/ropensci/codemetar +License: GPL-3 +URL: https://github.com/ropensci/codemetar,https://codemeta.github.io/codemetar BugReports: https://github.com/ropensci/codemetar/issues Encoding: UTF-8 LazyData: true -RoxygenNote: 6.0.1 +RoxygenNote: 6.0.1.9000 Depends: R (>= 3.0.0) -Imports: jsonlite (>= 1.3), +Imports: + jsonlite (>= 1.3), jsonld, git2r, devtools, methods, stats, stringi, - readr -Suggests: testthat, + readr, + desc, + usethis, + whisker, + tibble, + crul, + gh, + stringr, + sessioninfo +Suggests: + testthat, jsonvalidate, covr, knitr, rmarkdown, - httr, magrittr, xml2, dplyr (>= 0.7.0), - tibble, purrr, printr VignetteBuilder: knitr X-schema.org-keywords: metadata, codemeta, ropensci, citation, credit, linked-data X-schema.org-isPartOf: https://ropensci.org -X-schema.org-relatedLink: https://codemeta.github.io/codemetar diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 43bc93ef..00000000 --- a/LICENSE +++ /dev/null @@ -1,2 +0,0 @@ -YEAR: 2017 -COPYRIGHT HOLDER: Carl Boettiger diff --git a/NAMESPACE b/NAMESPACE index 4c9807ea..c8cb66e7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,9 +6,9 @@ export(create_codemeta) export(crosswalk) export(crosswalk_table) export(drop_context) +export(extract_badges) +export(give_opinions) export(write_codemeta) -importFrom(devtools,build) -importFrom(devtools,use_build_ignore) importFrom(git2r,branches) importFrom(git2r,repository) importFrom(jsonld,jsonld_compact) @@ -16,7 +16,6 @@ importFrom(jsonld,jsonld_expand) importFrom(jsonlite,fromJSON) importFrom(jsonlite,read_json) importFrom(jsonlite,toJSON) -importFrom(jsonlite,write_json) importFrom(methods,is) importFrom(readr,cols) importFrom(readr,read_csv) diff --git a/NEWS.md b/NEWS.md index 056ea00b..6683bfd6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,27 @@ +# codemetar 0.1.6 2018-04 + +* Use desc to parse DESCRIPTION files. + +* Writing codemeta.json for the first time adds a git pre-commit hook and suggests adding a release question for devtools::release. + +* License changed to GPL because of code borrowed from usethis + +* Add give_opinion function giving opiniated advice about package metadata + +* Replace httr with crul and use crul to check URLs. + +* relatedLink field now include provider URL and URL(s) from DESCRIPTION that are not the code repository + +* add an extract_badges function for extracting information from all badges in a Markdown file. + +* now if more than one CI service among Travis, Appveyor and Circle CI are used and shown via a README badge they're all added to the contIntegration field. + +* now ability to parse all CRAN-allowed MARC roles. + +* if there is a badge for an rOpenSci onboarding review and the review issue is closed, basic review metadata is added to codemeta.json + +* For dependencies, if the provider guessed is CRAN or BioConductor, their canonic CRAN/BioConductor URL is added to codemeta.json as sameAs, unless there's a GitHub repo mentioned for them in Remotes in DESCRIPTION, in which case sameAs is that GitHub repo. + # codemetar 0.1.5 2018-03-21 * Default to DOI-based schema. (previous CN issues now resolved) diff --git a/R/codemeta_description.R b/R/codemeta_description.R index 76103953..dd25adcf 100644 --- a/R/codemeta_description.R +++ b/R/codemeta_description.R @@ -17,40 +17,63 @@ new_codemeta <- function() { codemeta_description <- function(f, id = NULL, codemeta = new_codemeta()) { if (file.exists(f)) { - descr <- cm_read_dcf(f) + descr <- desc::desc(f) } else { return(codemeta) } - if (is.null(descr) || length(descr) == 0) { - return(codemeta) - } ## FIXME define an S3 class based on the codemeta list of lists? if (is.null(id)) { - id <- descr$Package + id <- descr$get("Package") } if (is_IRI(id)) { codemeta$`@id` <- id } - codemeta$identifier <- descr$Package - codemeta$description <- descr$Description - codemeta$name <- paste0(descr$Package, ": ", descr$Title) + codemeta$identifier <- descr$get("Package") + codemeta$description <- descr$get("Description") + codemeta$name <- paste0(descr$get("Package"), ": ", + descr$get("Title")) + + + ## Get URLs + code_repo <- descr$get_urls() + if (!is.na(code_repo[1])){ + + if(length(code_repo) == 1){ + # only one, easy + codemeta$codeRepository <- code_repo + }else{ + # try to identify a GitHub or Gitlab repo + actual_code_repo <- code_repo[grepl("github\\.com", code_repo)| + grepl("gitlab\\.com", code_repo)][1] + # otherwise take the first URL arbitrarily + if(is.null(codemeta$Repository)){ + codemeta$codeRepository <- actual_code_repo + } + + # add other URLs as related links + codemeta$relatedLink <- unique(c(codemeta$relatedLink, + code_repo[code_repo != actual_code_repo])) + } + } + + issue_tracker <- descr$get("BugReports") + if (!is.na(issue_tracker)){ + codemeta$issueTracker <- issue_tracker + } + - ## Will later guess these these a la devtools::use_github_links - codemeta$codeRepository <- descr$URL - codemeta$issueTracker <- descr$BugReports ## According to crosswalk, codemeta$dateModified and ## codemeta$dateCreated are not crosswalked in DESCRIPTION - codemeta$datePublished <- - descr$Date # probably not avaialable as descr$Date. + codemeta$datePublished <- NULL - codemeta$license <- spdx_license(descr$License) + codemeta$license <- spdx_license(descr$get("License")) - codemeta$version <- descr$Version + codemeta$version <- as.character(descr$get_version()) codemeta$programmingLanguage <- list( "@type" = "ComputerLanguage", @@ -64,33 +87,60 @@ codemeta_description <- codemeta$runtimePlatform <- R.version.string if (is.null(codemeta$provider)) - codemeta$provider <- guess_provider(descr$Package) - if ("Authors@R" %in% names(descr)) { + codemeta$provider <- guess_provider(descr$get("Package")) + authors <- try(descr$get_authors(), silent = TRUE) + if (!inherits(authors,'try-error')) { codemeta <- - parse_people(eval(parse(text = descr$`Authors@R`)), codemeta) + parse_people(authors, codemeta) } else { - codemeta <- parse_people(as.person(descr$Author), codemeta) - ## maintainer must come second in case Author list also specifies - ## maintainer by role [cre] without email - codemeta$maintainer <- - person_to_schema(as.person(descr$Maintainer)) + # get authors and maintainer from their fields + # and don't get maintainer twice! + authors <- as.person(descr$get("Author")) + maintainer <- descr$get_maintainer() + maintainer <- as.person(paste(maintainer)) + maintainer$role <- "cre" + authors_strings <- paste(authors$given, authors$family) + maintainer_strings <- paste(maintainer$given, maintainer$family) + # for now only one maintainer + if(length(maintainer) > 1){ + + authors <- c(authors[!authors_strings %in% maintainer_strings[1]], + maintainer[2:length(maintainer)]) + maintainer <- maintainer[1] + }else{ + authors <- authors[!authors_strings %in% maintainer_strings] + } + authors <- c(authors, maintainer) + codemeta <- + parse_people(authors, codemeta) } - codemeta$softwareSuggestions <- parse_depends(descr$Suggests) - codemeta$softwareRequirements <- - c(parse_depends(descr$Imports), - parse_depends(descr$Depends)) + dependencies <- descr$get_deps() + suggests <- dependencies[dependencies$type == "Suggests",] + requirements <- dependencies[dependencies$type %in% + c("Imports", "Depends"),] + + remotes <- descr$get_remotes() + + suggests$remote_provider <- unlist(lapply(suggests$package, + add_remote_to_dep, remotes = remotes)) + requirements$remote_provider <- unlist(lapply(requirements$package, + add_remote_to_dep, remotes = remotes)) + + codemeta$softwareSuggestions <- parse_depends(suggests) + codemeta$softwareRequirements <- parse_depends(requirements) ## add any additional codemeta terms found in the DESCRIPTION metadata + for(term in additional_codemeta_terms){ ## in DESCRIPTION, these terms must be *prefixed*: X_term <- paste0("X-schema.org-", term) - if(!is.null(descr[[X_term]])){ + if(!is.na(descr$get(X_term))){ codemeta[[term]] <- gsub("\\s+", "", - strsplit(descr[[X_term]], ",")[[1]]) + strsplit(descr$get(X_term), ",")[[1]]) } } diff --git a/R/create_codemeta.R b/R/create_codemeta.R index 43fdb05c..7f2ce0ea 100644 --- a/R/create_codemeta.R +++ b/R/create_codemeta.R @@ -17,6 +17,7 @@ create_codemeta <- function(pkg = ".", id = NULL, force_update = getOption("codemeta_force_update", TRUE), + verbose = TRUE, ...) { ## looks like we got a package name/path or Description file if (is.character(pkg)) { @@ -31,6 +32,7 @@ create_codemeta <- function(pkg = ".", ## no cm, no existing codemeta.json found, start fresh } else { cm <- new_codemeta() + } ## we got an existing codemeta object as pkg @@ -43,24 +45,45 @@ create_codemeta <- function(pkg = ".", } + if(verbose){ + opinions <- give_opinions(root) + if(!is.null(opinions)) + message(paste0("Some elements could be improved, see our opinions via give_opinions('", root, "')")) + } + cm <- codemeta_description(file.path(root, "DESCRIPTION"), id = id, cm) ## Guess these only if not set in current codemeta: - if (is.null(cm$codeRepository) | force_update) + if ((is.null(cm$codeRepository) & force_update)){ cm$codeRepository <- guess_github(root) - if (is.null(cm$contIntegration) | force_update) + } + + if ((is.null(cm$contIntegration) | force_update)){ cm$contIntegration <- guess_ci(file.path(root, "README.md")) - if (is.null(cm$developmentStatus) | force_update) + } + + if ((is.null(cm$developmentStatus) | force_update)){ cm$developmentStatus <- guess_devStatus(file.path(root, "README.md")) - if (is.null(cm$releaseNotes) | force_update) + } + + if ((is.null(cm$review) | force_update)){ + cm$review <- + guess_ropensci_review(file.path(root, "README.md")) + } + + if ((is.null(cm$releaseNotes) | force_update)){ cm$releaseNotes <- guess_releaseNotes(root) - if (is.null(cm$readme) | force_update) + } + + if ((is.null(cm$readme) | force_update)){ cm$readme <- guess_readme(root) - if (is.null(cm$fileSize) | force_update) - cm$fileSize <- guess_fileSize(root) + } + if ((is.null(cm$fileSize) | force_update)){ + cm$fileSize <- guess_fileSize(root) + } ## Citation metadata if(is.character(pkg)){ ## Doesn't apply if pkg is a list (codemeta object) @@ -71,6 +94,61 @@ create_codemeta <- function(pkg = ".", cm$`@context` <- c(cm$`@context`, "http://schema.org") } } + + # Add provider link as relatedLink + if(is.character(pkg)){ + pkg_info <- sessioninfo::package_info(cm$identifier) + pkg_info <- pkg_info[pkg_info$package == cm$identifier,] + provider_name <- pkg_info$source + if(grepl("CRAN", provider_name)){ + cm$relatedLink <- unique(c(cm$relatedLink, + paste0("https://CRAN.R-project.org/package=", + cm$identifier))) + }else{ + if(grepl("Bioconductor", provider_name)){ + cm$relatedLink <- unique(c(cm$relatedLink, + paste0("https://bioconductor.org/packages/release/bioc/html/", + cm$identifier, ".html"))) + }else{ + # if GitHub try to build the URL to commit or to repo in general + if(grepl("Github", provider_name)){ + if(grepl("@", provider_name)){ + commit <- gsub(".*@", "", provider_name) + commit <- gsub("\\)", "", commit) + link <- gsub(".*\\(", "", provider_name) + link <- gsub("@.*", "", link) + cm$relatedLink <- unique(c(cm$relatedLink, + paste0("https://github.com/", link, + "/commit/", commit))) + } + } + } + } + }else{ + provider <- guess_provider(cm$identifier) + + readme <- guess_readme(root) + if(!is.null(readme)){ + badges <- extract_badges(readme) + if(!is.null(provider) & + whether_provider_badge(badges, + provider$name)){ + if(provider$name == "Central R Archive Network (CRAN)"){ + cm$relatedLink <- unique(c(cm$relatedLink, + paste0("https://CRAN.R-project.org/package=", + cm$identifier))) + }else{ + if(provider$name == "BioConductor"){ + cm$relatedLink <- unique(c(cm$relatedLink, + paste0("https://bioconductor.org/packages/release/bioc/html/", + cm$identifier, ".html"))) + } + } + } + } + } + + ## Add blank slots as placeholders? and declare as an S3 class? cm diff --git a/R/crosswalk.R b/R/crosswalk.R index 721f7d45..cbe1ec13 100644 --- a/R/crosswalk.R +++ b/R/crosswalk.R @@ -35,6 +35,13 @@ crosswalk <- function(x, } else { to_context <- codemeta_context } + + # ids need to be coerced to character + # in order to be compacted by jsonld + x[["id"]] <- as.character(x[["id"]]) + x$owner$id <- as.character(x$owner$id) + x$organization$id <- as.character(x$organization$id) + crosswalk_transform(x, crosswalk_context = from_context, codemeta_context = to_context) diff --git a/R/give_opinions.R b/R/give_opinions.R new file mode 100644 index 00000000..6d471d38 --- /dev/null +++ b/R/give_opinions.R @@ -0,0 +1,127 @@ +#' Function giving opinions about a package +#' +#' @param pkg_path Path to the package root +#' +#' @return A data.frame of opinions +#' @export +#' +give_opinions <- function(pkg_path = getwd()){ + descr_path <- file.path(pkg_path, "DESCRIPTION") + # opinions about DESCRIPTION + descr_issues <- give_opinions_desc(descr_path) + + # opinions about README + if (!file.exists(file.path(pkg_path, "README.md"))) { + readme_issues <- NULL + }else{ + desc_info <- codemeta_description(descr_path) + readme_issues <- give_opinions_readme(file.path(pkg_path, + "README.md"), + desc_info$identifier) + } + + + fixmes <- rbind(descr_issues, readme_issues) + + if(!is.null(fixmes)){ + descr <- desc::desc(descr_path) + fixmes$package <- as.character(descr$get("Package")) + return(fixmes) + }else{ + return(NULL) + } + + +} + +give_opinions_desc <- function(descr_path){ + descr <- desc::desc(descr_path) + # Authors + if (inherits( try(descr$get_authors(), silent = TRUE), + 'try-error')){ + authors_fixme <- "The syntax Authors@R instead of plain Author and Maintainer is recommended in particular because it allows richer metadata" # no lint + }else{ + authors_fixme <- NULL + } + + # URL + if(is.na(descr$get("URL"))){ + url_fixme <- "URL field. Indicate the URL to your code repository." + }else{ + checkurls <- check_urls(descr$descr$get_urls) + if(checkurls != ""){ + url_fixme <- checkurls + }else{ + url_fixme <- NULL + } + + } + + # BugReports + if(is.na(descr$get("BugReports"))){ + bugreports_fixme <- "BugReports field. Indicate where to report bugs, e.g. GitHub issue tracker." + }else{ + checkurls <- check_urls(descr$get("BugReports")) + if(checkurls != ""){ + bugreports_fixme <- checkurls + }else{ + bugreports_fixme <- NULL + } + + } + + fixmes <- c(authors_fixme, url_fixme, bugreports_fixme) + fixmes <- fixmes[!is.null(fixmes)] + + if(length(fixmes) >0){ + + tibble::tibble(where = "DESCRIPTION", + fixme = fixmes) + }else{ + NULL + } + + +} + +give_opinions_readme <- function(readme_path, + pkg_name){ + + # look for badges + badges <- extract_badges(readme_path) + # status + if(!any(grepl("Project Status", + badges$text))){ + status_fixme <- "Add a status badge cf e.g repostatus.org" + }else{ + status_fixme <- NULL + } + + # provider + provider <- guess_provider(pkg_name) + if(!is.null(provider)){ + provider_badge <- whether_provider_badge(badges, + provider$name) + + if(!provider_badge){ + provider_fixme <- paste0("There is a package called ", + pkg_name, " on ", + provider$name, ", add a badge to show it is yours or rename your package.") + }else{ + provider_fixme <- NULL + } + }else{ + provider_fixme <- NULL + } + + fixmes <- c(status_fixme, provider_fixme) + fixmes <- fixmes[!is.null(fixmes)] + if(length(fixmes) >0){ + + tibble::tibble(where = "README", + fixme = fixmes) + }else{ + NULL + } + +} diff --git a/R/guess_metadata.R b/R/guess_metadata.R index 2920750a..737ecbd5 100644 --- a/R/guess_metadata.R +++ b/R/guess_metadata.R @@ -49,38 +49,73 @@ guess_provider <- function(pkg) { ## look for .travis.yml ? GREP .travis badge so we can guess repo name. guess_ci <- function(readme) { - link <- NULL if (file.exists(readme)) { - txt <- readLines(readme) - badge <- txt[grepl("travis-ci", txt)] - link <- - gsub(".*(https://travis-ci.org/\\w+/\\w+).*", "\\1", badge) - } - if (length(link) >= 1) { - link[[1]] - } else { + badges <- extract_badges(readme) + ci_badge <- badges[grepl("travis", badges$link)| + grepl("appveyor", badges$link)| + grepl("circleci", badges$link),] + if (!is.null(ci_badge)) { + ci_badge$link + } else { + NULL + } + }else{ NULL } } ## Currently looks for a repostatus.org link and returns the abbreviation. guess_devStatus <- function(readme) { + status <- NULL + if (file.exists(readme)) { + badges <- extract_badges(readme) + status_badge <- badges[grepl("Project Status", badges$text)| + grepl("lifecycle", badges$text),] + if (!is.null(status_badge)) { + if(nrow(status_badge) >0){ + status_badge$link[1] + } + } else { + NULL + } + }else{ + NULL + } + + +} + +# looks for a rOpenSci peer review badge +guess_ropensci_review <- function(readme) { status <- NULL if (file.exists(readme)) { txt <- readLines(readme) - badge <- txt[grepl("Project Status", txt)] - ## Text-based status line - # status <- gsub(".*\\[!\\[(Project Status: .*)\\.\\].*", "\\1", badge) - ## use \\2 for repostatus.org term, \\1 for repostatus.org term link - status <- - gsub(".*(http://www.repostatus.org/#(\\w+)).*", "\\2", badge) - } - if (length(status) >= 1) { - status[[1]] - } else { + badge <- txt[grepl("badges\\.ropensci\\.org", txt)] + if (length(badge) >= 1) { + review <- + gsub(".*https://github.com/ropensci/onboarding/issues/", "", badge) + review <- gsub(").*", "", review) + review <- as.numeric(review) + issue <- gh::gh("GET /repos/:owner/:repo/issues/:number", + owner = "ropensci", + repo = "onboarding", + number = review) + if(issue$state == "closed"){ + list("@type" = "Review", + "url" = paste0("https://github.com/ropensci/onboarding/issues/", + review), + "provider" = "http://ropensci.org") + }else{ + NULL + } + } else { + NULL + } + }else{ NULL } + } @@ -91,12 +126,16 @@ guess_orcids <- function(codemeta) { NULL } +# from devtools https://github.com/r-lib/devtools/blob/21fe55a912ca4eaa49ef5b7d891ff3e2aae7a370/R/git.R#L1 +# GPL>=2 code uses_git <- function(root) { !is.null(git2r::discover_repository(root, ceiling = 0)) } guess_github <- function(root = ".") { ## from devtools + # https://github.com/r-lib/devtools/blob/21fe55a912ca4eaa49ef5b7d891ff3e2aae7a370/R/git.R#L130 + # GPL>=2 code remote_urls <- function (r) { remotes <- git2r::remotes(r) stats::setNames(git2r::remote_url(r, remotes), remotes) @@ -159,7 +198,6 @@ guess_releaseNotes <- function(root = ".") { } -#' @importFrom devtools build guess_fileSize <- function(root = ".") { if (is.null(root)) { return(NULL) diff --git a/R/parse_citation.R b/R/parse_citation.R index b772d9cf..220e715c 100644 --- a/R/parse_citation.R +++ b/R/parse_citation.R @@ -75,7 +75,7 @@ guess_citation <- function(pkg){ installed <- installed.packages() if(file.exists(file.path(pkg, "inst/CITATION"))){ bib <- readCitationFile(file.path(pkg, "inst/CITATION"), - meta = cm_read_dcf(file.path(pkg, "DESCRIPTION"))) + meta = desc::desc(file.path(pkg, "DESCRIPTION"))) lapply(bib, parse_citation) } else if(pkg %in% installed[,1]){ bib <- suppressWarnings(citation(pkg)) # don't worry if no date diff --git a/R/parse_depends.R b/R/parse_depends.R index dee30ab6..9100110c 100644 --- a/R/parse_depends.R +++ b/R/parse_depends.R @@ -1,41 +1,46 @@ ## internal method for parsing a list of package dependencies into pkg URLs +format_depend <- function(package, version, remote_provider){ + dep <- list("@type" = "SoftwareApplication", + identifier = package, + ## FIXME technically the name includes the title + name = package) + ## Add Version if available + if (version != "*"){ + dep$version <- version + } + + dep$provider <- guess_provider(package) + + ## implemention could be better, e.g. support versioning + # dep$`@id` <- guess_dep_id(dep) + + # CRAN canonical URL + if(!is.null(dep$provider)){ + if(dep$provider$name == "Central R Archive Network (CRAN)"){ + dep$sameAs <- paste0("https://CRAN.R-project.org/package=", + dep$identifier) + }else{ + if(dep$provider$name == "BioConductor"){ + dep$sameAs <- paste0("https://bioconductor.org/packages/release/bioc/html/", + dep$identifier, ".html") + } + } + } + + if(remote_provider != ""){ + dep$sameAs <- paste0("https://github.com/", remote_provider) + } + + return(dep) + +} parse_depends <- function(deps) { - if (!is.null(deps)) - str <- strsplit(deps, ",\n*")[[1]] - else - str <- NULL - - lapply(str, function(str) { - #if (length(str) > 1) { - # warning(paste0("package depends", str, "may be multiple packages?")) - #} - - pkg <- gsub("\\s*(\\w+)\\s.*", "\\1", str) - pkg <- gsub("\\s+", "", pkg) - - dep <- list("@type" = "SoftwareApplication", - identifier = pkg, - ## FIXME technically the name includes the title - name = pkg) - - ## Add Version if available - pattern <- "\\s*\\w+\\s+\\([><=]+\\s([1-9.\\-]*)\\)*" - version <- gsub(pattern, "\\1", str) - version <- - gsub("\\)$", "", version) ## hack, avoid extraneous ending ) - has_version <- grepl(pattern, str) - if (has_version) - dep$version <- version - - dep$provider <- guess_provider(pkg) - - ## implemention could be better, e.g. support versioning - # dep$`@id` <- guess_dep_id(dep) - dep - }) + + unname(mapply(format_depend, deps$package, deps$version, + deps$remote_provider)) } @@ -61,3 +66,13 @@ guess_dep_id <- function(dep) { id } + +add_remote_to_dep <- function(package, remotes){ + remote_provider <- remotes[grepl(paste0("/", package, "$"), + remotes)] + if(length(remote_provider) == 0){ + "" + }else{ + remote_provider + } +} diff --git a/R/parse_people.R b/R/parse_people.R index 9b1aa43f..363e623d 100644 --- a/R/parse_people.R +++ b/R/parse_people.R @@ -16,8 +16,13 @@ parse_people <- function(people, codemeta) { codemeta$author <- c(people_with_role(people, "aut"), people_without_role(people)) - codemeta$contributor <- people_with_role(people, "ctb") + codemeta$contributor <- c(people_with_role(people, "ctb"), + people_with_role(people, "com"), + people_with_role(people, "dtc"), + people_with_role(people, "ths"), + people_with_role(people, "trl")) codemeta$copyrightHolder <- people_with_role(people, "cph") + codemeta$funder <- people_with_role(people, "fnd") codemeta$maintainer <- people_with_role(people, "cre") codemeta } diff --git a/R/utils.R b/R/utils.R index afdd51c3..948a78fd 100644 --- a/R/utils.R +++ b/R/utils.R @@ -17,14 +17,6 @@ get_root_path <- function(pkg){ } -## based on devtools::read_dcf -cm_read_dcf <- function(dcf) { - - fields <- colnames(read.dcf(dcf)) - as.list(read.dcf(dcf, keep.white = fields)[1, ]) - -} - ## Like system.file, but pkg can instead be path to package root directory get_file <- function(FILE, pkg = "."){ f <- file.path(pkg, FILE) @@ -46,3 +38,99 @@ is_IRI <- function(string){ grepl("^http[s]?://", string) } + +# from usethis cf https://github.com/r-lib/usethis/blob/2abb0422a97808cc573fa5900a8efcfed4c2d5b4/R/git.R#L68 +# this is GPL-3 code +uses_git <- function(path = usethis::proj_get()) { + !is.null(git2r::discover_repository(path)) +} + +# from usethis cf https://github.com/r-lib/usethis/blob/4fb556788d2588facaaa8560242d2c83f2261d6e/R/helpers.R#L55 +# this is GPL-3 code +render_template <- function(template, data = list(), package = "codemetar") { + template_path <- find_template(template, package = package) + strsplit(whisker::whisker.render(readLines(template_path), data), "\n")[[1]] +} + +# from usethis cf https://github.com/r-lib/usethis/blob/4fb556788d2588facaaa8560242d2c83f2261d6e/R/helpers.R#L60 +# this is GPL-3 code +find_template <- function(template_name, package = "usethis") { + system.file("templates", template_name, package = package) + +} + +get_url_status_code <- function(url){ + if(!is.null(url)){ + if(!is.na(url)){ + result <- try(crul::HttpClient$new(url)$get(), silent = TRUE) + if (!inherits(result,'try-error')){ + code <- result$status_code + if(code == 200){ + message <- "All good" + }else{ + message <- paste("Error code:", code) + } + }else{ + message <- "No connection was possible" + } + return(data.frame(message = message, url = url)) + }else{ + return(NULL) + } + }else{ + return(NULL) + } + +} + +check_urls <- function(urls){ + messages <- do.call(rbind, lapply(urls, get_url_status_code)) + if(any(messages$message != "All good")){ + paste("Problematic URLs\n", apply(messages[messages$message != "All good",], + 1, toString)) + }else{ + "" + } +} + +#' Extract all badges from Markdown file +#' +#' @param path Path to Markdown file +#' +#' @return A tibble with for each badge its text, link and link to +#' its image. +#' @export +#' +#' @examples +extract_badges <- function(path){ + txt <- readLines(path) + badges1 <- unlist(stringr::str_match_all(txt, "\\[!\\[\\]\\(.*?\\)\\]\\(.*?\\)")) + badges2 <- unlist(stringr::str_match_all(txt, "\\[!\\[.*?\\]\\(.*?\\)\\]\\(.*?\\)")) + badges <- c(badges1, badges2) + badges[!is.na(badges)] + + unique(do.call(rbind, + lapply(badges, parse_badge))) +} + +parse_badge <- function(badge){ + text <- stringr::str_match(badge, "\\[!\\[(.*?)\\]")[,2] + link <- stringr::str_match(badge, "\\)\\]\\((.*?)\\)")[,2] + image_link <- stringr::str_match(badge, "\\]\\((.*?)\\)\\]")[,2] + tibble::tibble(text = text, + link = link, + image_link = image_link) +} + +whether_provider_badge <- function(badges, provider_name){ + if(provider_name == "Central R Archive Network (CRAN)"){ + provider_badge <- any(grepl("CRAN", badges$text)) + }else{ + if(provider_name == "BioConductor"){ + provider_badge <- any(grepl("bioconductor", + badges$link)) + } + } + + provider_badge +} diff --git a/R/write_codemeta.R b/R/write_codemeta.R index e02f07eb..b055073d 100644 --- a/R/write_codemeta.R +++ b/R/write_codemeta.R @@ -13,6 +13,7 @@ #' to package root. Default guess is current dir. #' @param id identifier for the package, e.g. a DOI (or other resolvable URL) #' @param force_update Update guessed fields even if they are defined in an existing codemeta.json file +#' @param verbose Whether to print messages indicating opinions e.g. when DESCRIPTION has no URL. See \code{\link{give_opinions}}. #' @param ... additional arguments to \code{\link{write_json}} #' @details If pkg is a codemeta object, the function will attempt to #' update any fields it can guess (i.e. from the DESRIPTION file), @@ -21,26 +22,52 @@ #' @return writes out the codemeta.json file #' @export #' -#' @importFrom jsonlite write_json -#' @importFrom devtools use_build_ignore -# upcoming devtools release will switch this to: -# @importFrom usethis use_build_ignore #' @examples -#' write_codemeta("codemeta", tempfile()) +#' write_codemeta("codemetar", tempfile()) +#' @details +#' +#' When creating and writing a codemeta.json for the first time, +#' the function adds "codemeta.json" to .Rbuildignore and, if the +#' project uses git, adds a pre-commit hook ensuring that if DESCRIPTION changes, +#' the codemeta.json will be updated as well unless the DESCRIPTION change is committed +#' with 'git commit --no-verify'. write_codemeta <- function(pkg = ".", path = "codemeta.json", root = ".", id = NULL, force_update = getOption("codemeta_force_update", TRUE), + verbose = TRUE, ...) { if(length(pkg) <= 1 && file.exists(file.path(pkg, "DESCRIPTION"))){ - devtools::use_build_ignore("codemeta.json", pkg = pkg) - ## usethis::use_build_ignore("codemeta.json", base_path = pkg) + usethis::use_build_ignore("codemeta.json") + # add the git pre-commit hook + # https://github.com/r-lib/usethis/blob/master/inst/templates/readme-rmd-pre-commit.sh#L1 + # this is GPL-3 code + if (uses_git()) { + if(!file.exists(file.path(pkg, "codemeta.json"))){ + message("* Adding a pre-commit git hook ensuring that codemeta.json will be synchronized with DESCRIPTION") # nolint + usethis::use_git_hook( + "pre-commit", + render_template("description-codemetajson-pre-commit.sh") + ) + message( + "* Include the following code somewhere in R/ in your package, this way devtools::release() will remind you to update codemeta.json.\n", + ' release_questions <- function() "Have you updated codemeta.json with codemetar::write_codemeta()?"') + } + + } } cm <- create_codemeta(pkg = pkg, root = root) - jsonlite::write_json(cm, path, pretty=TRUE, auto_unbox = TRUE, ...) + write_json(cm, path, pretty=TRUE, auto_unbox = TRUE, ...) } +# from https://github.com/jeroen/jsonlite/blob/1f9e609e7d0ed702ede9c82aa5482ba08d5e5ab2/R/read_json.R#L22 +#' @param x an object to be serialized to JSON +#' @param ... additional arguments passed to \link{toJSON} or \link{fromJSON} +write_json <- function(x, path, ...) { + json <- jsonlite::toJSON(x, ...) + writeLines(json, path, useBytes = TRUE) +} diff --git a/appveyor.yml b/appveyor.yml index c6c14384..d4a4794c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,9 +14,17 @@ cache: - C:\RLibrary # Adapt as necessary starting from here +environment: + matrix: + - R_VERSION: stable + + - R_VERSION: patched + + - R_VERSION: devel build_script: - travis-tool.sh install_deps + - travis-tool.sh install_github r-lib/desc test_script: - travis-tool.sh run_tests diff --git a/codemeta.json b/codemeta.json index bcac4ce7..5b18b455 100644 --- a/codemeta.json +++ b/codemeta.json @@ -7,9 +7,10 @@ "identifier": "codemetar", "description": "The 'Codemeta' Project defines a 'JSON-LD' format for describing\n software metadata, as detailed at . This package\n provides utilities to generate, parse, and modify 'codemeta.json' files \n automatically for R packages, as well as tools and examples for working with\n 'codemeta.json' 'JSON-LD' more generally.", "name": "codemetar: Generate 'CodeMeta' Metadata for R Packages", + "codeRepository": "https://github.com/ropensci/codemetar", "issueTracker": "https://github.com/ropensci/codemetar/issues", - "license": "https://spdx.org/licenses/MIT", - "version": "0.1.5", + "license": "https://spdx.org/licenses/GPL-3.0", + "version": "0.1.6", "programmingLanguage": { "@type": "ComputerLanguage", "name": "R", @@ -30,6 +31,41 @@ "familyName": "Boettiger", "email": "cboettig@gmail.com", "@id": "https://orcid.org/0000-0002-1642-628X" + }, + { + "@type": "Person", + "givenName": "Maëlle", + "familyName": "Salmon", + "@id": "https://orcid.org/0000-0002-2815-0399" + } + ], + "contributor": [ + { + "@type": "Person", + "givenName": "Anna", + "familyName": "Krystalli", + "@id": "https://orcid.org/0000-0002-2378-4915" + }, + { + "@type": "Person", + "givenName": "Maëlle", + "familyName": "Salmon", + "@id": "https://orcid.org/0000-0002-2815-0399" + }, + { + "@type": "Person", + "givenName": "Katrin", + "familyName": "Leinweber" + }, + { + "@type": "Person", + "givenName": "Noam", + "familyName": "Ross" + }, + { + "@type": "Person", + "givenName": "Arfon", + "familyName": "Smith" } ], "copyrightHolder": [ @@ -41,6 +77,12 @@ "@id": "https://orcid.org/0000-0002-1642-628X" } ], + "funder": [ + { + "@type": "Organization", + "name": "rOpenSci" + } + ], "maintainer": { "@type": "Person", "givenName": "Carl", @@ -58,7 +100,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=testthat" }, { "@type": "SoftwareApplication", @@ -69,7 +112,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=jsonvalidate" }, { "@type": "SoftwareApplication", @@ -80,7 +124,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=covr" }, { "@type": "SoftwareApplication", @@ -91,7 +136,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=knitr" }, { "@type": "SoftwareApplication", @@ -102,18 +148,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } - }, - { - "@type": "SoftwareApplication", - "identifier": "httr", - "name": "httr", - "provider": { - "@id": "https://cran.r-project.org", - "@type": "Organization", - "name": "Central R Archive Network (CRAN)", - "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=rmarkdown" }, { "@type": "SoftwareApplication", @@ -124,7 +160,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=magrittr" }, { "@type": "SoftwareApplication", @@ -135,30 +172,21 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=xml2" }, { "@type": "SoftwareApplication", "identifier": "dplyr", "name": "dplyr", - "version": "0.7.0", + "version": ">= 0.7.0", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } - }, - { - "@type": "SoftwareApplication", - "identifier": "tibble", - "name": "tibble", - "provider": { - "@id": "https://cran.r-project.org", - "@type": "Organization", - "name": "Central R Archive Network (CRAN)", - "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=dplyr" }, { "@type": "SoftwareApplication", @@ -169,7 +197,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=purrr" }, { "@type": "SoftwareApplication", @@ -180,21 +209,29 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=printr" } ], "softwareRequirements": [ + { + "@type": "SoftwareApplication", + "identifier": "R", + "name": "R", + "version": ">= 3.0.0" + }, { "@type": "SoftwareApplication", "identifier": "jsonlite", "name": "jsonlite", - "version": "1.3", + "version": ">= 1.3", "provider": { "@id": "https://cran.r-project.org", "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=jsonlite" }, { "@type": "SoftwareApplication", @@ -205,7 +242,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=jsonld" }, { "@type": "SoftwareApplication", @@ -216,7 +254,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=git2r" }, { "@type": "SoftwareApplication", @@ -227,7 +266,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=devtools" }, { "@type": "SoftwareApplication", @@ -248,7 +288,8 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=stringi" }, { "@type": "SoftwareApplication", @@ -259,22 +300,120 @@ "@type": "Organization", "name": "Central R Archive Network (CRAN)", "url": "https://cran.r-project.org" - } + }, + "sameAs": "https://CRAN.R-project.org/package=readr" }, { "@type": "SoftwareApplication", - "identifier": "R", - "name": "R", - "version": "3.0.0" + "identifier": "desc", + "name": "desc", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=desc" + }, + { + "@type": "SoftwareApplication", + "identifier": "usethis", + "name": "usethis", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=usethis" + }, + { + "@type": "SoftwareApplication", + "identifier": "whisker", + "name": "whisker", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=whisker" + }, + { + "@type": "SoftwareApplication", + "identifier": "tibble", + "name": "tibble", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=tibble" + }, + { + "@type": "SoftwareApplication", + "identifier": "crul", + "name": "crul", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=crul" + }, + { + "@type": "SoftwareApplication", + "identifier": "gh", + "name": "gh", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=gh" + }, + { + "@type": "SoftwareApplication", + "identifier": "stringr", + "name": "stringr", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=stringr" + }, + { + "@type": "SoftwareApplication", + "identifier": "sessioninfo", + "name": "sessioninfo", + "provider": { + "@id": "https://cran.r-project.org", + "@type": "Organization", + "name": "Central R Archive Network (CRAN)", + "url": "https://cran.r-project.org" + }, + "sameAs": "https://CRAN.R-project.org/package=sessioninfo" } ], - "codeRepository": "https://github.com/ropensci/codemetar", - "isPartOf": "https://ropensci.org", - "keywords": ["metadata", "codemeta", "ropensci", "citation", "credit", "linked-data"], - "relatedLink": "https://codemeta.github.io/codemetar", - "contIntegration": "https://travis-ci.org/ropensci/codemetar", - "developmentStatus": "active", + "contIntegration": ["https://travis-ci.org/ropensci/codemetar", "https://ci.appveyor.com/project/cboettig/codemetar/branch/master"], + "developmentStatus": "http://www.repostatus.org/#active", "releaseNotes": "https://github.com/ropensci/codemetar/blob/master/NEWS.md", "readme": "https://github.com/ropensci/codemetar/blob/master/README.md", - "fileSize": "266.748KB" + "fileSize": "275.695KB", + "review": { + "@type": "Review", + "url": "https://github.com/ropensci/onboarding/issues/130", + "provider": "http://ropensci.org" + }, + "relatedLink": [ + "https://CRAN.R-project.org/package=codemetar", + "https://codemeta.github.io/codemetar" + ], + "isPartOf": "https://ropensci.org", + "keywords": ["metadata", "codemeta", "ropensci", "citation", "credit", "linked-data"] } diff --git a/cran-comments.md b/cran-comments.md index 5071d93d..3d681bb4 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,7 +1,8 @@ Dear CRAN Maintainers, -This release migrates the default schema to a DOI-based URL rather than a purl.org based URL. -Package reviewer contributions are also now acknowledged with the newly supported `rev` MARC code. +This release follows the release of jsonld that introduced a bug. + +More details in the release summary below. Thanks! @@ -12,6 +13,8 @@ Carl * local OS X install, R 3.4.4 * ubuntu 16.04 (on travis-ci), R 3.4.4 +* Windows (on Appveyor CI), dev and release +* local Windows install, R 3.4.4 * win-builder (devel and release) ## R CMD check results diff --git a/inst/examples/DESCRIPTION_HIBPwned b/inst/examples/DESCRIPTION_HIBPwned new file mode 100644 index 00000000..17f925c3 --- /dev/null +++ b/inst/examples/DESCRIPTION_HIBPwned @@ -0,0 +1,37 @@ +Package: HIBPwned +Title: Bindings for the 'HaveIBeenPwned.com' Data Breach API +Version: 0.1.7 +Authors@R: c(person("Steph", "Locke" + , email = "stephanie.g.locke@gmail.com" + , role = c("aut", "cre")) + ,person("Troy", "Hunt" + , email = "troyhunt@hotmail.com" + , role = c("aut") + , comment="HaveIBeenPwned.com" + ), + person("Locke Data", role = c("fnd")), + person("Maëlle", "Salmon", role = c("ctb"))) +Description: Utilising the 'Have I been pwned?' API (see + for more information), check whether email addresses and/or user names have been present + in a publicly disclosed data breach. +Depends: + R (>= 3.2.3) +License: MIT + file LICENSE +LazyData: true +Suggests: + testthat, + knitr, + rmarkdown, + lintr, + webmockr +Imports: + crul, + urltools, + jsonlite, + ratelimitr, + memoise +RoxygenNote: 6.0.1.9000 +VignetteBuilder: knitr +URL: https://github.com/lockedata/HIBPwned, https://itsalocke.com/hibpwned/ +BugReports: https://github.com/lockedata/HIBPwned/issues +Encoding: UTF-8 diff --git a/inst/examples/DESCRIPTION_Rforge b/inst/examples/DESCRIPTION_Rforge new file mode 100644 index 00000000..5bf2a4fa --- /dev/null +++ b/inst/examples/DESCRIPTION_Rforge @@ -0,0 +1,29 @@ +Package: essurvey +Title: Download Data from the European Social Survey on the Fly +Version: 1.0.0 +Authors@R: c( + person("Jorge", "Cimentada", email = "cimentadaj@gmail.com", role = c("aut", "cre")), + person("Thomas", "Leeper", role = "rev", comment = "Thomas reviewed the package for rOpensci,see https://github.com/ropensci/onboarding/issues/201"), + person("Nujcharee", "Haswell", role = "rev", comment = "Nujcharee reviewed the package for rOpensci, see https://github.com/ropensci/onboarding/issues/201") + ) +BugReports: https://github.com/ropensci/essurvey/issues +Description: Download data from the European Social Survey directly from their website . There are two families of functions that allow you to download and interactively check all countries and rounds available. +Depends: R (>= 3.4.0) +License: MIT + file LICENSE +URL: http://surveillance.R-Forge.R-project.org/ +Encoding: UTF-8 +LazyData: true +RoxygenNote: 6.0.1 +Imports: + xml2, + httr, + stringr, + haven, + rvest, + tibble +Suggests: + testthat, + knitr, + rmarkdown, + covr +VignetteBuilder: knitr diff --git a/inst/examples/DESCRIPTION_ex1.dcf b/inst/examples/DESCRIPTION_ex1.dcf index 7180518b..4a7a83e6 100644 --- a/inst/examples/DESCRIPTION_ex1.dcf +++ b/inst/examples/DESCRIPTION_ex1.dcf @@ -29,5 +29,5 @@ Suggests: testthat, readr, xml2 VignetteBuilder: knitr -Keywords: metadata, codemeta, ropensci, citation, credit -Affiliation: https://ropensci.org +X-schema.org-keywords: metadata, codemeta, ropensci, citation, credit, linked-data +X-schema.org-isPartOf: https://ropensci.org diff --git a/inst/examples/DESCRIPTION_jsonlite b/inst/examples/DESCRIPTION_jsonlite new file mode 100644 index 00000000..667d8807 --- /dev/null +++ b/inst/examples/DESCRIPTION_jsonlite @@ -0,0 +1,33 @@ +Package: jsonlite +Version: 1.5.9000 +Title: A Robust, High Performance JSON Parser and Generator for R +License: MIT + file LICENSE +NeedsCompilation: yes +Depends: + methods +Author: Jeroen Ooms, Duncan Temple Lang, Lloyd Hilaiel +URL: https://arxiv.org/abs/1403.2805, + https://www.opencpu.org/posts/jsonlite-a-smarter-json-encoder +BugReports: http://github.com/jeroen/jsonlite/issues +Maintainer: Jeroen Ooms +VignetteBuilder: knitr, R.rsp +Description: A fast JSON parser and generator optimized for statistical data + and the web. Started out as a fork of 'RJSONIO', but has been completely + rewritten in recent versions. The package offers flexible, robust, high + performance tools for working with JSON in R and is particularly powerful + for building pipelines and interacting with a web API. The implementation is + based on the mapping described in the vignette (Ooms, 2014). In addition to + converting JSON data from/to R objects, 'jsonlite' contains functions to + stream, validate, and prettify JSON data. The unit tests included with the + package verify that all edge cases are encoded and decoded consistently for + use with dynamic data in systems and applications. +Suggests: + httr, + curl, + plyr, + testthat, + knitr, + rmarkdown, + R.rsp, + sp +RoxygenNote: 6.0.1.9000 diff --git a/inst/examples/DESCRIPTION_no_URL b/inst/examples/DESCRIPTION_no_URL new file mode 100644 index 00000000..f0d23904 --- /dev/null +++ b/inst/examples/DESCRIPTION_no_URL @@ -0,0 +1,28 @@ +Package: essurvey +Title: Download Data from the European Social Survey on the Fly +Version: 1.0.0 +Authors@R: c( + person("Jorge", "Cimentada", email = "cimentadaj@gmail.com", role = c("aut", "cre")), + person("Thomas", "Leeper", role = "rev", comment = "Thomas reviewed the package for rOpensci,see https://github.com/ropensci/onboarding/issues/201"), + person("Nujcharee", "Haswell", role = "rev", comment = "Nujcharee reviewed the package for rOpensci, see https://github.com/ropensci/onboarding/issues/201") + ) +BugReports: https://github.com/ropensci/essurvey/issues +Description: Download data from the European Social Survey directly from their website . There are two families of functions that allow you to download and interactively check all countries and rounds available. +Depends: R (>= 3.4.0) +License: MIT + file LICENSE +Encoding: UTF-8 +LazyData: true +RoxygenNote: 6.0.1 +Imports: + xml2, + httr, + stringr, + haven, + rvest, + tibble +Suggests: + testthat, + knitr, + rmarkdown, + covr +VignetteBuilder: knitr diff --git a/inst/examples/DESCRIPTION_no_bugreports b/inst/examples/DESCRIPTION_no_bugreports new file mode 100644 index 00000000..d8c0c08a --- /dev/null +++ b/inst/examples/DESCRIPTION_no_bugreports @@ -0,0 +1,28 @@ +Package: essurvey +Title: Download Data from the European Social Survey on the Fly +Version: 1.0.0 +Authors@R: c( + person("Jorge", "Cimentada", email = "cimentadaj@gmail.com", role = c("aut", "cre")), + person("Thomas", "Leeper", role = "rev", comment = "Thomas reviewed the package for rOpensci,see https://github.com/ropensci/onboarding/issues/201"), + person("Nujcharee", "Haswell", role = "rev", comment = "Nujcharee reviewed the package for rOpensci, see https://github.com/ropensci/onboarding/issues/201") + ) +Description: Download data from the European Social Survey directly from their website . There are two families of functions that allow you to download and interactively check all countries and rounds available. +Depends: R (>= 3.4.0) +License: MIT + file LICENSE +URL: https://github.com/ropensci/essurvey, https://ropensci.github.io/essurvey/ +Encoding: UTF-8 +LazyData: true +RoxygenNote: 6.0.1 +Imports: + xml2, + httr, + stringr, + haven, + rvest, + tibble +Suggests: + testthat, + knitr, + rmarkdown, + covr +VignetteBuilder: knitr diff --git a/inst/examples/DESCRIPTION_two_URLs b/inst/examples/DESCRIPTION_two_URLs new file mode 100644 index 00000000..2c32fe91 --- /dev/null +++ b/inst/examples/DESCRIPTION_two_URLs @@ -0,0 +1,29 @@ +Package: essurvey +Title: Download Data from the European Social Survey on the Fly +Version: 1.0.0 +Authors@R: c( + person("Jorge", "Cimentada", email = "cimentadaj@gmail.com", role = c("aut", "cre")), + person("Thomas", "Leeper", role = "rev", comment = "Thomas reviewed the package for rOpensci,see https://github.com/ropensci/onboarding/issues/201"), + person("Nujcharee", "Haswell", role = "rev", comment = "Nujcharee reviewed the package for rOpensci, see https://github.com/ropensci/onboarding/issues/201") + ) +BugReports: https://github.com/ropensci/essurvey/issues +Description: Download data from the European Social Survey directly from their website . There are two families of functions that allow you to download and interactively check all countries and rounds available. +Depends: R (>= 3.4.0) +License: MIT + file LICENSE +URL: https://github.com/ropensci/essurvey, https://ropensci.github.io/essurvey/ +Encoding: UTF-8 +LazyData: true +RoxygenNote: 6.0.1 +Imports: + xml2, + httr, + stringr, + haven, + rvest, + tibble +Suggests: + testthat, + knitr, + rmarkdown, + covr +VignetteBuilder: knitr diff --git a/inst/examples/DESCRIPTION_with_remote b/inst/examples/DESCRIPTION_with_remote new file mode 100644 index 00000000..6262a94b --- /dev/null +++ b/inst/examples/DESCRIPTION_with_remote @@ -0,0 +1,18 @@ + +Package: jane +Type: Package +Title: Just Testing Something That Baffles Me +Version: 0.0.0.9000 +Authors@R: person("Jennifer", "Bryan", role=c("aut", "cre"), + email = "jenny@stat.ubc.ca") +Description: More about what it does (maybe more than one line). Even more + about what it does (so I don't have a Malformed Description field). +License: MIT + file LICENSE +LazyData: TRUE +Imports: readr (> 0.1) +VignetteBuilder: knitr +Suggests: knitr, + testthat +URL: https://github.com/jennybc/jane +BugReports: https://github.com/jennybc/jane/issues +Remotes: hadley/readr diff --git a/inst/examples/DESCRIPTION_wrongURLS b/inst/examples/DESCRIPTION_wrongURLS new file mode 100644 index 00000000..e65cf711 --- /dev/null +++ b/inst/examples/DESCRIPTION_wrongURLS @@ -0,0 +1,33 @@ +Package: jsonlite +Version: 1.5.9000 +Title: A Robust, High Performance JSON Parser and Generator for R +License: MIT + file LICENSE +NeedsCompilation: yes +Depends: + methods +Author: Jeroen Ooms, Duncan Temple Lang, Lloyd Hilaiel +URL: https://httpbin.org/status/429, + https://www.opencpu.org/posts/jsonlite-a-smarter-json-encoder +BugReports: https://httpbin.org/status/404 +Maintainer: Jeroen Ooms +VignetteBuilder: knitr, R.rsp +Description: A fast JSON parser and generator optimized for statistical data + and the web. Started out as a fork of 'RJSONIO', but has been completely + rewritten in recent versions. The package offers flexible, robust, high + performance tools for working with JSON in R and is particularly powerful + for building pipelines and interacting with a web API. The implementation is + based on the mapping described in the vignette (Ooms, 2014). In addition to + converting JSON data from/to R objects, 'jsonlite' contains functions to + stream, validate, and prettify JSON data. The unit tests included with the + package verify that all edge cases are encoded and decoded consistently for + use with dynamic data in systems and applications. +Suggests: + httr, + curl, + plyr, + testthat, + knitr, + rmarkdown, + R.rsp, + sp +RoxygenNote: 6.0.1.9000 diff --git a/inst/examples/README_codemetar_bad.md b/inst/examples/README_codemetar_bad.md new file mode 100644 index 00000000..1d339af6 --- /dev/null +++ b/inst/examples/README_codemetar_bad.md @@ -0,0 +1,86 @@ + + [![Travis-CI Build Status](https://travis-ci.org/ropensci/codemetar.svg?branch=master)](https://travis-ci.org/ropensci/codemetar) [![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/csawpip238vvbd72/branch/master?svg=true)](https://ci.appveyor.com/project/cboettig/codemetar/branch/master) [![Coverage Status](https://img.shields.io/codecov/c/github/ropensci/codemetar/master.svg)](https://codecov.io/github/ropensci/codemetar?branch=master) [![](http://badges.ropensci.org/130_status.svg)](https://github.com/ropensci/onboarding/issues/130) [![DOI](https://zenodo.org/badge/86626030.svg)](https://zenodo.org/badge/latestdoi/86626030) + + +codemetar +========= + +The goal of codemetar is to generate the JSON-LD file, `codemeta.json` containing software metadata describing an R package. For more general information about the CodeMeta Project for defining software metadata, see . In particular, new users might want to start with the [User Guide](https://codemeta.github.io/user-guide/), while those looking to learn more about JSON-LD and consuming existing codemeta files should see the [Developer Guide](https://codemeta.github.io/developer-guide/). + +Installation +------------ + +You can install the latest version from CRAN using: + +``` r +install.packages("codemetar") +``` + +You can also install the development version of `codemetar` from github with: + +``` r +# install.packages("devtools") +devtools::install_github("ropensci/codemetar") +``` + +``` r +library("codemetar") +``` + +Example +------- + +This is a basic example which shows you how to generate a `codemeta.json` for an R package (e.g. for `testthat`): + +``` r +write_codemeta("testthat") +``` + +`codemetar` can take the path to the package root instead. This may allow `codemetar` to glean some additional information that is not available from the description file alone. + +``` r +write_codemeta(".") +``` + +Which creates a file looking like so (first 10 lines; see full [codemeta.json here](https://github.com/codemeta/codemetar/blob/master/codemeta.json)): + + { + "@context": [ + "http://purl.org/codemeta/2.0", + "http://schema.org" + ], + "@type": "SoftwareSourceCode", + "identifier": "codemetar", + "description": "The 'Codemeta' Project defines a 'JSON-LD' format for describing\n software metadata, as detailed at . This package\n provides utilities to generate, parse, and modify 'codemeta.json' files \n automatically for R packages, as well as tools and examples for working with\n 'codemeta.json' 'JSON-LD' more generally.", + "name": "codemetar: Generate 'CodeMeta' Metadata for R Packages", + "issueTracker": "https://github.com/ropensci/codemetar/issues", + +Modifying or enriching CodeMeta metadata +---------------------------------------- + +The best way to ensure `codemeta.json` is as complete as possible is to begin by making full use of the fields that can be set in an R package DESCRIPTION file, such as `BugReports` and `URL`. Using the `Authors@R` notation allows a much richer specification of author roles, correct parsing of given vs family names, and email addresses. + +In the current implementation, developers may specify an ORCID url for an author in the optional `comment` field of `Authors@R`, e.g. + + Authors@R: person("Carl", "Boettiger", role=c("aut", "cre", "cph"), email="cboettig@gmail.com", comment="http://orcid.org/0000-0002-1642-628X") + +which will allow `codemetar` to associate an identifier with the person. If the package is hosted on CRAN, including the ORCiD in this way will cause an ORCiD logo and link to the ORCiD page to be added to the package CRAN webpage. + +### Using the DESCRIPTION file + +The DESCRIPTION file is the natural place to specify any metadata for an R package. The `codemetar` package can detect certain additional terms in the [CodeMeta context](https://codemeta.github.io/terms). Almost any additional codemeta field (see `codemetar:::additional_codemeta_terms` for a list) and can be added to and read from the DESCRIPTION into a `codemeta.json` file. + +CRAN requires that you prefix any additional such terms to indicate the use of `schema.org` explicitly, e.g. `keywords` would be specified in a DESCRIPTION file as: + + X-schema.org-keywords: metadata, codemeta, ropensci, citation, credit, linked-data + +Where applicable, these will override values otherwise guessed from the source repository. Use comma-separated lists to separate multiple values to a property, e.g. keywords. + +See the [DESCRIPTION](https://github.com/codemeta/codemetar/blob/master/DESCRIPTION) file of the `codemetar` package for an example. + +Going further +------------- + +Check out all the [codemetar vignettes](https://codemeta.github.io/codemetar/articles/index.html) for tutorials on other cool stuff you can do with codemeta and json-ld. + +[![ropensci\_footer](https://ropensci.org/public_images/ropensci_footer.png)](https://ropensci.org) diff --git a/inst/examples/README_codemetar_good.md b/inst/examples/README_codemetar_good.md new file mode 100644 index 00000000..7daf8adc --- /dev/null +++ b/inst/examples/README_codemetar_good.md @@ -0,0 +1,86 @@ + +[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active) [![Travis-CI Build Status](https://travis-ci.org/ropensci/codemetar.svg?branch=master)](https://travis-ci.org/ropensci/codemetar) [![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/csawpip238vvbd72/branch/master?svg=true)](https://ci.appveyor.com/project/cboettig/codemetar/branch/master) [![Coverage Status](https://img.shields.io/codecov/c/github/ropensci/codemetar/master.svg)](https://codecov.io/github/ropensci/codemetar?branch=master) [![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version/codemetar)](https://cran.r-project.org/package=codemetar) [![](http://badges.ropensci.org/130_status.svg)](https://github.com/ropensci/onboarding/issues/130) [![DOI](https://zenodo.org/badge/86626030.svg)](https://zenodo.org/badge/latestdoi/86626030) + + +codemetar +========= + +The goal of codemetar is to generate the JSON-LD file, `codemeta.json` containing software metadata describing an R package. For more general information about the CodeMeta Project for defining software metadata, see . In particular, new users might want to start with the [User Guide](https://codemeta.github.io/user-guide/), while those looking to learn more about JSON-LD and consuming existing codemeta files should see the [Developer Guide](https://codemeta.github.io/developer-guide/). + +Installation +------------ + +You can install the latest version from CRAN using: + +``` r +install.packages("codemetar") +``` + +You can also install the development version of `codemetar` from github with: + +``` r +# install.packages("devtools") +devtools::install_github("ropensci/codemetar") +``` + +``` r +library("codemetar") +``` + +Example +------- + +This is a basic example which shows you how to generate a `codemeta.json` for an R package (e.g. for `testthat`): + +``` r +write_codemeta("testthat") +``` + +`codemetar` can take the path to the package root instead. This may allow `codemetar` to glean some additional information that is not available from the description file alone. + +``` r +write_codemeta(".") +``` + +Which creates a file looking like so (first 10 lines; see full [codemeta.json here](https://github.com/codemeta/codemetar/blob/master/codemeta.json)): + + { + "@context": [ + "http://purl.org/codemeta/2.0", + "http://schema.org" + ], + "@type": "SoftwareSourceCode", + "identifier": "codemetar", + "description": "The 'Codemeta' Project defines a 'JSON-LD' format for describing\n software metadata, as detailed at . This package\n provides utilities to generate, parse, and modify 'codemeta.json' files \n automatically for R packages, as well as tools and examples for working with\n 'codemeta.json' 'JSON-LD' more generally.", + "name": "codemetar: Generate 'CodeMeta' Metadata for R Packages", + "issueTracker": "https://github.com/ropensci/codemetar/issues", + +Modifying or enriching CodeMeta metadata +---------------------------------------- + +The best way to ensure `codemeta.json` is as complete as possible is to begin by making full use of the fields that can be set in an R package DESCRIPTION file, such as `BugReports` and `URL`. Using the `Authors@R` notation allows a much richer specification of author roles, correct parsing of given vs family names, and email addresses. + +In the current implementation, developers may specify an ORCID url for an author in the optional `comment` field of `Authors@R`, e.g. + + Authors@R: person("Carl", "Boettiger", role=c("aut", "cre", "cph"), email="cboettig@gmail.com", comment="http://orcid.org/0000-0002-1642-628X") + +which will allow `codemetar` to associate an identifier with the person. If the package is hosted on CRAN, including the ORCiD in this way will cause an ORCiD logo and link to the ORCiD page to be added to the package CRAN webpage. + +### Using the DESCRIPTION file + +The DESCRIPTION file is the natural place to specify any metadata for an R package. The `codemetar` package can detect certain additional terms in the [CodeMeta context](https://codemeta.github.io/terms). Almost any additional codemeta field (see `codemetar:::additional_codemeta_terms` for a list) and can be added to and read from the DESCRIPTION into a `codemeta.json` file. + +CRAN requires that you prefix any additional such terms to indicate the use of `schema.org` explicitly, e.g. `keywords` would be specified in a DESCRIPTION file as: + + X-schema.org-keywords: metadata, codemeta, ropensci, citation, credit, linked-data + +Where applicable, these will override values otherwise guessed from the source repository. Use comma-separated lists to separate multiple values to a property, e.g. keywords. + +See the [DESCRIPTION](https://github.com/codemeta/codemetar/blob/master/DESCRIPTION) file of the `codemetar` package for an example. + +Going further +------------- + +Check out all the [codemetar vignettes](https://codemeta.github.io/codemetar/articles/index.html) for tutorials on other cool stuff you can do with codemeta and json-ld. + +[![ropensci\_footer](https://ropensci.org/public_images/ropensci_footer.png)](https://ropensci.org) diff --git a/inst/examples/README_usethis.md b/inst/examples/README_usethis.md new file mode 100644 index 00000000..503e1501 --- /dev/null +++ b/inst/examples/README_usethis.md @@ -0,0 +1,132 @@ + + + +# usethis + +[![Travis build status](https://travis-ci.org/r-lib/usethis.svg?branch=master)](https://travis-ci.org/r-lib/usethis) +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/r-lib/usethis?branch=master&svg=true)](https://ci.appveyor.com/project/r-lib/usethis) +[![Coverage status](https://codecov.io/gh/r-lib/usethis/branch/master/graph/badge.svg)](https://codecov.io/github/r-lib/usethis?branch=master) +[![CRAN status](http://www.r-pkg.org/badges/version/usethis)](https://cran.r-project.org/package=usethis) +[![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://www.tidyverse.org/lifecycle/#stable) + +The goal of usethis is to automate many common package and analysis +setup tasks. + +## Installation + +Install the released version of usethis from CRAN: + +``` r +install.packages("usethis") +``` + +Or install the development version from GitHub with: + +``` r +# install.packages("devtools") +devtools::install_github("r-lib/usethis") +``` + +## Usage + +Most `use_*()` functions operate on the *active project*. If you’ve just +used usethis to create a new package or project, that will be the +current project. Otherwise usethis tries to confirm that current working +directory can be recognized as a project. Use `proj_get()` and +`proj_set()` for manual intervention. Some functions have no strong +connections to projects and will expect you to provide a path. + +usethis is quite chatty, explaining what it’s doing and assigning you +tasks. `✔` indicates something usethis has done for you. `●` indicates +that you’ll need to do some work yourself. + +Below is a quick look at how usethis can help to set up a package. + +*Note: usethis is gaining more and more functionality for analytical +project that are not packages. Stay tuned.* + +``` r +library(usethis) + +# Create a new package ------------------------------------------------- +tmp <- file.path(tempdir(), "mypkg") +create_package(tmp) +#> Changing active project to mypkg +#> ✔ Creating 'R/' +#> ✔ Creating 'man/' +#> ✔ Writing 'DESCRIPTION' +#> ✔ Writing 'NAMESPACE' + +# Modify the description ---------------------------------------------- +use_mit_license("My Name") +#> ✔ Setting License field in DESCRIPTION to 'MIT + file LICENSE' +#> ✔ Writing 'LICENSE.md' +#> ✔ Adding '^LICENSE\\.md$' to '.Rbuildignore' +#> ✔ Writing 'LICENSE' + +use_package("MASS", "Suggests") +#> ✔ Adding 'MASS' to Suggests field in DESCRIPTION +#> ● Use `requireNamespace("MASS", quietly = TRUE)` to test if package is installed +#> ● Then use `MASS::fun()` to refer to functions. + +use_dev_package("callr") +#> ✔ Adding 'callr' to Imports field in DESCRIPTION +#> ● Refer to functions with `callr::fun()` +#> ✔ Adding 'r-lib/callr' to DESCRIPTION Remotes + +# Set up various packages --------------------------------------------- +use_roxygen_md() +#> ✔ Setting Roxygen field in DESCRIPTION to 'list(markdown = TRUE)' +#> ✔ Setting RoxygenNote field in DESCRIPTION to '6.0.1.9000' +#> ● Re-document + +use_rcpp() +#> ✔ Adding 'Rcpp' to LinkingTo field in DESCRIPTION +#> ✔ Adding 'Rcpp' to Imports field in DESCRIPTION* +#> ✔ Creating 'src/' +#> ✔ Adding '*.o', '*.so', '*.dll' to 'src/.gitignore' +#> ● Include the following roxygen tags somewhere in your package +#> #' @useDynLib mypkg, .registration = TRUE +#> #' @importFrom Rcpp sourceCpp +#> NULL +#> ● Run document() + +use_revdep() +#> ✔ Creating 'revdep/' +#> ✔ Adding '^revdep$' to '.Rbuildignore' +#> ✔ Adding 'checks', 'library', 'checks.noindex', 'library.noindex', 'data.sqlite', '*.html' to 'revdep/.gitignore' +#> ✔ Writing 'revdep/email.yml' +#> ● Run checks with `revdepcheck::revdep_check(num_workers = 4)` + +# Set up other files ------------------------------------------------- +use_readme_md() +#> ✔ Writing 'README.md' + +use_news_md() +#> ✔ Writing 'NEWS.md' +#> ● Edit 'NEWS.md' + +use_test("my-test") +#> ✔ Adding 'testthat' to Suggests field in DESCRIPTION +#> ✔ Creating 'tests/testthat/' +#> ✔ Writing 'tests/testthat.R' +#> ✔ Writing 'tests/testthat/test-my-test.R' +#> ● Edit 'test-my-test.R' + +x <- 1 +y <- 2 +use_data(x, y) +#> ✔ Creating 'data/' +#> ✔ Saving x to data/x.rda +#> ✔ Saving y to data/y.rda + +# Use git ------------------------------------------------------------ +use_git() +#> ✔ Initialising Git repo +#> ✔ Adding '.Rhistory', '.RData', '.Rproj.user' to './.gitignore' +#> ✔ Adding files and committing +``` + +Please note that this project is released with a [Contributor Code of +Conduct](.github/CODE_OF_CONDUCT.md). By participating in this project +you agree to abide by its terms. diff --git a/inst/extdata/licenses.R b/inst/extdata/licenses.R deleted file mode 100644 index 3dc7a8c7..00000000 --- a/inst/extdata/licenses.R +++ /dev/null @@ -1,12 +0,0 @@ - - -cran <- tools:::R_license_db() -resp <- httr::GET(paste0("https://github.com/spdx/license-list/", - "blob/master/spdx_licenselist_v2.6.xls?raw=true")) -writeBin(httr::content(resp,as="raw"), "spdx.xls") -spdx <- readxl::read_excel("spdx.xls") - -## Manual synthesis happens here ## - -cran_to_spdx <- readr::read_csv("data-raw/cran-to-spdx.csv") -devtools::use_data(cran_to_spdx, overwrite = TRUE) diff --git a/inst/templates/description-codemetajson-pre-commit.sh b/inst/templates/description-codemetajson-pre-commit.sh new file mode 100644 index 00000000..4491aa46 --- /dev/null +++ b/inst/templates/description-codemetajson-pre-commit.sh @@ -0,0 +1,12 @@ +#!/bin/bash +DESCRIPTION=($(git diff --cached --name-only | grep 'DESCRIPTION')) +MSG="use 'git commit --no-verify' to override this check" + +if [[ ${#DESCRIPTION[@]} == 0 ]]; then + exit 0 +fi + +if [[ DESCRIPTION -nt codemeta.json ]]; then + echo -e "codemeta.json is out of date; please re-run codemetar::write_codemeta\n$MSG" + exit 1 +fi diff --git a/man/codemetar-package.Rd b/man/codemetar-package.Rd index 59ba4504..433a6329 100644 --- a/man/codemetar-package.Rd +++ b/man/codemetar-package.Rd @@ -38,7 +38,7 @@ Developer Guide (https://codemeta.github.io/developer-guide/). \seealso{ Useful links: \itemize{ - \item \url{https://github.com/ropensci/codemetar} + \item \url{https://github.com/ropensci/codemetar,https://codemeta.github.io/codemetar} \item Report bugs at \url{https://github.com/ropensci/codemetar/issues} } @@ -46,10 +46,19 @@ Useful links: \author{ \strong{Maintainer}: Carl Boettiger \email{cboettig@gmail.com} (0000-0002-1642-628X) [copyright holder] +Authors: +\itemize{ + \item Maëlle Salmon (0000-0002-2815-0399) [contributor] +} + Other contributors: \itemize{ - \item Anna Krystalli (0000-0002-2378-4915) [NA] - \item Toph Allen (0000-0003-4580-091X) [NA] + \item Anna Krystalli (0000-0002-2378-4915) [reviewer, contributor] + \item Toph Allen (0000-0003-4580-091X) [reviewer] + \item rOpenSci (https://ropensci.org/) [funder] + \item Katrin Leinweber [contributor] + \item Noam Ross [contributor] + \item Arfon Smith [contributor] } } diff --git a/man/create_codemeta.Rd b/man/create_codemeta.Rd index 93174304..803da10b 100644 --- a/man/create_codemeta.Rd +++ b/man/create_codemeta.Rd @@ -5,7 +5,8 @@ \title{create_codemeta} \usage{ create_codemeta(pkg = ".", root = ".", id = NULL, - force_update = getOption("codemeta_force_update", TRUE), ...) + force_update = getOption("codemeta_force_update", TRUE), verbose = TRUE, + ...) } \arguments{ \item{pkg}{package path to package root, or package name, or @@ -18,6 +19,8 @@ to package root. Default guess is current dir.} \item{force_update}{Update guessed fields even if they are defined in an existing codemeta.json file} +\item{verbose}{Whether to print messages indicating opinions e.g. when DESCRIPTION has no URL. See \code{\link{give_opinions}}.} + \item{...}{additional arguments to \code{\link{write_json}}} } \value{ diff --git a/man/extract_badges.Rd b/man/extract_badges.Rd new file mode 100644 index 00000000..808e4561 --- /dev/null +++ b/man/extract_badges.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{extract_badges} +\alias{extract_badges} +\title{Extract all badges from Markdown file} +\usage{ +extract_badges(path) +} +\arguments{ +\item{path}{Path to Markdown file} +} +\value{ +A tibble with for each badge its text, link and link to +its image. +} +\description{ +Extract all badges from Markdown file +} diff --git a/man/give_opinions.Rd b/man/give_opinions.Rd new file mode 100644 index 00000000..b46246ca --- /dev/null +++ b/man/give_opinions.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/give_opinions.R +\name{give_opinions} +\alias{give_opinions} +\title{Function giving opinions about a package} +\usage{ +give_opinions(pkg_path = getwd()) +} +\arguments{ +\item{pkg_path}{Path to the package root} +} +\value{ +A data.frame of opinions +} +\description{ +Function giving opinions about a package +} diff --git a/man/write_codemeta.Rd b/man/write_codemeta.Rd index 855759d4..b8e66e76 100644 --- a/man/write_codemeta.Rd +++ b/man/write_codemeta.Rd @@ -5,7 +5,8 @@ \title{write_codemeta} \usage{ write_codemeta(pkg = ".", path = "codemeta.json", root = ".", id = NULL, - force_update = getOption("codemeta_force_update", TRUE), ...) + force_update = getOption("codemeta_force_update", TRUE), verbose = TRUE, + ...) } \arguments{ \item{pkg}{package path to package root, or package name, or @@ -20,6 +21,8 @@ to package root. Default guess is current dir.} \item{force_update}{Update guessed fields even if they are defined in an existing codemeta.json file} +\item{verbose}{Whether to print messages indicating opinions e.g. when DESCRIPTION has no URL. See \code{\link{give_opinions}}.} + \item{...}{additional arguments to \code{\link{write_json}}} } \value{ @@ -37,7 +40,13 @@ If pkg is a codemeta object, the function will attempt to update any fields it can guess (i.e. from the DESRIPTION file), overwriting any existing data in that block. In this case, the package root directory should be the current working directory. + +When creating and writing a codemeta.json for the first time, +the function adds "codemeta.json" to .Rbuildignore and, if the +project uses git, adds a pre-commit hook ensuring that if DESCRIPTION changes, + the codemeta.json will be updated as well unless the DESCRIPTION change is committed + with 'git commit --no-verify'. } \examples{ -write_codemeta("codemeta", tempfile()) +write_codemeta("codemetar", tempfile()) } diff --git a/tests/testthat/test-cloned-repo.R b/tests/testthat/test-cloned-repo.R index abff0313..0223d23c 100644 --- a/tests/testthat/test-cloned-repo.R +++ b/tests/testthat/test-cloned-repo.R @@ -7,6 +7,7 @@ testthat::test_that("we can generate codemeta { skip_on_cran() + unlink("codemetar_copy", recursive=TRUE) git2r::clone("https://github.com/ropensci/codemetar", "codemetar_copy", progress = FALSE) diff --git a/tests/testthat/test-codemeta_description.R b/tests/testthat/test-codemeta_description.R index 72f3e7f2..0afb739c 100644 --- a/tests/testthat/test-codemeta_description.R +++ b/tests/testthat/test-codemeta_description.R @@ -5,8 +5,37 @@ testthat::test_that("We can use a preset id", { codemeta_description(f, id = "https://doi.org/10.looks.like/doi") }) + +testthat::test_that("several URLs", { + f <- system.file("examples/DESCRIPTION_two_URLs", package = "codemetar") + cm <- codemeta_description(f) + expect_equal(cm$codeRepository, "https://github.com/ropensci/essurvey") + expect_true("https://ropensci.github.io/essurvey/" %in% + cm$relatedLink) +}) + +testthat::test_that("We can parse additional terms", { + f <- system.file("examples/DESCRIPTION_ex1.dcf", package = "codemetar") + cm <- codemeta_description(f) + testthat::expect_equal(length(cm$keywords), 6) + testthat::expect_equal(cm$isPartOf, "https://ropensci.org") + }) + testthat::test_that("We can parse plain Authors: & Maintainers: entries", { f <- system.file("examples/DESCRIPTION_ex1.dcf", package = "codemetar") - codemeta_description(f) + authors <- codemeta_description(f) + expect_true(authors$maintainer$familyName == "Boettiger") + expect_equal(length(authors$author), 0) + f <- system.file("examples/example.dcf", package = "codemetar") + authors <- codemeta_description(f) + expect_true(authors$maintainer$familyName == "Developer") + expect_equal(length(authors$author), 2) + + f <- system.file("examples/DESCRIPTION_jsonlite", package = "codemetar") + authors <- codemeta_description(f) + expect_true(authors$maintainer$familyName == "Ooms") + expect_equal(length(authors$author), 2) + + }) diff --git a/tests/testthat/test-codemeta_validate.R b/tests/testthat/test-codemeta_validate.R index fb602a8f..28c32f9d 100644 --- a/tests/testthat/test-codemeta_validate.R +++ b/tests/testthat/test-codemeta_validate.R @@ -8,9 +8,9 @@ testthat::test_that("we can validate this file", { }) -testthat::test_that("we can create & validate codemeta for testthat package", { +testthat::test_that("we can create & validate codemeta for usethis package", { skip_on_cran() - write_codemeta("testthat") + write_codemeta("usethis") testthat::expect_true(codemeta_validate("codemeta.json")) unlink("codemeta.json") diff --git a/tests/testthat/test-give_opinions.R b/tests/testthat/test-give_opinions.R new file mode 100644 index 00000000..cdd422a6 --- /dev/null +++ b/tests/testthat/test-give_opinions.R @@ -0,0 +1,48 @@ +context("test-give_opinions.R") +testthat::test_that("Silent if ok URLs", { + f <- system.file("examples/DESCRIPTION_HIBPwned", package = "codemetar") + desc_fixmes <- give_opinions_desc(f) + expect_null(desc_fixmes) +}) + + +testthat::test_that("Message if no URL", { + f <- system.file("examples/DESCRIPTION_no_URL", package = "codemetar") + desc_fixmes <- give_opinions_desc(f) + expect_is(desc_fixmes, "data.frame") + expect_equal(desc_fixmes$where, "DESCRIPTION") + expect_true(any(grepl("URL", desc_fixmes$fixme))) +}) + +testthat::test_that("Message if no BugReports", { + f <- system.file("examples/DESCRIPTION_no_bugreports", package = "codemetar") + desc_fixmes <- give_opinions_desc(f) + expect_is(desc_fixmes, "data.frame") + expect_equal(desc_fixmes$where[1], "DESCRIPTION") + expect_true(any(grepl("BugReports", desc_fixmes$fixme))) +}) + +testthat::test_that("No message if ok description",{ + f <- system.file("examples/DESCRIPTION_Rforge", package = "codemetar") + expect_null(give_opinions_desc(f)) +}) + +test_that("Message if bad URLS", { + f <- system.file("examples/DESCRIPTION_wrongURLS", package = "codemetar") + desc_fixmes <- give_opinions_desc(f) + expect_true(any(grepl("Problematic URLs", desc_fixmes$fixme))) +}) + +test_that("badges in README", { + f <- system.file("examples/README_codemetar_bad.md", package = "codemetar") + readme_fixmes <- give_opinions_readme(f, "codemetar") + expect_equal(nrow(readme_fixmes), 2) + expect_true(any(grepl("status", + readme_fixmes$fixme))) + expect_true(any(grepl("CRAN", + readme_fixmes$fixme))) + + readme_fixmes <- give_opinions_readme(f, "a4") + expect_true(any(grepl("BioConductor", + readme_fixmes$fixme))) +}) diff --git a/tests/testthat/test-guess_metadata.R b/tests/testthat/test-guess_metadata.R index 6178b0e4..b817c133 100644 --- a/tests/testthat/test-guess_metadata.R +++ b/tests/testthat/test-guess_metadata.R @@ -17,12 +17,16 @@ testthat::test_that("guess_provider",{ testthat::test_that("guess_ci",{ f <- system.file("examples/README_ex.md", package="codemetar") a <- guess_ci(f) - expect_gt(length(a), 0) + testthat::expect_equal(a, "https://travis-ci.org/codemeta/codemetar") f2 <- system.file("examples/README_ex2.md", package="codemetar") a2 <- guess_ci(f2) expect_null(a2) + f <- system.file("examples/README_usethis.md", package="codemetar") + a3 <- guess_ci(f) + testthat::expect_equal(length(a3), 2) + testthat::expect_equal(a3[1], "https://travis-ci.org/r-lib/usethis") }) @@ -32,7 +36,15 @@ testthat::test_that("guess_ci",{ testthat::test_that("guess_devStatus",{ f <- system.file("examples/README_ex.md", package="codemetar") a <- guess_devStatus(f) - expect_gt(length(a), 0) + expect_equal(a, "http://www.repostatus.org/#wip") + + f <- system.file("examples/README_usethis.md", package="codemetar") + a <- guess_devStatus(f) + expect_equal(a, "https://www.tidyverse.org/lifecycle/#stable") + + f <- system.file("examples/README_codemetar_bad.md", package="codemetar") + a <- guess_devStatus(f) + expect_null(a) f2 <- system.file("examples/README_ex2.md", package="codemetar") a2 <- guess_devStatus(f2) diff --git a/tests/testthat/test-parse_depends.R b/tests/testthat/test-parse_depends.R index 66883544..673867d7 100644 --- a/tests/testthat/test-parse_depends.R +++ b/tests/testthat/test-parse_depends.R @@ -2,23 +2,43 @@ testthat::context("parse_depends.R") testthat::test_that("Test the various cases for dependencies", { - a <- parse_depends(NULL) - testthat::expect_length(a, 0) - a <- parse_depends(deps = "a4") # BIOC provider - testthat::expect_gt(length(a[[1]]), 1) - a <- parse_depends(deps = "httr") # CRAN provider - testthat::expect_gt(length(a[[1]]), 1) - a <- parse_depends(deps = "R") - testthat::expect_equal(a[[1]]$name, "R") - a <- parse_depends(deps = "not-a-package") + testthat::expect_error(format_depend(NULL)) + a <- format_depend(package = "a4", + version = "*", + remote_provider = "") # BIOC provider + testthat::expect_equal(a$sameAs, "https://bioconductor.org/packages/release/bioc/html/a4.html") + + + testthat::expect_equal(a$provider$`@id`, "https://www.bioconductor.org") + + a <- format_depend(package = "httr", + version = "*", + remote_provider = "") # CRAN provider + testthat::expect_equal(a$sameAs, "https://CRAN.R-project.org/package=httr") + + a <- format_depend(package = "desc", + version = "*", + remote_provider = "r-lib/desc") # CRAN provider + testthat::expect_equal(a$sameAs, "https://github.com/r-lib/desc") + + f <- system.file("examples/DESCRIPTION_with_remote", package = "codemetar") + descr <- codemeta_description(f) + testthat::expect_equal(descr$softwareRequirements[6, 1][[1]], + "https://github.com/hadley/readr") + + testthat::expect_equal(a$provider$`@id`, "https://cran.r-project.org") + a <- format_depend(package = "R", + version = ">= 3.0.0", + remote_provider = "") + testthat::expect_equal(a$name, "R") }) testthat::test_that("Test the various cases for ids (NOT used currently)", { - a <- guess_dep_id(parse_depends("a4")[[1]]) # BIOC provider - a <- guess_dep_id(parse_depends("httr")[[1]]) # CRAN provider - a <- guess_dep_id(parse_depends("R")[[1]]) - a <- guess_dep_id(parse_depends("not-a-package")[[1]]) + # a <- guess_dep_id(parse_depends("a4")[[1]]) # BIOC provider + # a <- guess_dep_id(parse_depends("httr")[[1]]) # CRAN provider + # a <- guess_dep_id(parse_depends("R")[[1]]) + # a <- guess_dep_id(parse_depends("not-a-package")[[1]]) }) diff --git a/tests/testthat/test-write_codemeta.R b/tests/testthat/test-write_codemeta.R index 20589d26..a0e07be3 100644 --- a/tests/testthat/test-write_codemeta.R +++ b/tests/testthat/test-write_codemeta.R @@ -26,60 +26,40 @@ testthat::test_that("we can write a codemeta document given a package name", { testthat::test_that("We can read an existing codemeta.json file", { - write_codemeta(system.file("DESCRIPTION", package = "codemetar")) - write_codemeta(".") + write_codemeta("codemetar") testthat::expect_true(file.exists("codemeta.json")) + write_codemeta("codemetar") unlink("codemeta.json") }) - testthat::test_that("We can use either a path or pkg name in writing", { - write_codemeta(system.file("DESCRIPTION", package = "codemetar")) + + write_codemeta(path.package("codemetar")) testthat::expect_true(file.exists("codemeta.json")) unlink("codemeta.json") -}) + write_codemeta("codemetar") + testthat::expect_true(file.exists("codemeta.json")) + unlink("codemeta.json") -## Test that we can write codemeta from a temp working dir (e.g. non-root dir) -testthat::test_that("we can write codemeta given a codemeta object", { - codemeta <- new_codemeta() - create_codemeta(codemeta) }) -##(author test below includes such a step already) +testthat::test_that("We can deduce relatedLink from installed pkg", { + skip_on_travis() + usethis_cm <- create_codemeta(find.package("usethis")) + testthat::expect_equal(usethis_cm$relatedLink, + "https://CRAN.R-project.org/package=usethis") +}) -testthat::test_that("We can parse author lists - that use Authors@R, Authors, or both", { - dcf <- system.file("examples/example.dcf", package = "codemetar") - descr <- as.list(read.dcf(dcf)[1, ]) +## Test that we can write codemeta from a temp working dir (e.g. non-root dir) +testthat::test_that("we can write codemeta given a codemeta object", { codemeta <- new_codemeta() - codemeta <- - parse_people(eval(parse(text = descr$`Authors@R`)), codemeta) - - ## Tests that we can write codemeta given an existing codemeta - ## object, expects a warning - write_codemeta(codemeta, path = "test.json") - - testthat::expect_true(file.exists("test.json")) - - codemeta2 <- codemeta - descr2 <- descr - descr2$`Authors@R` <- NULL - - ## parse without Authors@R: - codemeta2 <- parse_people(as.person(descr2$Author), codemeta2) - codemeta2$maintainer <- - person_to_schema(as.person(descr2$Maintainer)) - - write_codemeta(codemeta2, path = "test2.json") - testthat::expect_true(file.exists("test2.json")) - + expect_is(create_codemeta("codemetar", codemeta), "list") +}) - unlink("test.json") - unlink("test2.json") +##(author test below includes such a step already) -}) diff --git a/vignettes/A-codemeta-intro.Rmd b/vignettes/A-codemeta-intro.Rmd index fc2f1c58..26748247 100644 --- a/vignettes/A-codemeta-intro.Rmd +++ b/vignettes/A-codemeta-intro.Rmd @@ -82,7 +82,7 @@ write_codemeta("testthat") `codemetar` can take the path to the package root instead. This may allow `codemetar` to glean some additional information that is not available from the description file alone. ```{r} -write_codemeta(".") +write_codemeta(find.package("codemetar")) ``` ```{r echo = FALSE}