Title: | Practical 'R' Packaging in 'Docker' |
Version: | 0.2.0 |
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>. |
License: | MIT + file LICENSE |
Encoding: | UTF-8 |
RoxygenNote: | 7.2.3 |
Imports: | magrittr, glue, fs, rprojroot, renv, pkgbuild |
Depends: | R (≥ 2.10) |
Suggests: | rmarkdown, knitr, testthat (≥ 3.0.0), withr |
VignetteBuilder: | knitr |
Config/testthat/edition: | 3 |
URL: | https://signaturescience.github.io/pracpac/, https://github.com/signaturescience/pracpac/ |
BugReports: | https://github.com/signaturescience/pracpac/issues |
NeedsCompilation: | no |
Packaged: | 2023-06-17 00:03:12 UTC; vpnagraj |
Author: | Stephen Turner |
Maintainer: | VP Nagraj <nagraj@nagraj.net> |
Repository: | CRAN |
Date/Publication: | 2023-06-18 22:40:05 UTC |
pracpac: 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.
Author(s)
Maintainer: VP Nagraj nagraj@nagraj.net (ORCID)
Authors:
Stephen Turner (ORCID)
Other contributors:
Signature Science, LLC. [copyright holder]
See Also
Useful links:
Report bugs at https://github.com/signaturescience/pracpac/issues
Pipe operator
Description
See magrittr::%>%
for details.
Usage
lhs %>% rhs
Arguments
lhs |
A value or the magrittr placeholder. |
rhs |
A function call using the magrittr semantics. |
Value
The result of calling rhs(lhs)
.
Add assets for the specified use case
Description
Add template assets for the use case specified in add_dockerfile or use_docker.
Usage
add_assets(
pkg_path = ".",
img_path = NULL,
use_case = "default",
overwrite = TRUE
)
Arguments
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 |
Details
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.
Value
Invisibly returns assets per handle_use_case. Called primarily for its side effects.
Examples
## 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)
Add a Dockerfile to the docker directory
Description
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.
Usage
add_dockerfile(
pkg_path = ".",
img_path = NULL,
use_renv = TRUE,
use_case = "default",
base_image = NULL,
repos = NULL
)
Arguments
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 |
Details
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.
Value
Invisibly returns a list of package info returned by pkg_info. Primarily called for side-effect to create Dockerfile.
Examples
## 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)
Build a Docker image
Description
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.
Usage
build_image(
pkg_path = ".",
img_path = NULL,
cache = TRUE,
tag = NULL,
build = TRUE
)
Arguments
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 |
Value
Invisibly returns the docker build
command. Primarily called for its side effects, which runs the docker build
as a system command.
Examples
## 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)
Build a package tar.gz
Description
Builds a package source tar.gz using pkgbuild::build and moves it into a user-specified location (default docker/
).
Usage
build_pkg(pkg_path = ".", img_path = NULL, ...)
Arguments
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. |
Value
Invisibly returns a list of package info returned by pkg_info, tar.gz source and destination file paths.
Examples
## 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)
Create Docker directory
Description
Creates a docker/
directory for a given package. By default, assumes that docker/
should be a subdirectory of the specified package path.
Usage
create_docker_dir(pkg_path = ".", img_path = NULL)
Arguments
pkg_path |
Path to the package directory. Default is |
img_path |
Path to the write the docker image definition contents. The default |
Details
This function is run as part of use_docker but can be used on its own.
Value
Invisibly returns a list of package info returned by pkg_info. Primarily called for side-effect to create docker directory.
Examples
## 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)
Handle the use case
Description
This unexported helper function internally handles the provided use case.
Usage
handle_use_case(use_case)
Arguments
use_case |
The specified use case. |
Value
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).
Get information about the current package
Description
Returns information about the current package in a list which can be passed to other functions.
Usage
pkg_info(pkg_path = ".", ...)
Arguments
pkg_path |
Path to the package directory. Default is |
... |
Arguments passed to rprojroot::find_package_root_file. |
Value
A list of information about the package.
-
pkgroot
: Root directory of the package. -
pkgdeps
: Package dependencies fromImports
in theDESCRIPTION
. -
descfile
: File path to theDESCRIPTION
file. -
pkgname
: Package name. -
pkgver
: Package version.
Examples
## 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)
Find package root
Description
Unexported helper to find the root of the R package. Returns an error if the path specified is not an R package.
Usage
pkg_root(pkg_path = ".", ...)
Arguments
pkg_path |
Path to the package directory. Default is |
... |
Arguments passed to rprojroot::find_package_root_file. |
Value
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
Description
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.
Usage
renv_deps(
pkg_path = ".",
img_path = NULL,
other_packages = NULL,
overwrite = TRUE,
consent_renv = TRUE
)
Arguments
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 |
Details
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.
Value
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.
Examples
## 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)
Use docker packaging tools
Description
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 anrenv.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.
Usage
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
)
Arguments
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 |
Value
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
).
Examples
## 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)