diff --git a/DESCRIPTION b/DESCRIPTION index 64119c23..597c9015 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -58,7 +58,8 @@ Suggests: BiocStyle, BiocPkgTools, biocViews, - reticulate + reticulate, + rvest VignetteBuilder: knitr License: GPL-3 Config/testthat/edition: 3 diff --git a/R/check_cont.R b/R/check_cont.R index 4d7fa9e1..abaf920c 100644 --- a/R/check_cont.R +++ b/R/check_cont.R @@ -3,42 +3,34 @@ #' Check that a list of containers are valid. #' @inheritParams construct_runners #' @param domain Container repository API domain. -#' @returns NUll +#' @returns Null #' -#' @keywords internal -check_cont <- function(cont, - domain = "https://hub.docker.com/v2/repositories/", +#' @keywords internal +check_cont <- function(cont, verbose = TRUE){ + # devoptera::args2vars(check_cont) + # cont <- list("ghcr.io/bioconductor/bioconductor_docker:devel" ) - requireNamespace("jsonlite") + n_parts <- check_cont_general(cont) for(co in cont){ if(is.null(co)) next() - if(isFALSE(grepl("/",co))){ - stopper("Container must be specificied in the following format:", - "'owner/repo:tag'") - } - splt <- strsplit(co,":")[[1]] - URL <- paste(domain, splt[1], - "tags?page_size=1000",sep="/" - ) - #### Check repo exists #### - messager("Checking public container repository exists:",shQuote(splt[1]), - v=verbose) - js <- tryCatch({ - jsonlite::read_json(URL) - }, error=function(e){ - stopper("Unable to find public container:",co) - } - ) - #### Check tag exists #### - if(length(splt)==2){ - messager("Checking tag exists:",shQuote(splt[2]), - v=verbose) - tags <- sapply(js$results ,function(x){x$name}) - if(!splt[2] %in% tags){ - stopper("The tag", shQuote(splt[2]), - "does not exist in the repo",shQuote(splt[1])) - } + #### Check container #### + if(grepl("ghcr.io",co)){ + check_cont_ghcr(cont = co, + verbose = verbose) + } else if(grepl("docker.io",cont)){ + check_cont_dockerhub(cont = co, + verbose = verbose) + } else if(n_parts==2){ + messager("Assuming container is on DockerHub.",v=verbose) + check_cont_dockerhub(cont = co, + verbose = verbose) + } else { + warning(paste( + "Unable to check registry for container",co, + "Skipping check." + )) } } + } diff --git a/R/check_cont_dockerhub.R b/R/check_cont_dockerhub.R new file mode 100644 index 00000000..b38c523b --- /dev/null +++ b/R/check_cont_dockerhub.R @@ -0,0 +1,27 @@ +check_cont_dockerhub <- function(cont, + domain = + "https://hub.docker.com/v2/repositories/", + verbose = TRUE){ + requireNamespace("jsonlite") + for(co in cont){ + if(is.null(co)) next() + co <- gsub("docker\\.io/","",co) + splt <- strsplit(co,":")[[1]] + URL <- paste(domain, splt[1], + "tags?page_size=1000",sep="/" + ) + #### Check repo exists #### + messager("Checking public container repository exists:",shQuote(splt[1]), + v=verbose) + js <- tryCatch({ + jsonlite::read_json(URL) + }, error=function(e){ + stopper("Unable to find public container:",co) + }) + #### Check tag exists #### + tags <- sapply(js$results ,function(x){x$name}) + check_tags(tags = tags, + splt = splt, + verbose = verbose) + } +} \ No newline at end of file diff --git a/R/check_cont_general.R b/R/check_cont_general.R new file mode 100644 index 00000000..094142ff --- /dev/null +++ b/R/check_cont_general.R @@ -0,0 +1,11 @@ +check_cont_general <- function(cont){ + lapply(cont, function(co){ + if(is.null(co)) return(NULL) + if(!grepl("/",co)){ + stopper("Container must be specified in the following format:", + "'registry/owner/repo:tag'") + } + n_parts <- length(strsplit(gsub("[/]+","/",co),"/")[[1]]) + return(n_parts) + }) +} \ No newline at end of file diff --git a/R/check_cont_ghcr.R b/R/check_cont_ghcr.R new file mode 100644 index 00000000..89a4afa5 --- /dev/null +++ b/R/check_cont_ghcr.R @@ -0,0 +1,40 @@ +check_cont_ghcr <- function(cont, + verbose = TRUE){ + requireNamespace("rvest") + + for(co in cont){ + if(is.null(co)) next() + # co <- "ghcr.io/bioconductor/bioconductor_docker:devel" + co_notag <- gsub("ghcr.io/","",strsplit(co,":")[[1]][1]) + ##### Only works if you have permissions to the repo ##### + # get_ghcr_tags <- function(container) { + # url <- paste0("https://ghcr.io/v2/", container, "/tags/list") + # response <- jsonlite::fromJSON(url) + # tags <- response$tags + # return(tags) + # } + # tags <- get_ghcr_tags("bioconductor/bioconductor_docker") + # + URL <- paste0( + "https://github.com/", + co_notag, + "/pkgs/container/",basename(co_notag), + "/versions?filters%5Bversion_type%5D=tagged" + ) + #### Check if container exists #### + x <- tryCatch({ + rvest::read_html(URL) + }, error=function(e){ + stopper("Unable to find public container:",co)} + ) + #### Check if container has tags #### + tags <- x |> rvest::html_nodes(".Label") |> rvest::html_text() + if(length(tags)==0){ + stopper("Unable to find tags for container:",co) + } else { + check_tags(tags = tags, + splt = strsplit(co,":")[[1]][1], + verbose = verbose) + } + } +} \ No newline at end of file diff --git a/R/check_tags.R b/R/check_tags.R new file mode 100644 index 00000000..8f8bfa33 --- /dev/null +++ b/R/check_tags.R @@ -0,0 +1,12 @@ +check_tags <- function(splt, + tags, + verbose=TRUE){ + if(length(splt)==2){ + messager("Checking tag exists:",shQuote(splt[2]), + v=verbose) + if(!splt[2] %in% tags){ + stopper("The tag", shQuote(splt[2]), + "does not exist in the repo",shQuote(splt[1])) + } + } +} \ No newline at end of file diff --git a/R/construct_cont.R b/R/construct_cont.R index 56bc7bac..59e04226 100644 --- a/R/construct_cont.R +++ b/R/construct_cont.R @@ -7,6 +7,7 @@ #' \itemize{ #' \item{"ghcr.io/" : }{\href{https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry}{GitHub Container Registry}} #' \item{"docker.io/" : }{\href{https://hub.docker.com/}{DockerHub}} +#' \item{"mcr.microsoft.com/" : }{\href{https://github.com/microsoft/ContainerRegistry}{Microsoft Container Registry}} #' } #' @param default_cont The DockerHub container to default to. #' Used when it's detected that only the tag has been given in one or more @@ -20,7 +21,8 @@ #' @examples #' cont <- construct_cont() construct_cont <- function(default_registry = c("ghcr.io/", - "docker.io/"), + "docker.io/", + "mcr.microsoft.com/"), default_cont = "bioconductor/bioconductor_docker", default_tag = "devel", cont = list( diff --git a/man/check_cont.Rd b/man/check_cont.Rd index dc1562ff..9ec606e1 100644 --- a/man/check_cont.Rd +++ b/man/check_cont.Rd @@ -4,11 +4,7 @@ \alias{check_cont} \title{Check containers exist} \usage{ -check_cont( - cont, - domain = "https://hub.docker.com/v2/repositories/", - verbose = TRUE -) +check_cont(cont, verbose = TRUE) } \arguments{ \item{cont}{Which Docker container to use on each OS @@ -17,12 +13,12 @@ See \href{https://hub.docker.com/r/bioconductor/bioconductor_docker/tags}{here} for a list of all official Bioconductor Docker container versions.} -\item{domain}{Container repository API domain.} - \item{verbose}{Print messages.} + +\item{domain}{Container repository API domain.} } \value{ -NUll +Null } \description{ Check that a list of containers are valid. diff --git a/man/construct_cont.Rd b/man/construct_cont.Rd index b45c4d48..1e171e00 100644 --- a/man/construct_cont.Rd +++ b/man/construct_cont.Rd @@ -5,7 +5,7 @@ \title{Construct containers list} \usage{ construct_cont( - default_registry = c("ghcr.io/", "docker.io/"), + default_registry = c("ghcr.io/", "docker.io/", "mcr.microsoft.com/"), default_cont = "bioconductor/bioconductor_docker", default_tag = "devel", cont = list(paste(default_cont, default_tag, sep = ":"), NULL, NULL), @@ -20,6 +20,7 @@ Options include: \itemize{ \item{"ghcr.io/" : }{\href{https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry}{GitHub Container Registry}} \item{"docker.io/" : }{\href{https://hub.docker.com/}{DockerHub}} + \item{"mcr.microsoft.com/" : }{\href{https://github.com/microsoft/ContainerRegistry}{Microsoft Container Registry}} }} \item{default_cont}{The DockerHub container to default to. diff --git a/tests/testthat/test-check_cont.R b/tests/testthat/test-check_cont.R index 337d33e2..00e481f6 100644 --- a/tests/testthat/test-check_cont.R +++ b/tests/testthat/test-check_cont.R @@ -17,4 +17,16 @@ test_that("check_cont works", { testthat::expect_error( check_cont(cont = "typooooo") ) + + #### Include registry #### + testthat::expect_no_warning( + check_cont(cont = "ghcr.io/bioconductor/bioconductor_docker") + ) + + testthat::expect_no_warning( + check_cont(cont = "docker.io/bioconductor/bioconductor_docker:devel") + ) + testthat::expect_warning( + check_cont(cont = "mcr.microsoft.com/bioconductor/bioconductor_docker:devel") + ) }) diff --git a/tests/testthat/test-construct_cont.R b/tests/testthat/test-construct_cont.R index 590e5c61..dc6107cc 100644 --- a/tests/testthat/test-construct_cont.R +++ b/tests/testthat/test-construct_cont.R @@ -1,12 +1,16 @@ test_that("construct_cont works", { cont1 <- construct_cont() - testthat::expect_equal(cont1[[1]], "bioconductor/bioconductor_docker:devel") + default_registry <- eval(formals(construct_cont)$default_registry)[1] + default_tag <- eval(formals(construct_cont)$default_tag)[1] + testthat::expect_equal(cont1[[1]], + paste0(opts,"bioconductor/bioconductor_docker:",default_tag)) testthat::expect_null(cont1[[2]]) testthat::expect_null(cont1[[3]]) cont2 <- construct_cont(cont = "devel") - testthat::expect_equal(cont2[[1]], "bioconductor/bioconductor_docker:devel") + testthat::expect_equal(cont2[[1]], + paste0(opts,"bioconductor/bioconductor_docker:",default_tag)) cont3 <- construct_cont(versions_explicit = TRUE) testthat::expect_true(grepl("bioconductor/bioconductor_docker:RELEASE_*", @@ -16,7 +20,7 @@ test_that("construct_cont works", { cont4 <- construct_cont(default_tag = "release", run_check_cont = TRUE) - testthat::expect_equal(cont4[[1]], "bioconductor/bioconductor_docker:latest") + testthat::expect_equal(cont4[[1]], paste0(opts,"bioconductor/bioconductor_docker:latest")) testthat::expect_null(cont4[[2]]) testthat::expect_null(cont4[[3]]) })