Title: | Practical 'R' Packaging in 'Docker' |
---|---|
Description: | Streamline the creation of 'Docker' images with 'R' packages and dependencies embedded. The 'pracpac' package provides a 'usethis'-like interface to creating Dockerfiles with dependencies managed by 'renv'. The 'pracpac' functionality is described in Nagraj and Turner (2023) <doi:10.48550/arXiv.2303.07876>. |
Authors: | Stephen Turner [aut] , VP Nagraj [cre, aut] , Signature Science, LLC. [cph] |
Maintainer: | VP Nagraj <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.2.0 |
Built: | 2024-11-11 05:00:11 UTC |
Source: | https://github.com/signaturescience/pracpac |
Add template assets for the use case specified in add_dockerfile or use_docker.
add_assets( pkg_path = ".", img_path = NULL, use_case = "default", overwrite = TRUE )
add_assets( pkg_path = ".", img_path = NULL, use_case = "default", overwrite = TRUE )
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
use_case |
Name of the use case. Defaults to |
overwrite |
Logical; should existing assets should be overwritten? Default is |
Example #1: the "shiny"
use case requires than an app.R
file moved into
/srv/shiny-server/
in the container image. Using add_assets(use_case="shiny")
(or when using the "shiny"
use case in add_dockerfile or use_docker)
will create a placeholder assets/app.R
in the docker/
directory. The
Dockerfile for the "shiny"
use case will place COPY assets/app.R/srv/shiny-server
into the Dockerfile.
Example #2: the "pipeline"
use case creates boilerplate for moving pre- and
post-processing R and shell scripts into the container at
add_assets(use_case="pipeline")
(or when using the "pipeline"
use case in
add_dockerfile or use_docker) will create a placeholder assets/pre.R
,
assets/post.R
, and assets/run.sh
into the docker/assets
directory. The
Dockerfile for the "pipeline"
use case will place COPY assets/run.sh /run.sh
into the Dockerfile.
This function is run as part of use_docker but can be used on its own.
See vignette("use-cases", package="pracpac")
for details on use cases.
Invisibly returns assets per handle_use_case. Called primarily for its side effects.
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Add assets for shiny use case add_assets(pkg_path = file.path(tempdir(), "hellow"), use_case="shiny") # Add assets for pipeline use case add_assets(pkg_path = file.path(tempdir(), "hellow"), use_case="pipeline") ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Add assets for shiny use case add_assets(pkg_path = file.path(tempdir(), "hellow"), use_case="shiny") # Add assets for pipeline use case add_assets(pkg_path = file.path(tempdir(), "hellow"), use_case="pipeline") ## End(Not run)
Adds a Dockerfile to the docker directory created by create_docker_dir. Allows for specification of several preset use cases, whether or not use use renv to manage dependencies, and optional overriding the base image.
add_dockerfile( pkg_path = ".", img_path = NULL, use_renv = TRUE, use_case = "default", base_image = NULL, repos = NULL )
add_dockerfile( pkg_path = ".", img_path = NULL, use_renv = TRUE, use_case = "default", base_image = NULL, repos = NULL )
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
use_renv |
Logical; use renv? Defaults to |
use_case |
Name of the use case. Defaults to |
base_image |
Name of the base image to start |
repos |
Option to override the repos used for installing packages with |
This function is run as part of use_docker but can be used on its own.
See vignette("use-cases", package="pracpac")
for details on use cases.
Invisibly returns a list of package info returned by pkg_info. Primarily called for side-effect to create Dockerfile.
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Default: FROM rocker/r-ver:latest with no additional template # By default add_dockerfile requires you either to specify use_renv = FALSE # Or run renv_deps() prior to add_dockerfile() # The use_docker() wrapper runs these sequentially, and is recommended for most usage add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_renv = FALSE) # Specify tidyverse base image renv_deps(pkg_path = file.path(tempdir(), "hellow")) add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), base_image="rocker/tidyverse:4.2.2") # Specify different default repo add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), repos="https://cran.wustl.edu/") # RStudio template add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_case="rstudio") # Shiny template add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_case = "shiny") # Pipeline template add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_case="pipeline") ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Default: FROM rocker/r-ver:latest with no additional template # By default add_dockerfile requires you either to specify use_renv = FALSE # Or run renv_deps() prior to add_dockerfile() # The use_docker() wrapper runs these sequentially, and is recommended for most usage add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_renv = FALSE) # Specify tidyverse base image renv_deps(pkg_path = file.path(tempdir(), "hellow")) add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), base_image="rocker/tidyverse:4.2.2") # Specify different default repo add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), repos="https://cran.wustl.edu/") # RStudio template add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_case="rstudio") # Shiny template add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_case = "shiny") # Pipeline template add_dockerfile(pkg_path = file.path(tempdir(), "hellow"), use_case="pipeline") ## End(Not run)
Builds a Docker image created by use_docker or add_dockerfile. This function is run as part of use_docker when build = TRUE
is set, but can be used on its own.
build_image( pkg_path = ".", img_path = NULL, cache = TRUE, tag = NULL, build = TRUE )
build_image( pkg_path = ".", img_path = NULL, cache = TRUE, tag = NULL, build = TRUE )
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
cache |
Logical; should caching be used? Default |
tag |
Image tag to use; default is |
build |
Logical as to whether or not the image should be built. Default is |
Invisibly returns the docker build
command. Primarily called for its side effects, which runs the docker build
as a system command.
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Run use_docker to create Docker directory and assets for the example package use_docker(pkg_path = file.path(tempdir(), "hellow")) # Build the image build_image(pkg_path = file.path(tempdir(), "hellow")) # Or construct the image build command without building build_cmd <- build_image(pkg_path = file.path(tempdir(), "hellow"), build=FALSE) build_cmd ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Run use_docker to create Docker directory and assets for the example package use_docker(pkg_path = file.path(tempdir(), "hellow")) # Build the image build_image(pkg_path = file.path(tempdir(), "hellow")) # Or construct the image build command without building build_cmd <- build_image(pkg_path = file.path(tempdir(), "hellow"), build=FALSE) build_cmd ## End(Not run)
Builds a package source tar.gz using pkgbuild::build and moves it into a user-specified location (default docker/
).
build_pkg(pkg_path = ".", img_path = NULL, ...)
build_pkg(pkg_path = ".", img_path = NULL, ...)
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
... |
Additional optional arguments passed to pkgbuild::build. |
Invisibly returns a list of package info returned by pkg_info, tar.gz source and destination file paths.
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Build the example package from tempdir() build_pkg(pkg = file.path(tempdir(), "hellow")) ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Build the example package from tempdir() build_pkg(pkg = file.path(tempdir(), "hellow")) ## End(Not run)
Creates a docker/
directory for a given package. By default, assumes that docker/
should be a subdirectory of the specified package path.
create_docker_dir(pkg_path = ".", img_path = NULL)
create_docker_dir(pkg_path = ".", img_path = NULL)
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
This function is run as part of use_docker but can be used on its own.
Invisibly returns a list of package info returned by pkg_info. Primarily called for side-effect to create docker directory.
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Assuming default behavior then docker/ will be created under source root create_docker_dir(pkg_path = file.path(tempdir(), "hellow")) # Alternatively you can specify another directory above, below, or beside package source create_docker_dir(pkg_path = file.path(tempdir(), "hellow"), img_path = file.path(tempdir(), "img")) ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Assuming default behavior then docker/ will be created under source root create_docker_dir(pkg_path = file.path(tempdir(), "hellow")) # Alternatively you can specify another directory above, below, or beside package source create_docker_dir(pkg_path = file.path(tempdir(), "hellow"), img_path = file.path(tempdir(), "img")) ## End(Not run)
This unexported helper function internally handles the provided use case.
handle_use_case(use_case)
handle_use_case(use_case)
use_case |
The specified use case. |
List of parsed information for the use case including, the name of the use case, path to Dockerfile template, base image, and path to assets (delimited by ;
if there are multiple and NA
if there are none).
Returns information about the current package in a list which can be passed to other functions.
pkg_info(pkg_path = ".", ...)
pkg_info(pkg_path = ".", ...)
pkg_path |
Path to the package directory. Default is |
... |
Arguments passed to rprojroot::find_package_root_file. |
A list of information about the package.
pkgroot
: Root directory of the package.
pkgdeps
: Package dependencies from Imports
in the DESCRIPTION
.
descfile
: File path to the DESCRIPTION
file.
pkgname
: Package name.
pkgver
: Package version.
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # This will succeed if this is a package pkg_info(pkg_path = file.path(tempdir(), "hellow")) # This will fail if this is not a package location pkg_info(pkg_path = tempdir()) ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # This will succeed if this is a package pkg_info(pkg_path = file.path(tempdir(), "hellow")) # This will fail if this is not a package location pkg_info(pkg_path = tempdir()) ## End(Not run)
Unexported helper to find the root of the R package. Returns an error if the path specified is not an R package.
pkg_root(pkg_path = ".", ...)
pkg_root(pkg_path = ".", ...)
pkg_path |
Path to the package directory. Default is |
... |
Arguments passed to rprojroot::find_package_root_file. |
A file path of the package root. If no package is found at the root then the function will stop
with an error message.
Get dependencies using renv. This function will inspect your package specified
at pkg_path
(default is current working directory, .
), and create an renv lock file (renv.lock
) in
the docker/
directory. More information about the renv
implementation is provided in the Details section.
renv_deps( pkg_path = ".", img_path = NULL, other_packages = NULL, overwrite = TRUE, consent_renv = TRUE )
renv_deps( pkg_path = ".", img_path = NULL, other_packages = NULL, overwrite = TRUE, consent_renv = TRUE )
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
other_packages |
Vector of other packages to be included in |
overwrite |
Logical; should an existing lock file should be overwritten? Default is |
consent_renv |
Logical; give renv consent in this session with |
The renv.lock
file will capture all your package's dependencies (and all
their dependencies) at the current version installed on your system at the
time this function is run. When using the default use_renv=TRUE
in
use_docker or add_dockerfile, the resulting Dockerfile
will install
packages from this renv.lock
file using renv::restore. This ensures that
versions of dependencies in the image mirror what is installed on your system
at the time of image creation, rather than potentially newer versions on package repositories like
CRAN or Bioconductor, which may come with breaking changes that you are unaware of at the
time of package development.
If there are additional R packages that may be useful for the Docker image you plan to build (but may not be captured under your package dependencies), then you can add these packages to the renv
procedure with the "other_packages" argument.
This function is run as part of use_docker but can be used on its own.
Invisibly returns a list of package info returned by pkg_info. Primarily called for side effect. Writes an renv
lock file to the docker/ directory.
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Run using defaults; only gets current package dependencies renv_deps(pkg_path = file.path(tempdir(), "hellow")) # Add additional packages not explicitly required by your package renv_deps(pkg_path = file.path(tempdir(), "hellow"), other_packages=c("shiny", "knitr")) ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Run using defaults; only gets current package dependencies renv_deps(pkg_path = file.path(tempdir(), "hellow")) # Add additional packages not explicitly required by your package renv_deps(pkg_path = file.path(tempdir(), "hellow"), other_packages=c("shiny", "knitr")) ## End(Not run)
Wrapper function around other pracpac
functions. See help for the functions linked below for detail on individual functions.
All arguments to use_docker()
are passed to downstream functions. use_docker()
will sequentially run:
pkg_info to get information about the current R package.
create_docker_dir to create the docker/
directory in the specified location, if it doesn't already exist.
renv_deps (if use_renv=TRUE
, the default) to capture package dependencies with renv and create an renv.lock
file
add_dockerfile to create a Dockerfile using template specified by use_case
add_assets depending on the use_case
build_pkg to build the current R package source .tar.gz, and place it into the docker/
directory
build_image optional, default FALSE
; if TRUE, will build the Docker image.
The default build=FALSE
means that everything up to build_image()
is run,
but the image is not actually built. Instead, use_docker()
will message the
docker build
command, and return that string in $buildcmd
in the
invisibly returned output.
See vignette("use-cases", package="pracpac")
for details on use cases.
use_docker( pkg_path = ".", img_path = NULL, use_renv = TRUE, use_case = "default", base_image = NULL, other_packages = NULL, build = FALSE, repos = NULL, overwrite_assets = TRUE, overwrite_renv = TRUE, consent_renv = TRUE )
use_docker( pkg_path = ".", img_path = NULL, use_renv = TRUE, use_case = "default", base_image = NULL, other_packages = NULL, build = FALSE, repos = NULL, overwrite_assets = TRUE, overwrite_renv = TRUE, consent_renv = TRUE )
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
use_renv |
Logical; use renv? Defaults to |
use_case |
Name of the use case. Defaults to |
base_image |
Name of the base image to start |
other_packages |
Vector of other packages to be included in |
build |
Logical as to whether or not the image should be built. Default is |
repos |
Option to override the repos used for installing packages with |
overwrite_assets |
Logical; should existing asset files should be overwritten? Default is |
overwrite_renv |
Logical; should an existing lock file should be overwritten? Default is |
consent_renv |
Logical; give renv consent in this session with |
Invisibly returns a list with information about the package ($info
) and
the docker build
command ($buildcmd
). Primarily called for side effect.
Creates docker/
directory, identifies renv dependencies and creates lock
file (if use_renv = TRUE
), writes Dockerfile, builds package tar.gz,
moves all relevant assets to the docker/
directory, and builds Docker
image (if build = TRUE
).
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Run use_docker to create Docker directory and assets for the example package use_docker(pkg_path = file.path(tempdir(), "hellow")) # To not use renv use_docker(pkg_path = file.path(tempdir(), "hellow"), use_renv=FALSE) # To specify a use case use_docker(pkg_path = file.path(tempdir(), "hellow"), use_case="pipeline") # To overwrite the default base image use_docker(pkg_path = file.path(tempdir(), "hellow"), base_image="alpine:latest") ## End(Not run)
## Not run: # Specify path to example package source and copy to tempdir() # Note that in practice you do not need to copy to a tempdir() # And in fact it may be easiest to use pracpac relative to your package directory root ex_pkg_src <- system.file("hellow", package = "pracpac", mustWork = TRUE) file.copy(from = ex_pkg_src, to = tempdir(), recursive = TRUE) # Run use_docker to create Docker directory and assets for the example package use_docker(pkg_path = file.path(tempdir(), "hellow")) # To not use renv use_docker(pkg_path = file.path(tempdir(), "hellow"), use_renv=FALSE) # To specify a use case use_docker(pkg_path = file.path(tempdir(), "hellow"), use_case="pipeline") # To overwrite the default base image use_docker(pkg_path = file.path(tempdir(), "hellow"), base_image="alpine:latest") ## End(Not run)