Nothing
#' Generate Docker Configuration
#'
#' @description
#' Generate a Dockerfile and docker-compose.yml for complete environment reproducibility
#'
#' @param output_dir Character. Directory to save Docker files (required).
#' @param r_version Character. R version to use. Default is current R version.
#' @param base_image Character. Base Docker image. Default "rocker/r-ver"
#' @param system_deps Character vector. System dependencies to install
#' @param project_name Character. Name for the project
#' @param include_rstudio Logical. Include RStudio Server. Default FALSE.
#'
#' @return List of generated file paths
#'
#' @export
#'
#' @examples
#' \dontrun{
#' generate_docker(
#' output_dir = tempdir(),
#' project_name = "my_analysis",
#' system_deps = c("libcurl4-openssl-dev", "libxml2-dev")
#' )
#' }
generate_docker <- function(output_dir,
r_version = NULL,
base_image = "rocker/r-ver",
system_deps = NULL,
project_name = "reproflow-project",
include_rstudio = FALSE) {
if (is.null(r_version)) {
r_version <- paste(R.version$major, R.version$minor, sep = ".")
}
# Override base image if RStudio requested
if (include_rstudio) {
base_image <- "rocker/rstudio"
}
# Create output directory if it doesn't exist
dir.create(output_dir, recursive = TRUE, showWarnings = FALSE)
# Generate Dockerfile
dockerfile_path <- file.path(output_dir, "Dockerfile")
.generate_dockerfile(
dockerfile_path,
r_version,
base_image,
system_deps
)
# Generate docker-compose.yml
compose_path <- file.path(output_dir, "docker-compose.yml")
.generate_docker_compose(
compose_path,
project_name,
include_rstudio
)
# Generate .dockerignore
dockerignore_path <- file.path(output_dir, ".dockerignore")
.generate_dockerignore(dockerignore_path)
# Generate README for Docker usage
docker_readme_path <- file.path(output_dir, "DOCKER_README.md")
.generate_docker_readme(docker_readme_path, project_name, include_rstudio)
cli::cli_alert_success("Docker configuration generated in {.file {output_dir}}")
cli::cli_ul(c(
"Dockerfile",
"docker-compose.yml",
".dockerignore",
"DOCKER_README.md"
))
invisible(list(
dockerfile = dockerfile_path,
compose = compose_path,
dockerignore = dockerignore_path,
readme = docker_readme_path
))
}
#' Generate Dockerfile
#'
#' @description
#' Internal function to generate Dockerfile content
#'
#' @keywords internal
.generate_dockerfile <- function(output_file, r_version, base_image, system_deps) {
dockerfile_lines <- c(
paste0("# Reproducible R Environment"),
paste0("# Generated by Capsule on ", Sys.time()),
paste0("FROM ", base_image, ":", r_version),
"",
"# Set working directory",
"WORKDIR /project",
"",
"# Install system dependencies",
"RUN apt-get update && apt-get install -y \\"
)
# Add system dependencies
default_deps <- c(
"libcurl4-openssl-dev",
"libssl-dev",
"libxml2-dev",
"libfontconfig1-dev",
"libharfbuzz-dev",
"libfribidi-dev",
"libfreetype6-dev",
"libpng-dev",
"libtiff5-dev",
"libjpeg-dev",
"git"
)
all_deps <- unique(c(default_deps, system_deps))
for (i in seq_along(all_deps)) {
end_char <- if (i < length(all_deps)) " \\" else ""
dockerfile_lines <- c(
dockerfile_lines,
paste0(" ", all_deps[i], end_char)
)
}
dockerfile_lines <- c(
dockerfile_lines,
" && rm -rf /var/lib/apt/lists/*",
"",
"# Install renv for package management",
"RUN R -e \"install.packages('renv', repos='https://cloud.r-project.org/')\"",
"",
"# Copy project files",
"COPY . /project/",
"",
"# Restore R packages from renv lockfile",
"RUN R -e \"if (file.exists('renv.lock')) renv::restore()\"",
"",
"# Default command",
"CMD [\"R\"]"
)
writeLines(dockerfile_lines, output_file)
cli::cli_alert_success("Dockerfile created: {.file {output_file}}")
}
#' Generate docker-compose.yml
#'
#' @description
#' Internal function to generate docker-compose configuration
#'
#' @keywords internal
.generate_docker_compose <- function(output_file, project_name, include_rstudio) {
compose_lines <- c(
"version: '3.8'",
"",
"services:",
paste0(" ", project_name, ":"),
" build: .",
" volumes:",
" - .:/project",
" - renv-cache:/renv/cache",
" environment:",
" - RENV_PATHS_CACHE=/renv/cache"
)
if (include_rstudio) {
compose_lines <- c(
compose_lines,
" - PASSWORD=rstudio",
" ports:",
" - \"8787:8787\""
)
}
compose_lines <- c(
compose_lines,
"",
"volumes:",
" renv-cache:"
)
writeLines(compose_lines, output_file)
cli::cli_alert_success("docker-compose.yml created: {.file {output_file}}")
}
#' Generate .dockerignore
#'
#' @description
#' Internal function to generate .dockerignore file
#'
#' @keywords internal
.generate_dockerignore <- function(output_file) {
dockerignore_lines <- c(
"# Git",
".git",
".gitignore",
"",
"# R",
".Rproj.user",
".Rhistory",
".RData",
".Ruserdata",
"",
"# renv",
"renv/library",
"renv/local",
"renv/cellar",
"renv/lock",
"renv/python",
"renv/sandbox",
"renv/staging",
"",
"# Docker",
"Dockerfile",
"docker-compose.yml",
".dockerignore",
"",
"# Misc",
"*.Rproj",
".DS_Store",
"Thumbs.db"
)
writeLines(dockerignore_lines, output_file)
cli::cli_alert_success(".dockerignore created: {.file {output_file}}")
}
#' Generate Docker README
#'
#' @description
#' Internal function to generate Docker usage instructions
#'
#' @keywords internal
.generate_docker_readme <- function(output_file, project_name, include_rstudio) {
readme_lines <- c(
"# Docker Setup for Reproducible R Environment",
"",
"This directory contains Docker configuration for running your R analysis in a completely reproducible environment.",
"",
"## Prerequisites",
"",
"- Docker installed on your system",
"- Docker Compose (usually comes with Docker Desktop)",
"",
"## Quick Start",
"",
"### Build the Docker image",
"",
"```bash",
"docker-compose build",
"```",
"",
"### Run the container",
""
)
if (include_rstudio) {
readme_lines <- c(
readme_lines,
"```bash",
"docker-compose up",
"```",
"",
"Then open your browser to http://localhost:8787",
"",
"- Username: rstudio",
"- Password: rstudio",
""
)
} else {
readme_lines <- c(
readme_lines,
"```bash",
"docker-compose run --rm ", project_name, " R",
"```",
""
)
}
readme_lines <- c(
readme_lines,
"### Run your analysis script",
"",
"```bash",
paste0("docker-compose run --rm ", project_name, " Rscript your_script.R"),
"```",
"",
"## Container Details",
"",
paste0("- **Service name**: ", project_name),
paste0("- **R Version**: ", paste(R.version$major, R.version$minor, sep = ".")),
"- **Package management**: renv",
"",
"## Volume Mounts",
"",
"- Current directory is mounted to `/project` in the container",
"- renv cache is persisted in a named volume for faster rebuilds",
"",
"## Customization",
"",
"### Adding System Dependencies",
"",
"Edit the `Dockerfile` and add packages to the `apt-get install` command.",
"",
"### Changing R Version",
"",
"Modify the version tag in the `FROM` line of the Dockerfile.",
"",
"## Cleaning Up",
"",
"```bash",
"# Stop containers",
"docker-compose down",
"",
"# Remove volumes (including renv cache)",
"docker-compose down -v",
"```",
"",
"## Troubleshooting",
"",
"### Packages not installing",
"",
"Try rebuilding without cache:",
"",
"```bash",
"docker-compose build --no-cache",
"```",
"",
"### Permission issues",
"",
"The container runs as root by default. Generated files will be owned by root.",
"Add this to your docker-compose.yml under the service:",
"",
"```yaml",
paste0("user: \"", Sys.info()["uid"], ":", Sys.info()["gid"], "\""),
"```"
)
writeLines(readme_lines, output_file)
cli::cli_alert_success("Docker README created: {.file {output_file}}")
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.