From 8b97cc33612c17ff0b6c44545f56353ed5c85036 Mon Sep 17 00:00:00 2001 From: Vincent Guyader Date: Sun, 6 Apr 2025 17:22:15 +0000 Subject: [PATCH 1/2] feat Add `append` parameter to `write()` to allow appending to file instead of overwriting --- R/dockerfile.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/dockerfile.R b/R/dockerfile.R index a2f3f98..913b261 100644 --- a/R/dockerfile.R +++ b/R/dockerfile.R @@ -162,9 +162,10 @@ cat(self$Dockerfile, sep = "\n") #' @description #' Write the Dockerfile to a file. #' @param as The file to write to. +#' @param append boolean, if TRUE append to file. #' @return used for side effect -write = function(as = "Dockerfile") { -base::write(self$Dockerfile, file = as) +write = function(as = "Dockerfile", append = FALSE) { +base::write(self$Dockerfile, file = as, append = append) }, #' @description #' Switch commands. From 1d8370219d9967abc010b68083fad5ce7e7fecde Mon Sep 17 00:00:00 2001 From: Vincent Guyader Date: Thu, 10 Apr 2025 17:41:56 +0000 Subject: [PATCH 2/2] feat allow multistage dockerfile --- DESCRIPTION | 2 +- NEWS.md | 4 ++++ R/add.R | 25 ++++++++++++++----------- R/dockerfile.R | 5 +++-- man/Dockerfile.Rd | 8 ++++++-- tests/testthat/test-copy-stage.R | 27 +++++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 tests/testthat/test-copy-stage.R diff --git a/DESCRIPTION b/DESCRIPTION index 131ab1a..92bc449 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: dockerfiler Title: Easy Dockerfile Creation from R -Version: 0.2.4 +Version: 0.2.4.9000 Authors@R: c( person("Colin", "Fay", , "contact@colinfay.me", role = c("cre", "aut"), comment = c(ORCID = "0000-0001-7343-1846")), diff --git a/NEWS.md b/NEWS.md index 86c778c..19cac5e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# dockerfiler 0.2.4.9xxx + +- allow multistage dockerfile creation + # dockerfiler 0.2.4 - remove native pipe thanks to @HenningLorenzen-ext-bayer, this enable to use of older R versions diff --git a/R/add.R b/R/add.R index c463047..dc45369 100644 --- a/R/add.R +++ b/R/add.R @@ -31,19 +31,22 @@ add_add <- function( glue("ADD {from} {to}") } -add_copy <- function( - from, - to, - force = TRUE -) { +add_copy <- function(from, + to, + stage = NULL, + force = TRUE) { if (!force) { - warn_if_not( - normalizePath(from), - file.exists, - "The file `from` doesn't seem to exists" - ) + warn_if_not(normalizePath(from), + file.exists, + "The file `from` doesn't seem to exists") + } + + if (is.null(stage)) { + glue("COPY {from} {to}") + } else { + glue("COPY --from={stage} {from} {to}") + } - glue("COPY {from} {to}") } add_workdir <- function(where) { diff --git a/R/dockerfile.R b/R/dockerfile.R index 913b261..2c291b3 100644 --- a/R/dockerfile.R +++ b/R/dockerfile.R @@ -39,9 +39,10 @@ self$Dockerfile <- c(self$Dockerfile, add_add(from, to, force)) #' @param from The source file. #' @param to The destination file. #' @param force If TRUE, overwrite the destination file. +#' @param stage Optional. Name of the build stage (e.g., `"builder"`) to copy files from. This corresponds to the `--from=` part in a Dockerfile COPY instruction (e.g., `COPY --from=builder /source /dest`). If `NULL`, the `--from=` argument is omitted. #' @return the Dockerfile object, invisibly. -COPY = function(from, to, force = TRUE) { -self$Dockerfile <- c(self$Dockerfile, add_copy(from, to, force)) +COPY = function(from, to, stage= NULL , force = TRUE) { +self$Dockerfile <- c(self$Dockerfile, add_copy(from, to, stage, force)) }, #' @description #' Add a WORKDIR command. diff --git a/man/Dockerfile.Rd b/man/Dockerfile.Rd index 9ab72aa..390a5f3 100644 --- a/man/Dockerfile.Rd +++ b/man/Dockerfile.Rd @@ -120,7 +120,7 @@ the Dockerfile object, invisibly. \subsection{Method \code{COPY()}}{ Add a COPY command. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Dockerfile$COPY(from, to, force = TRUE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Dockerfile$COPY(from, to, stage = NULL, force = TRUE)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -130,6 +130,8 @@ Add a COPY command. \item{\code{to}}{The destination file.} +\item{\code{stage}}{Optional. Name of the build stage (e.g., \code{"builder"}) to copy files from. This corresponds to the \verb{--from=} part in a Dockerfile COPY instruction (e.g., \code{COPY --from=builder /source /dest}). If \code{NULL}, the \verb{--from=} argument is omitted.} + \item{\code{force}}{If TRUE, overwrite the destination file.} } \if{html}{\out{}} @@ -459,13 +461,15 @@ used for side effect \subsection{Method \code{write()}}{ Write the Dockerfile to a file. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Dockerfile$write(as = "Dockerfile")}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Dockerfile$write(as = "Dockerfile", append = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{as}}{The file to write to.} + +\item{\code{append}}{boolean, if TRUE append to file.} } \if{html}{\out{
}} } diff --git a/tests/testthat/test-copy-stage.R b/tests/testthat/test-copy-stage.R new file mode 100644 index 0000000..5aa4ad5 --- /dev/null +++ b/tests/testthat/test-copy-stage.R @@ -0,0 +1,27 @@ +test_that("Dockerfile COPY with build stage works correctly", { + # Création des deux étapes du Dockerfile + stage_1 <- Dockerfile$new(FROM = "alpine", AS = "builder") + stage_1$RUN('echo "coucou depuis le builder" > /coucou') + + stage_2 <- Dockerfile$new(FROM = "ubuntu") + stage_2$COPY(from = "/coucou", to = "/truc.txt", force = TRUE, stage = "builder") + stage_2$RUN("cat /truc.txt") + + tmpfile <- tempfile(fileext = ".Dockerfile") + stage_1$write(as = tmpfile) + stage_2$write(as = tmpfile, append = TRUE) + + docker_lines <- readLines(tmpfile) + + expect_length(docker_lines, 5) + + expect_equal(docker_lines[1], "FROM alpine AS builder") + expect_equal(docker_lines[2], 'RUN echo "coucou depuis le builder" > /coucou') + expect_equal(docker_lines[3], "FROM ubuntu") + expect_equal(docker_lines[4], "COPY --from=builder /coucou /truc.txt") + expect_equal(docker_lines[5], "RUN cat /truc.txt") + + expect_true(any(grepl("COPY --from=builder", docker_lines))) + expect_true(any(grepl("/coucou", docker_lines))) + expect_true(any(grepl("/truc.txt", docker_lines))) +})