From 29847273b09c4b96dfceee13c67b92e14b302846 Mon Sep 17 00:00:00 2001 From: vincent guyader Date: Sun, 29 Oct 2023 19:40:37 +0100 Subject: [PATCH] New1 (#63) * add Dockerfile parser (#55) * add Dockerfile parser * add parsing test * add flag to dock_from_renv function (#60) * add keep_renv_version flag to dock_from_renv function and associated test * fixed typo * Update dock_from_renv.R * improve dock_from_renv * flat_ file correction * it's always a typo --------- Co-authored-by: Josiah Parry Co-authored-by: Adam J Campbell --- DESCRIPTION | 5 +- NAMESPACE | 1 + NEWS.md | 4 + R/dock_from_renv.R | 46 +-- R/dockerignore.R | 8 +- R/gen_base_image.R | 21 ++ R/parse-dockerfile.R | 48 +++ dev/config_fusen.yaml | 15 + dev/flat_dock_from_renv.Rmd | 460 +++++++++++++++++++++-- man/dock_from_renv.Rd | 6 +- man/docker_ignore_add.Rd | 2 +- man/gen_base_image.Rd | 12 - man/parse_dockerfile.Rd | 21 ++ tests/testthat/test-dock_from_renv.R | 62 +++ tests/testthat/test-dockerfile-parsing.R | 10 + vignettes/dockerfile-from-renv-lock.Rmd | 27 +- 16 files changed, 679 insertions(+), 69 deletions(-) create mode 100644 R/gen_base_image.R create mode 100644 R/parse-dockerfile.R create mode 100644 dev/config_fusen.yaml delete mode 100644 man/gen_base_image.Rd create mode 100644 man/parse_dockerfile.Rd create mode 100644 tests/testthat/test-dockerfile-parsing.R diff --git a/DESCRIPTION b/DESCRIPTION index e3adace..0e00572 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: dockerfiler Title: Easy Dockerfile Creation from R -Version: 0.2.1.9002 +Version: 0.2.1.9003 Authors@R: c( person("Colin", "Fay", , "contact@colinfay.me", role = c("cre", "aut"), comment = c(ORCID = "0000-0001-7343-1846")), @@ -36,8 +36,9 @@ Suggests: rmarkdown (>= 2.6), testthat (>= 3.0.0), withr -VignetteBuilder: +VignetteBuilder: knitr +Config/fusen/version: 0.5.2.9000 Config/testthat/edition: 3 Encoding: UTF-8 LazyData: true diff --git a/NAMESPACE b/NAMESPACE index ac7d2fa..a724b58 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,7 @@ export(dock_from_desc) export(dock_from_renv) export(docker_ignore_add) export(get_sysreqs) +export(parse_dockerfile) export(r) importFrom(R6,R6Class) importFrom(attempt,map_try_catch) diff --git a/NEWS.md b/NEWS.md index bb58064..9a6605c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,10 @@ - feat: Added `dock_from_renv()`, to create a Dockerfile from a renv.lock file (@JosiahParry, @statnmap) +- feat: Added `parse_dockerfile()`, to Create a Dockerfile object from a Dockerfile (@JosiahParry) + +- feat: Added `fix_renv_version` boolean parameter to `dock_from_renv` to be able to fix the renv version to use during `renv::restore()` + - fix: the dedicated `compact_sysreqs` function allow to deal with 'complex' sysreqs, such as chromimum installation - feat: add jammy ubuntu distro in available distro diff --git a/R/dock_from_renv.R b/R/dock_from_renv.R index b609d66..006a164 100644 --- a/R/dock_from_renv.R +++ b/R/dock_from_renv.R @@ -1,3 +1,5 @@ +# WARNING - Generated by {fusen} from dev/flat_dock_from_renv.Rmd: do not edit by hand + # ignoring opensuse for the time being available_distros <- c( "xenial", @@ -22,6 +24,8 @@ available_distros <- c( #' @param repos character. The URL(s) of the repositories to use for `options("repos")`. #' @param extra_sysreqs character vector. Extra debian system requirements. #' Will be installed with apt-get install. +#' @param fix_renv_version boolean. If `TRUE` the version of renv in the lockfile +#' will be used for the `renv::restore()` command #' @importFrom utils getFromNamespace #' @return A R6 object of class `Dockerfile`. #' @details @@ -38,12 +42,13 @@ available_distros <- c( #' @importFrom attempt map_try_catch #' @importFrom glue glue #' @importFrom pak pkg_system_requirements + +#' @export #' @examples #' \dontrun{ #' dock <- dock_from_renv("renv.lock", distro = "xenial") #' dock$write("Dockerfile") #' } -#' @export dock_from_renv <- function( lockfile = "renv.lock", distro = "focal", @@ -52,7 +57,8 @@ dock_from_renv <- function( sysreqs = TRUE, repos = c(CRAN = "https://cran.rstudio.com/"), expand = FALSE, - extra_sysreqs = NULL + extra_sysreqs = NULL, + fix_renv_version = FALSE ) { distro <- match.arg(distro, available_distros) @@ -72,6 +78,9 @@ dock_from_renv <- function( AS = AS ) + # get renv version + renv_version <- lock$data()$Packages$renv$Version + distro_args <- switch( distro, centos7 = list( @@ -235,7 +244,19 @@ dock_from_renv <- function( ) ) - dock$RUN("R -e 'install.packages(c(\"renv\",\"remotes\"))'") + # check if fix_renv_version is true + if (fix_renv_version){ + dock$RUN("R -e 'install.packages(\"remotes\")'") + install_renv_string <- paste0( + "R -e 'remotes::install_version(\"renv\", version = ", + renv_version, + ")'" + ) + dock$RUN(install_renv_string) + + } else { + dock$RUN("R -e 'install.packages(c(\"renv\",\"remotes\"))'") + } dock$COPY(basename(lockfile), "renv.lock") dock$RUN(r(renv::restore())) @@ -243,22 +264,3 @@ dock_from_renv <- function( dock } - -#' Generate base image name -#' -#' Creates the base image name from the provided distro name and the R version found in the `renv.lock` file. -#' -#' @keywords internal -gen_base_image <- function( - distro = "bionic", - r_version = "4.0", - FROM = "rstudio/r-base" -) { - distro <- match.arg(distro, available_distros) - - if (FROM == "rstudio/r-base") { - glue::glue("{FROM}:{r_version}-{distro}") - } else { - glue::glue("{FROM}:{r_version}") - } -} diff --git a/R/dockerignore.R b/R/dockerignore.R index e410249..1afe6a5 100644 --- a/R/dockerignore.R +++ b/R/dockerignore.R @@ -10,7 +10,7 @@ #' @export #' #' @examples -#' if (interactive()) { +#' \dontrun{ #' docker_ignore_add() #' } docker_ignore_add <- function(path) { @@ -48,8 +48,8 @@ docker_ignore_add <- function(path) { path_ri )) { write( - "^\\.dockerignore$", - path_ri, + "^\\.dockerignore$", + path_ri, append = TRUE) cat_bullet( ".dockerignore added to the .Rbuildignore file.", @@ -66,4 +66,4 @@ docker_ignore_add <- function(path) { } return(invisible(path_di)) -} \ No newline at end of file +} diff --git a/R/gen_base_image.R b/R/gen_base_image.R new file mode 100644 index 0000000..c6db29e --- /dev/null +++ b/R/gen_base_image.R @@ -0,0 +1,21 @@ +# WARNING - Generated by {fusen} from dev/flat_dock_from_renv.Rmd: do not edit by hand + +#' Generate base image name +#' +#' Creates the base image name from the provided distro name and the R version found in the `renv.lock` file. +#' +#' @keywords internal +#' @noRd +gen_base_image <- function( + distro = "bionic", + r_version = "4.0", + FROM = "rstudio/r-base" +) { + distro <- match.arg(distro, available_distros) + + if (FROM == "rstudio/r-base") { + glue::glue("{FROM}:{r_version}-{distro}") + } else { + glue::glue("{FROM}:{r_version}") + } +} diff --git a/R/parse-dockerfile.R b/R/parse-dockerfile.R new file mode 100644 index 0000000..29e289f --- /dev/null +++ b/R/parse-dockerfile.R @@ -0,0 +1,48 @@ + +#' Parse a Dockerfile +#' +#' Create a Dockerfile object from a Dockerfile. +#' +#' @param path path to the Dockerfile +#' @returns A Dockerfile object +#' @export +#' @examples +#' parse_dockerfile(system.file("Dockerfile", package = "dockerfiler")) +#' +parse_dockerfile <- function(path) { + + # note that MAINTAINER is deprecated but there + # for backwards compatability + DOCKER_INSTRUCTIONS <- c( + "#", # for detecting comments + # "^$", # to capture empty lines + "FROM", "RUN", "CMD", "EXPOSE", "LABEL", "MAINTAINER", "EXPOSE", "ENV", "ADD", "COPY", "ENTRYPOINT", "VOLUME", "USER", "WORKDIR", "ARG", "ONBUILD", "STOPSIGNAL", "HEALTHCHECK", "SHELL") + + instruction_regex <- paste0(DOCKER_INSTRUCTIONS, collapse = "|") + + # read the dockerfile + dock_raw <- readLines(path) + # capture instructions + m <- gregexpr(paste0("^", instruction_regex), dock_raw) + # extract the instruction + instructions <- unlist(lapply(regmatches(dock_raw, m), `[`, 1)) + # find positions + instr_pos <- which(!is.na(instructions)) + # find how many lines between instructions and last line + n_lines_between <- diff(c(instr_pos, length(dock_raw))) - 1 + # the last one needs to add 1 + n_lines_between[length(n_lines_between)] <- n_lines_between[length(n_lines_between)] + 1 + + dock_lines <- find_line_positions(dock_raw, instr_pos, instr_pos + n_lines_between) + + res <- Dockerfile$new() + res$Dockerfile <- dock_lines + res +} + +# helper function to craft commands +find_line_positions <- function(x, start, end) { + indexes <- Map(seq.int, start, end, by = 1) + cmnds <- lapply(indexes, function(.x) x[.x]) + vapply(cmnds, paste0, character(1), collapse = "\n") +} diff --git a/dev/config_fusen.yaml b/dev/config_fusen.yaml new file mode 100644 index 0000000..24c7ffc --- /dev/null +++ b/dev/config_fusen.yaml @@ -0,0 +1,15 @@ +flat_dock_from_renv.Rmd: + path: dev/flat_dock_from_renv.Rmd + state: active + R: + - R/dock_from_renv.R + - R/gen_base_image.R + tests: tests/testthat/test-dock_from_renv.R + vignettes: vignettes/dockerfile-from-renv-lock.Rmd + inflate: + flat_file: dev/flat_dock_from_renv.Rmd + vignette_name: Dockerfile from renv.lock + open_vignette: false + check: false + document: true + overwrite: 'yes' diff --git a/dev/flat_dock_from_renv.Rmd b/dev/flat_dock_from_renv.Rmd index 62de8fd..5bc3aa6 100644 --- a/dev/flat_dock_from_renv.Rmd +++ b/dev/flat_dock_from_renv.Rmd @@ -13,8 +13,305 @@ library(testthat) You need to run the 'description' chunk in the '0-dev_history.Rmd' file before continuing your code there. --> +# gen_base_image + +```{r function-gen_base_image} +#' Generate base image name +#' +#' Creates the base image name from the provided distro name and the R version found in the `renv.lock` file. +#' +#' @keywords internal +gen_base_image <- function( + distro = "bionic", + r_version = "4.0", + FROM = "rstudio/r-base" +) { + distro <- match.arg(distro, available_distros) + + if (FROM == "rstudio/r-base") { + glue::glue("{FROM}:{r_version}-{distro}") + } else { + glue::glue("{FROM}:{r_version}") + } +} +``` + + + + # Create a Dockerfile from a renv.lock file + +```{r function-dock_from_renv} +# ignoring opensuse for the time being +available_distros <- c( + "xenial", + "bionic", + "focal", + "centos7", + "centos8" +) + +#' Create a Dockerfile from an `renv.lock` file +#' +#' @param lockfile Path to an `renv.lock` file to use as an input.. +#' @param FROM Docker image to start FROM Default is +#' FROM rocker/r-base +#' @param AS The AS of the Dockerfile. Default it NULL. +#' @param distro One of "focal", "bionic", "xenial", "centos7", +#' or "centos8". See available distributions +#' at https://hub.docker.com/r/rstudio/r-base/. +#' @param sysreqs boolean. If `TRUE`, the Dockerfile +#' will contain sysreq installation. +#' @param expand boolean. If `TRUE` each system requirement will have its own `RUN` line. +#' @param repos character. The URL(s) of the repositories to use for `options("repos")`. +#' @param extra_sysreqs character vector. Extra debian system requirements. +#' Will be installed with apt-get install. +#' @param fix_renv_version boolean. If `TRUE` the version of renv in the lockfile +#' will be used for the `renv::restore()` command +#' @importFrom utils getFromNamespace +#' @return A R6 object of class `Dockerfile`. +#' @details +#' +#' System requirements for packages are provided +#' through RStudio Package Manager via the {pak} +#' package. The install commands provided from pak +#' are added as `RUN` directives within the `Dockerfile`. +#' +#' The R version is taken from the `renv.lock` file. +#' Packages are installed using `renv::restore()` which ensures +#' that the proper package version and source is used when installed. +#' +#' @importFrom attempt map_try_catch +#' @importFrom glue glue +#' @importFrom pak pkg_system_requirements + +#' @export +dock_from_renv <- function( + lockfile = "renv.lock", + distro = "focal", + FROM = "rocker/r-base", + AS = NULL, + sysreqs = TRUE, + repos = c(CRAN = "https://cran.rstudio.com/"), + expand = FALSE, + extra_sysreqs = NULL, + fix_renv_version = FALSE +) { + distro <- match.arg(distro, available_distros) + + lock <- getFromNamespace("lockfile", "renv")(lockfile) + + # lock$repos(CRAN = repos) + lockfile <- basename(lockfile) + + # start the dockerfile + R_major_minor <- lock$data()$R$Version + dock <- Dockerfile$new( + FROM = gen_base_image( + distro = distro, + r_version = R_major_minor, + FROM = FROM + ), + AS = AS + ) + + # get renv version + renv_version <- lock$data()$Packages$renv$Version + + distro_args <- switch( + distro, + centos7 = list( + os = "centos", + os_release = "7" + ), + centos8 = list( + os = "centos", + os_release = "8" + ), + xenial = list( + os = "ubuntu", + os_release = "16.04" + ), + bionic = list( + os = "ubuntu", + os_release = "18.04" + ), + focal = list( + os = "ubuntu", + os_release = "20.04" + ), + jammy = list( + os = "ubuntu", + os_release = "22.04" + ) + ) + + install_cmd <- switch( + distro, + centos7 = "yum install -y", + centos8 = "yum install -y", + xenial = "apt-get install -y", + bionic = "apt-get install -y", + focal = "apt-get install -y", + jammy = "apt-get install -y" + ) + + update_cmd <- switch( + distro, + centos7 = "yum update -y", + centos8 = "yum update -y", + xenial = "apt-get update -y", + bionic = "apt-get update -y", + focal = "apt-get update -y", + jammy = "apt-get update -y" + ) + + clean_cmd <- switch( + distro, + centos7 = "yum clean all && rm -rf /var/cache/yum", + centos8 = "yum clean all && rm -rf /var/cache/yum", + xenial = "rm -rf /var/lib/apt/lists/*", + bionic = "rm -rf /var/lib/apt/lists/*", + focal = "rm -rf /var/lib/apt/lists/*", + jammy = "rm -rf /var/lib/apt/lists/*" + ) + + pkgs <- names(lock$data()$Packages) + + if (sysreqs) { + + # please wait during system requirement calculation + cat_bullet( + "Please wait while we compute system requirements...", + bullet = "info", + bullet_col = "green" + ) + + message( + sprintf( + "Fetching system dependencies for %s package(s) records.", + length(pkgs) + ) + ) + + pkg_os <- lapply( + pkgs, + FUN = function(x) { + c( + list(package = x), + distro_args + ) + } + ) + + pkg_sysreqs <- attempt::map_try_catch( + pkg_os, + function(x) { + do.call( + pak::pkg_system_requirements, + x + ) + }, + .e = ~ character(0) + ) + + + + + + pkg_installs <- unique(pkg_sysreqs) + + if (length(unlist(pkg_installs)) == 0) { + cat_bullet( + "No sysreqs required", + bullet = "info", + bullet_col = "green" + ) + } + + cat_green_tick("Done") # TODO animated version ? + } else { + pkg_installs <- NULL + } + + # extra_sysreqs + + + + + if (length(extra_sysreqs) > 0) { + extra <- paste( + install_cmd, + extra_sysreqs + ) + pkg_installs <- unique(c(pkg_installs, extra)) + } + + + + + + # compact + if (!expand) { + # we compact sysreqs + pkg_installs <- compact_sysreqs( + pkg_installs, + update_cmd = update_cmd, + install_cmd = install_cmd, + clean_cmd = clean_cmd + ) + + } else { + dock$RUN(update_cmd) + } + + do.call(dock$RUN, list(pkg_installs)) + + if (expand) { + dock$RUN(clean_cmd) + } + + repos_as_character <- repos_as_character(repos) + dock$RUN("mkdir -p /usr/local/lib/R/etc/ /usr/lib/R/etc/") + + dock$RUN( + sprintf( + "echo \"options(renv.config.pak.enabled = TRUE, repos = %s, download.file.method = 'libcurl', Ncpus = 4)\" | tee /usr/local/lib/R/etc/Rprofile.site | tee /usr/lib/R/etc/Rprofile.site", + repos_as_character + ) + ) + + # check if fix_renv_version is true + if (fix_renv_version){ + dock$RUN("R -e 'install.packages(\"remotes\")'") + install_renv_string <- paste0( + "R -e 'remotes::install_version(\"renv\", version = ", + renv_version, + ")'" + ) + dock$RUN(install_renv_string) + + } else { + dock$RUN("R -e 'install.packages(c(\"renv\",\"remotes\"))'") + } + + dock$COPY(basename(lockfile), "renv.lock") + dock$RUN(r(renv::restore())) + + dock +} + +``` + +```{r example-dock_from_renv} +#' \dontrun{ +#' dock <- dock_from_renv("renv.lock", distro = "xenial") +#' dock$write("Dockerfile") +#' } +``` + + ```{r dock_from_renv, eval=FALSE} # A temporary directory dir_build <- tempfile(pattern = "renv") @@ -51,48 +348,102 @@ dir.create(dir_build) # Create a lockfile the_lockfile <- file.path(dir_build, "renv.lock") + custom_packages <- c( # attachment::att_from_description(), "renv", - "cli", "glue", #"golem", - "shiny", "stats", "utils", + "cli", + "glue", # "golem", + "shiny", + "stats", + "utils", "testthat", "knitr" ) renv::snapshot( packages = custom_packages, lockfile = the_lockfile, - prompt = FALSE) + prompt = FALSE +) # Modify R version for tests renv_file <- readLines(file.path(dir_build, "renv.lock")) renv_file[grep("Version", renv_file)[1]] <- ' "Version": "4.1.2",' writeLines(renv_file, file.path(dir_build, "renv.lock")) + + # dock_from_renv ---- test_that("dock_from_renv works", { - skip_if_not(interactive()) + # skip_if_not(interactive()) # Create Dockerfile - expect_error( - dock_from_renv(lockfile = the_lockfile, - distro = "focal", - FROM = "rocker/verse", - out_dir = dir_build - ), regexp = NA) + out <- dock_from_renv( + lockfile = the_lockfile, + distro = "focal", + FROM = "rocker/verse", + ) + expect_s3_class( + out, + "Dockerfile" + ) + expect_s3_class( + out, + "R6" + ) # read Dockerfile - dock_created <- readLines(file.path(dir_build, "Dockerfile")) + out$write( + file.path( + dir_build, + "Dockerfile" + ) + ) + + dock_created <- readLines( + file.path( + dir_build, + "Dockerfile" + ) + ) expect_equal( - dock_created[grep("renv.lock.dock renv.lock", dock_created)], - paste0("COPY ", dir_build, "/renv.lock.dock renv.lock") + dock_created[1], + "FROM rocker/verse:4.1.2" + ) + + expect_length( + grep("install.packages\\(c\\(\"renv\",\"remotes\"", dock_created), + 1 + ) + expect_length( + grep("RUN R -e 'renv::restore\\(\\)'", dock_created), + 1 ) - dock_created[grep("renv.lock.dock renv.lock", dock_created)] <- - "COPY renv.lock.dock renv.lock" - # file.copy(file.path(dir_build, "Dockerfile"), "inst/renv_Dockefile", overwrite = TRUE) - dock_expected <- readLines(system.file("renv_Dockefile", package = "dockerfiler")) - dock_expected[grep("renv.lock.dock renv.lock", dock_expected)] <- - "COPY renv.lock.dock renv.lock" + # System dependencies are different when build in interactive environment? + skip_if_not(interactive()) + dir.create( + file.path( + dir_build, + "inst" + ) + ) + file.copy( + file.path( + dir_build, + "Dockerfile" + ), + file.path( + dir_build, + "inst" + ), + overwrite = TRUE + ) + dock_expected <- readLines( + system.file( + "renv_Dockefile", + package = "dockerfiler" + ) + ) expect_equal(dock_created, dock_expected) }) @@ -101,26 +452,28 @@ unlink(dir_build) # repos_as_character ---- test_that("repos_as_character works", { - out <- repos_as_character( + out <- dockerfiler:::repos_as_character( repos = c( RSPM = paste0("https://packagemanager.rstudio.com/all/__linux__/focal/latest"), - CRAN = "https://cran.rstudio.com/") + CRAN = "https://cran.rstudio.com/" + ) ) expect_equal( out, - "c(RSPM = 'https://packagemanager.rstudio.com/all/__linux__/focal/latest', CRAN = 'https://cran.rstudio.com/')") + "c(RSPM = 'https://packagemanager.rstudio.com/all/__linux__/focal/latest', CRAN = 'https://cran.rstudio.com/')" + ) }) # gen_base_image ---- test_that("gen_base_image works", { - out <- gen_base_image( + out <- dockerfiler:::gen_base_image( distro = "focal", r_version = "4.0", FROM = "rstudio/r-base" ) expect_equal(out, "rstudio/r-base:4.0-focal") - out <- gen_base_image( + out <- dockerfiler:::gen_base_image( distro = "focal", r_version = "4.0", FROM = "rocker/verse" @@ -128,11 +481,68 @@ test_that("gen_base_image works", { expect_equal(out, "rocker/verse:4.0") }) + + + + +test_that("dock_from_renv works with old renv", { + out_true <- dock_from_renv( + lockfile = the_lockfile, + distro = "focal", + FROM = "rocker/verse", + fix_renv_version = TRUE + ) + + out_true$write( + file.path( + dir_build, + "Dockerfile_keep_true" + ) + ) + + dock_created_true <- readLines( + file.path( + dir_build, + "Dockerfile_keep_true" + ) + ) + out_false <- dock_from_renv( + lockfile = the_lockfile, + distro = "focal", + FROM = "rocker/verse", + fix_renv_version = FALSE + ) + + out_false$write( + file.path( + dir_build, + "Dockerfile_keep_false" + ) + ) + + dock_created_false <- readLines( + file.path( + dir_build, + "Dockerfile_keep_false" + ) + ) + dock_created_false + dock_created_true + + packageVersion("renv") + test_string <- paste0("remotes::install_version\\(\"renv\", version = ", packageVersion("renv") ,"\\)") + expect_true( any( grepl(test_string , dock_created_true) )) + + dock_created_false + test_string <- 'install.packages\\(c\\(\"renv\",\"remotes\"))' + expect_true( any( grepl(test_string , dock_created_false) )) + +}) ``` ```{r development-inflate, eval=FALSE} # Run but keep eval=FALSE to avoid infinite loop # Execute in the console directly -fusen::inflate(flat_file = "dev/flat_dock_from_renv.Rmd", vignette_name = "Dockerfile from renv.lock") +fusen::inflate(flat_file = "dev/flat_dock_from_renv.Rmd", vignette_name = "Dockerfile from renv.lock",check=FALSE,open_vignette = FALSE,overwrite = TRUE) ``` diff --git a/man/dock_from_renv.Rd b/man/dock_from_renv.Rd index 1b02558..f0f05b1 100644 --- a/man/dock_from_renv.Rd +++ b/man/dock_from_renv.Rd @@ -12,7 +12,8 @@ dock_from_renv( sysreqs = TRUE, repos = c(CRAN = "https://cran.rstudio.com/"), expand = FALSE, - extra_sysreqs = NULL + extra_sysreqs = NULL, + fix_renv_version = FALSE ) } \arguments{ @@ -36,6 +37,9 @@ will contain sysreq installation.} \item{extra_sysreqs}{character vector. Extra debian system requirements. Will be installed with apt-get install.} + +\item{fix_renv_version}{boolean. If `TRUE` the version of renv in the lockfile +will be used for the `renv::restore()` command} } \value{ A R6 object of class `Dockerfile`. diff --git a/man/docker_ignore_add.Rd b/man/docker_ignore_add.Rd index c0397fe..3650ecc 100644 --- a/man/docker_ignore_add.Rd +++ b/man/docker_ignore_add.Rd @@ -16,7 +16,7 @@ The path to the `.dockerignore` file, invisibly. Create a dockerignore file } \examples{ -if (interactive()) { +\dontrun{ docker_ignore_add() } } diff --git a/man/gen_base_image.Rd b/man/gen_base_image.Rd deleted file mode 100644 index 91b3951..0000000 --- a/man/gen_base_image.Rd +++ /dev/null @@ -1,12 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dock_from_renv.R -\name{gen_base_image} -\alias{gen_base_image} -\title{Generate base image name} -\usage{ -gen_base_image(distro = "bionic", r_version = "4.0", FROM = "rstudio/r-base") -} -\description{ -Creates the base image name from the provided distro name and the R version found in the `renv.lock` file. -} -\keyword{internal} diff --git a/man/parse_dockerfile.Rd b/man/parse_dockerfile.Rd new file mode 100644 index 0000000..fc2774d --- /dev/null +++ b/man/parse_dockerfile.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/parse-dockerfile.R +\name{parse_dockerfile} +\alias{parse_dockerfile} +\title{Parse a Dockerfile} +\usage{ +parse_dockerfile(path) +} +\arguments{ +\item{path}{path to the Dockerfile} +} +\value{ +A Dockerfile object +} +\description{ +Create a Dockerfile object from a Dockerfile. +} +\examples{ +parse_dockerfile(system.file("Dockerfile", package = "dockerfiler")) + +} diff --git a/tests/testthat/test-dock_from_renv.R b/tests/testthat/test-dock_from_renv.R index a3bb149..805172c 100644 --- a/tests/testthat/test-dock_from_renv.R +++ b/tests/testthat/test-dock_from_renv.R @@ -1,3 +1,5 @@ +# WARNING - Generated by {fusen} from dev/flat_dock_from_renv.Rmd: do not edit by hand + dir_build <- tempfile(pattern = "renv") dir.create(dir_build) @@ -26,6 +28,8 @@ renv_file <- readLines(file.path(dir_build, "renv.lock")) renv_file[grep("Version", renv_file)[1]] <- ' "Version": "4.1.2",' writeLines(renv_file, file.path(dir_build, "renv.lock")) + + # dock_from_renv ---- test_that("dock_from_renv works", { # skip_if_not(interactive()) @@ -133,3 +137,61 @@ test_that("gen_base_image works", { ) expect_equal(out, "rocker/verse:4.0") }) + + + + + +test_that("dock_from_renv works with old renv", { + out_true <- dock_from_renv( + lockfile = the_lockfile, + distro = "focal", + FROM = "rocker/verse", + fix_renv_version = TRUE + ) + + out_true$write( + file.path( + dir_build, + "Dockerfile_keep_true" + ) + ) + + dock_created_true <- readLines( + file.path( + dir_build, + "Dockerfile_keep_true" + ) + ) + out_false <- dock_from_renv( + lockfile = the_lockfile, + distro = "focal", + FROM = "rocker/verse", + fix_renv_version = FALSE + ) + + out_false$write( + file.path( + dir_build, + "Dockerfile_keep_false" + ) + ) + + dock_created_false <- readLines( + file.path( + dir_build, + "Dockerfile_keep_false" + ) + ) + dock_created_false + dock_created_true + + packageVersion("renv") + test_string <- paste0("remotes::install_version\\(\"renv\", version = ", packageVersion("renv") ,"\\)") + expect_true( any( grepl(test_string , dock_created_true) )) + + dock_created_false + test_string <- 'install.packages\\(c\\(\"renv\",\"remotes\"))' + expect_true( any( grepl(test_string , dock_created_false) )) + +}) diff --git a/tests/testthat/test-dockerfile-parsing.R b/tests/testthat/test-dockerfile-parsing.R new file mode 100644 index 0000000..61125bb --- /dev/null +++ b/tests/testthat/test-dockerfile-parsing.R @@ -0,0 +1,10 @@ +test_that("Dockerfile parsing works", { + dock_file <- system.file("Dockerfile", package = "dockerfiler") + parsed <- parse_dockerfile(dock_file) + + expect_identical( + paste0(parsed$Dockerfile, collapse = "\n"), + paste0(readLines(dock_file), collapse = "\n") + ) + +}) diff --git a/vignettes/dockerfile-from-renv-lock.Rmd b/vignettes/dockerfile-from-renv-lock.Rmd index 037845b..78b70a3 100644 --- a/vignettes/dockerfile-from-renv-lock.Rmd +++ b/vignettes/dockerfile-from-renv-lock.Rmd @@ -2,7 +2,7 @@ title: "Dockerfile from renv.lock" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{Dockerfile from renv.lock} + %\VignetteIndexEntry{dockerfile-from-renv-lock} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -18,15 +18,38 @@ knitr::opts_chunk$set( library(dockerfiler) ``` - + +# gen_base_image + + + + + + + + # Create a Dockerfile from a renv.lock file + + + + +```{r example-dock_from_renv} +#' \dontrun{ +#' dock <- dock_from_renv("renv.lock", distro = "xenial") +#' dock$write("Dockerfile") +#' } +``` + + + + ```{r dock_from_renv, eval = FALSE} # A temporary directory dir_build <- tempfile(pattern = "renv")