Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ Package: dockerfiler
Title: Easy Dockerfile Creation from R
Version: 0.3.0
Authors@R: c(
person("Colin", "Fay", , "contact@colinfay.me", role = "aut",
person("Colin", "Fay", , "contact@colinfay.me", role = c("cre", "aut"),
comment = c(ORCID = "0000-0001-7343-1846")),
person("Vincent", "Guyader", , "vincent@thinkr.fr", role = c("cre", "aut"),
person("Vincent", "Guyader", , "vincent@thinkr.fr", role = "aut",
comment = c(ORCID = "0000-0003-0671-9270")),
person("Josiah", "Parry", , "josiah.parry@gmail.com", role = "aut",
comment = c(ORCID = "0000-0001-9910-865X")),
Expand Down Expand Up @@ -44,4 +44,4 @@ Config/testthat/edition: 3
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
22 changes: 13 additions & 9 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
appended at codegen time (e.g. `rocker/r-ver:4.5.0`). Apple
Silicon and ARM Linux hosts (Ampere, AWS Graviton) now build
natively without Rosetta. Pass the legacy `FROM = "rocker/r-base"`
to opt out. Closes #47.
- `dock_from_renv()` default `repos` flips from
to opt out. `dock_from_desc()`'s default `FROM` was already
`rocker/r-ver:<R version>` and is unchanged. Closes #47.
- `dock_from_renv()` and `dock_from_desc()` default `repos` flips from
`"https://cran.rstudio.com/"` (source-only CRAN mirror) to
`"https://p3m.dev/cran/latest"` (Posit Public Package Manager),
with automatic rewrite to the `__linux__/$VERSION_CODENAME/` shape
Expand Down Expand Up @@ -151,13 +152,16 @@
function arguments, so the branch was unreachable; the success path
always ran when `build()` returned. The branch is removed; failures
of `pkgbuild::build()` propagate normally via `stop()`. Closes #98.
- Small polish bundle (no behavioural changes for end users): fix two
`length(x > 0)` typos in `dock_from_desc()` (intent was
`length(x) > 0`); drop a duplicate `@export` tag in `dockerignore.R`;
use the new `dock$ARG(name, default = ...)` form internally instead
of inlining the `=`; tighten a previously brittle regression test
that checked for any occurrence of `"remotes"` in the generated
Dockerfile.
- `dock_from_desc()`: fixed two `length(x > 0)` typos in the dependency
handling (the intent was `length(x) > 0`); the conditions now behave
as documented.
- The `<pkg>_*.tar.gz` cleanup glob in `dock_from_desc(build_from_source = FALSE)`
is now built with `glob2rx()`, so a dot in a package name (e.g.
`R.utils`) is matched literally and a sibling package's tarball is no
longer swept up.
- Internal tidy-ups with no user-visible effect: dropped a duplicate
`@export` in `dockerignore.R`; the codegen now uses the new
`dock$ARG(name, default = ...)` form instead of inlining the `=`.


# dockerfiler 0.2.6
Expand Down
59 changes: 51 additions & 8 deletions R/dock_from_desc.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,24 @@ quote_not_na <- function(x){
#' dash, underscore, optional `:tag` and / or `@sha256:<hex>`); other
#' values raise an error to prevent shell-metacharacter injection
#' into the generated FROM directive.
#' @param AS The AS of the Dockerfile. Default it NULL. When non-NULL,
#' validated as a simple build-stage name (`^[a-zA-Z0-9][a-zA-Z0-9._-]*$`).
#' @param AS The build-stage name of the Dockerfile (`FROM ... AS <name>`).
#' Default is `NULL` (no `AS`). When non-`NULL`, validated as a simple
#' build-stage name (`^[a-zA-Z0-9][a-zA-Z0-9._-]*$`).
#' @param sysreqs boolean. If TRUE, the Dockerfile will contain sysreq installation.
#' @param repos character. The URL(s) of the repositories to use for
#' `options("repos")`. Each value must look like an http(s) URL (no
#' quotes, spaces or newlines); each name (when set) must be a simple
#' identifier (`^[A-Za-z][A-Za-z0-9._-]*$`). Other values raise an
#' error to prevent injection into the generated `echo "options(...)"`
#' shell command.
#' `options("repos")`. Default is `c(CRAN = "https://p3m.dev/cran/latest")`
#' (Posit Public Package Manager). When `repos` is a single `CRAN`-keyed
#' PPM URL (`packagemanager.posit.co`, `packagemanager.rstudio.com`, or
#' `p3m.dev`), the codegen rewrites it to the
#' `__linux__/$VERSION_CODENAME/` shape (codename resolved from
#' `/etc/os-release` at image build time) and adds the strict
#' `HTTPUserAgent` PPM requires, so the build pulls pre-compiled Linux
#' binaries instead of compiling from source. Pass the legacy
#' `c(CRAN = "https://cran.rstudio.com/")` to opt out. Each value must
#' look like an http(s) URL (no quotes, spaces or newlines); each name
#' (when set) must be a simple identifier (`^[A-Za-z][A-Za-z0-9._-]*$`).
#' Other values raise an error to prevent injection into the generated
#' `echo "options(...)"` shell command.
#' @param expand boolean. If `TRUE` each system requirement will have its own `RUN` line.
#' @param build_from_source boolean. If `TRUE` no tar.gz is created and
#' the Dockerfile directly mount the source folder.
Expand Down Expand Up @@ -86,9 +95,38 @@ quote_not_na <- function(x){
#' Must be a single scalar logical; `NA`, character, numeric,
#' `NULL` and length-2+ vectors are rejected with an error.
#'
#' @details
#' Two install strategies are available for the package itself:
#' * `build_from_source = TRUE` (the default): the generated Dockerfile
#' mounts the source folder and installs from it directly. `update_tar_gz`
#' is ignored.
#' * `build_from_source = FALSE`: a source tarball (`<pkg>_<version>.tar.gz`)
#' is `COPY`'d into the image and installed with
#' `remotes::install_local()`. When `update_tar_gz = TRUE`, a fresh
#' tarball is built with `pkgbuild::build()` first (and any stale
#' `<pkg>_*.tar.gz` in the current directory is removed); when
#' `update_tar_gz = FALSE`, an already-built tarball is expected
#' alongside the `DESCRIPTION`.
#'
#' The package name and its dependency-field names are read from the
#' `DESCRIPTION` and validated against the CRAN package-name grammar
#' before being interpolated into the generated directives.
#'
#' @export
#' @rdname dockerfiles
#'
#' @examples
#' \dontrun{
#' # From the DESCRIPTION of the package in the working directory:
#' dock <- dock_from_desc("DESCRIPTION")
#' dock
#'
#' # Pull source packages from the classic CRAN mirror instead of PPM:
#' dock_from_desc(
#' "DESCRIPTION",
#' repos = c(CRAN = "https://cran.rstudio.com/")
#' )
#' }
#' @importFrom utils installed.packages packageVersion glob2rx
#' @importFrom remotes dev_package_deps
#' @importFrom desc desc_get_deps desc_get
Expand All @@ -106,7 +144,7 @@ dock_from_desc <- function(
),
AS = NULL,
sysreqs = TRUE,
repos = c(CRAN = "https://cran.rstudio.com/"),
repos = c(CRAN = "https://p3m.dev/cran/latest"),
expand = FALSE,
update_tar_gz = TRUE,
build_from_source = TRUE,
Expand Down Expand Up @@ -244,6 +282,11 @@ dock_from_desc <- function(
repos_as_character
)
)
# When `repos` is a single CRAN-keyed Posit Package Manager URL,
# rewrite the Rprofile.site line so the build pulls pre-compiled
# Linux binaries (the `__linux__/$VERSION_CODENAME/` shape + the
# strict `HTTPUserAgent` PPM requires). No-op for non-PPM repos.
.patch_rprofile_for_ppm(dock, repos)



Expand Down
12 changes: 8 additions & 4 deletions R/dockerfile.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ public = list(
Dockerfile = character(),
#' @description
#' Create a new Dockerfile object.
#' @param FROM The base image.
#' @param AS The name of the image.
#' @param FROM The base image. Default `"rocker/r-base"`. (Note: the
#' high-level generators [dock_from_desc()] and [dock_from_renv()]
#' use a different default, `rocker/r-ver` tagged with your R
#' version.)
#' @param AS Optional build-stage name (`FROM ... AS <name>`). Default
#' `NULL` (no `AS`).
#' @return A Dockerfile object.
initialize = function(FROM = "rocker/r-base",
AS = NULL) {
Expand Down Expand Up @@ -80,8 +84,8 @@ LABEL = function(key, value) {
self$Dockerfile <- c(self$Dockerfile, add_label(key, value))
},
#' @description
#' Add a ENV command.
#' @param key,value The key and value of the label.
#' Add an ENV command.
#' @param key,value The key and value of the environment variable.
#' @return the Dockerfile object, invisibly.
ENV = function(key, value) {
self$Dockerfile <- c(self$Dockerfile, add_env(key, value))
Expand Down
6 changes: 6 additions & 0 deletions R/get_sysreqs.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#' @export
#'
#' @return A vector of system requirements.
#'
#' @examples
#' \dontrun{
#' get_sysreqs("glue")
#' get_sysreqs(c("curl", "xml2"))
#' }
get_sysreqs <- function(
packages,
quiet = TRUE,
Expand Down
13 changes: 9 additions & 4 deletions R/rthis.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
#' Turn an R call into an Unix call
#' Turn an R expression into a shell `R -e '...'` call
#'
#' @param code the function to call
#' Captures an R expression unevaluated and renders it as a single
#' shell-quoted `R -e '...'` string, suitable for a [Dockerfile]
#' `$RUN()` directive.
#'
#' @return an unix R call
#' @param code an R expression (captured unevaluated) to wrap.
#'
#' @return a length-1 character string of the form `R -e '...'`,
#' shell-quoted with [base::shQuote()].
#' @export
#'
#' @examples
#' r(print("yeay"))
#' r(install.packages("plumber", repo = "http://cran.irsn.fr/"))
#' r(install.packages("plumber", repos = "http://cran.irsn.fr/"))
r <- function(code) {
code <- paste(trimws(deparse(substitute(code))), collapse = " ")
glue("R -e {shQuote(code, type = 'sh')}")
Expand Down
30 changes: 25 additions & 5 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ Or from CRAN with :
install.packages("dockerfiler")
```

See `vignette("dockerfiler")` for a longer walkthrough.

## Basic workflow

By default, Dockerfiles are created with `FROM "rocker/r-base"`.
By default, `Dockerfile$new()` creates a Dockerfile with `FROM "rocker/r-base"`.
(The high-level generators `dock_from_desc()` and `dock_from_renv()` use a
different default: `rocker/r-ver` tagged with your R version; see below.)

You can set another FROM in `new()`

Expand Down Expand Up @@ -201,10 +205,26 @@ renv::snapshot(

- Build Dockerfile
```{r}
my_dock <- dock_from_renv(
lockfile = the_lockfile,
FROM = "rocker/verse"
)
my_dock <- dock_from_renv(lockfile = the_lockfile)
my_dock
```

By default the generated Dockerfile is `FROM rocker/r-ver:<your R version>`
(multi-arch: linux/amd64 + linux/arm64), pulls pre-compiled Linux binaries
from Posit Public Package Manager (`https://p3m.dev/cran/latest`, rewritten
to the `__linux__/$VERSION_CODENAME/` shape at build time), and runs the
container as the non-root `rstudio` user. Pass `FROM = "rocker/r-base"`,
`repos = c(CRAN = "https://cran.rstudio.com/")`, or `user = NULL` to restore
the previous behaviour. `dock_from_desc()` uses the same `FROM` / `repos`
defaults.

## Parse an existing Dockerfile

Already have a Dockerfile? `parse_dockerfile()` reads it back into a
`Dockerfile` object you can edit and re-`$write()`.

```{r}
my_dock <- parse_dockerfile("Dockerfile")
my_dock
```

Expand Down
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ Or from CRAN with :
install.packages("dockerfiler")
```

See `vignette("dockerfiler")` for a longer walkthrough.

## Basic workflow

By default, Dockerfiles are created with `FROM "rocker/r-base"`.
By default, `Dockerfile$new()` creates a Dockerfile with
`FROM "rocker/r-base"`. (The high-level generators `dock_from_desc()`
and `dock_from_renv()` use a different default: `rocker/r-ver` tagged
with your R version; see below.)

You can set another FROM in `new()`

Expand Down Expand Up @@ -195,10 +200,27 @@ renv::snapshot(
- Build Dockerfile

``` r
my_dock <- dock_from_renv(
lockfile = the_lockfile,
FROM = "rocker/verse"
)
my_dock <- dock_from_renv(lockfile = the_lockfile)
my_dock
```

By default the generated Dockerfile is
`FROM rocker/r-ver:<your R version>` (multi-arch: linux/amd64 +
linux/arm64), pulls pre-compiled Linux binaries from Posit Public
Package Manager (`https://p3m.dev/cran/latest`, rewritten to the
`__linux__/$VERSION_CODENAME/` shape at build time), and runs the
container as the non-root `rstudio` user. Pass `FROM = "rocker/r-base"`,
`repos = c(CRAN = "https://cran.rstudio.com/")`, or `user = NULL` to
restore the previous behaviour. `dock_from_desc()` uses the same `FROM`
/ `repos` defaults.

## Parse an existing Dockerfile

Already have a Dockerfile? `parse_dockerfile()` reads it back into a
`Dockerfile` object you can edit and re-`$write()`.

``` r
my_dock <- parse_dockerfile("Dockerfile")
my_dock
```

Expand Down
18 changes: 4 additions & 14 deletions cran-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@ clock skew on the build VM; the package itself is unaffected.)
CRAN submission; the win-builder result will follow by email
and will be forwarded to CRAN if it surfaces anything new.

## Maintainer change

This release changes the maintainer from Colin Fay
(`contact@colinfay.me`) to Vincent Guyader
(`vincent@thinkr.fr`). Both remain listed as authors. The
previous maintainer (Colin Fay) is aware of this submission and
will confirm the maintainer change by replying to the automated
email from CRAN's submission system.

## Major changes since 0.2.6

A focused 0.3.0 release. Headline bullets (full details in
Expand Down Expand Up @@ -138,10 +129,9 @@ its summary table.

## Other notes

* Test coverage stands at 99.84% (320+ tests). The single
uncovered line is a defensive `stop()` guard in
`dock_from_desc()` that fires only when `{pkgbuild}` is not
installed; since `{pkgbuild}` is in `Imports`, the guard is
unreachable in normal package use.
* Test coverage stands at 99.65% (`covr::package_coverage()`). The
handful of uncovered lines are defensive `stop()` guards that are
unreachable in normal package use; chiefly the `{pkgbuild}`-not-
installed guard in `dock_from_desc()` (`{pkgbuild}` is in `Imports`).
* No URLs in the package metadata or vignettes 404 (verified
with `urlchecker::url_check()` prior to submission).
12 changes: 8 additions & 4 deletions man/Dockerfile.Rd

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

Loading
Loading