{"id":17550849,"url":"https://github.com/dmolitor/jetty","last_synced_at":"2025-04-11T12:06:46.705Z","repository":{"id":200081453,"uuid":"704343069","full_name":"dmolitor/jetty","owner":"dmolitor","description":"Execute R functions within the context of a Docker container.","archived":false,"fork":false,"pushed_at":"2025-01-24T23:47:33.000Z","size":4404,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-05T01:48:14.507Z","etag":null,"topics":["docker","reproducible-research","rstats"],"latest_commit_sha":null,"homepage":"http://www.dmolitor.com/jetty/","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmolitor.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-13T04:10:33.000Z","updated_at":"2025-03-31T20:18:12.000Z","dependencies_parsed_at":"2023-10-15T17:10:47.866Z","dependency_job_id":"49349995-a553-432c-992b-21a58ada2072","html_url":"https://github.com/dmolitor/jetty","commit_stats":null,"previous_names":["dmolitor/pod"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmolitor%2Fjetty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmolitor%2Fjetty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmolitor%2Fjetty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmolitor%2Fjetty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmolitor","download_url":"https://codeload.github.com/dmolitor/jetty/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248396355,"owners_count":21096920,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","reproducible-research","rstats"],"created_at":"2024-10-21T04:43:57.953Z","updated_at":"2025-04-11T12:06:46.677Z","avatar_url":"https://github.com/dmolitor.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput:\n  github_document:\n    toc: false\nalways_allow_html: yes\neditor_options:\n  markdown:\n    wrap: sentence\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"80%\",\n  dpi = 300\n)\n```\n\n# jetty \u003cimg src='man/figures/logo-no-bg.png' align=\"right\" height=\"139\"/\u003e\n\n\u003c!-- badges: start --\u003e\n[![pkgdown](https://github.com/dmolitor/jetty/actions/workflows/pkgdown.yaml/badge.svg)](https://github.com/dmolitor/jetty/actions/workflows/pkgdown.yaml)\n[![R-CMD-check](https://github.com/dmolitor/jetty/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/dmolitor/jetty/actions/workflows/R-CMD-check.yaml)\n[![CRAN status](https://www.r-pkg.org/badges/version/jetty)](https://CRAN.R-project.org/package=jetty)\n\u003c!-- badges: end --\u003e\n\n\u003e Execute R functions or code blocks within a Docker container.\n\nIt may be useful, in certain circumstances, to perform a computation in a \nseparate R process that is running within a Docker container. This package\nattempts to achieve this!\n\n## Features\n\n- Calls an R function with arguments or a code block in a subprocess \nwithin a Docker container.\n\n- Copies function arguments (as necessary) to the subprocess and copies the \nreturn value of the function/code block.\n\n- Discovers and installs required packages in the Docker container at run-time.\n\n- Copies error objects back from the subprocess. In general, these error objects\ndo not include the stack trace from the Docker R process. However, if for example\nthe error is an rlang error, it will include the full stack trace.\n\n- Shows and/or collects the standard output and standard error of the Docker\nsubprocess.\n\n- Executes an R script in a subprocess within a Docker container.\nThe user specifies a directory to mount, enabling the script to interact with its contents.\n\n## Installation\n\nInstall jetty from CRAN:\n```{r, eval = FALSE}\ninstall.packages(\"jetty\")\n```\n\nOr install the development version of jetty from GitHub:\n```{r, eval = FALSE}\n# install.packages(\"pak\")\npak::pkg_install(\"dmolitor/jetty\")\n```\n\n## Synchronous, one-off R processes in a Docker container\n\nUse `run()` to execute an R function or code block in a new R process within a\nDocker container. The results are passed back directly to the local R session.\n\n```{r}\njetty::run(function() var(iris[, 1:4]))\n```\n\n### Specifying Docker container\n\nThe desired Docker container can be set via the `image` argument, and should be\nspecified as a string in standard Docker format. These formats include\n`username/image:tag`, `username/image`, `image:tag`, and `image`. The default\nchoice is `r-base:{jetty:::r_version()}` which is a bare-bones R image that mirrors\nthe R version running locally. For example, the following command would be\nexecuted in the official [`r-base`](https://hub.docker.com/_/r-base)\nimage with the latest version of R, which comes with no packages beyond the\nbase set installed:\n\n```{r, eval = FALSE}\njetty::run(function() var(iris[, 1:4]), image = \"r-base:latest\")\n```\n\n### Passing arguments\n\nYou can pass arguments to the function by setting `args` to the list of\narguments, similar to the base `do.call` function. This is often necessary, as\nthe function being evaluated in the Docker R process does not have access to\nvariables in the parent process. For example, the following does not work:\n```{r, error = TRUE}\nmycars \u003c- cars\njetty::run(function() summary(mycars))\n```\n\nBut this does:\n```{r}\nmycars \u003c- cars\njetty::run(function(x) summary(x), args = list(mycars))\n```\n\n### Using packages\n\nYou can use any package in the child R process, with the caveat that the\npackage must be installed in the Docker container. While it's recommended to\nrefer to it explicitly with the `::` operator, the code snippet can also call\n`library()` or `require()` and will work fine. For example, the following code\nsnippets both work equally well:\n\n```{r}\njetty::run(\n  {\n    library(Matrix);\n    function(nrow, ncol) rsparsematrix(nrow, ncol, density = 1)\n  },\n  args = list(nrow = 10, ncol = 2)\n)\n```\n\nand\n```{r}\njetty::run(\n  function(nrow, ncol) Matrix::rsparsematrix(nrow, ncol, density = 1),\n  args = list(nrow = 10, ncol = 2)\n)\n```\n\n#### Installing required packages\n\njetty also supports installing required packages at runtime. For example,\nthe following code will fail because the required packages are not installed\nin the Docker image:\n```{r, error = TRUE}\njetty::run(\n  {\n    ggplot2::ggplot(mtcars, ggplot2::aes(x = hp, y = mpg)) +\n      ggplot2::geom_point()\n  }\n)\n```\n\nHowever, by setting `install_dependencies = TRUE` we can tell jetty to discover\nthe required packages and install them before executing the code:\n```{r}\njetty::run(\n  {\n    ggplot2::ggplot(mtcars, ggplot2::aes(x = hp, y = mpg)) +\n      ggplot2::geom_point()\n  },\n  install_dependencies = TRUE,\n  stdout = TRUE\n)\n```\n\n**Note**: this feature uses [`renv::dependencies`](https://rstudio.github.io/renv/reference/dependencies.html)\nto discover the required packages, and won't handle all possible scenarios.\nIn particular, it won't install specific package versions (just the latest version)\nand it will only install packages that are on CRAN. Use this with care!\n\n### Error handling\n\njetty copies errors from the child R process to the main R session:\n\n```{r, error = TRUE}\njetty::run(function() 1 + \"A\")\n```\n\nAlthough the errors themselves are propagated to the main R session, the stack\ntrace is (currently) not propagated. This means that calling functions such as\n`traceback()` and `rlang::last_trace()` won't be of any help.\n\n### Standard output and error\n\nBy default, the standard output and error of the Docker subprocess are printed\nto the R console. However, since jetty uses `system2()` to execute all Docker\ncommands, you can specify the `stdout` and `stderr` arguments which will\nbe passed directly to `system2()`. For example the following code will print\na series of messages to the console:\n```{r, eval = FALSE, message = TRUE}\njetty::run({for (i in 1:5) message(paste0(\"iter\", i)); TRUE})\n#\u003e iter1\n#\u003e iter2\n#\u003e iter3\n#\u003e iter4\n#\u003e iter5\n#\u003e [1] TRUE\n```\n\nBut you can discard this output by setting `stdout = FALSE`:\n```{r, eval = FALSE, message = TRUE}\njetty::run({for (i in 1:5) message(paste0(\"iter\", i)); TRUE}, stdout = FALSE)\n#\u003e [1] TRUE\n```\n\nTo see more details on controlling `stdout` and `stderr`, check out the\n[documentation here](https://stat.ethz.ch/R-manual/R-devel/library/base/html/system2.html).\n\n### .Rprofile and .Renviron\n\njetty also provides some support for `.Rprofile` and `.Renviron` files. By\ndefault, jetty will search for files called \".Rprofile\" and \".Renviron\" in\nthe current working directory. If these files exist, jetty will port them\nto the Docker execution environment and will execute any code in `.Rprofile`\nand load all environment variables in `.Renviron` before executing the provided\nR code. If the `.Rprofile` file uses external packages, it is\nessential to tell jetty to install required packages (as described above)\notherwise the code will fail.\n\nThe user can explicitly provide `.Rprofile` and `.Renviron` file paths via the\n`r_profile` and `r_environ` arguments. For example, the following code will\nattach the `.Rprofile` found in the `/man/scaffolding/` sub-directory of the\ncurrent working directory. This file simply uses the\n[praise](https://github.com/rladies/praise) package to provide some\nencouragement at the start of a new R session.\n```{r, eval = FALSE, message = TRUE}\nfour \u003c- jetty::run(\n  \\() 2 + 2,\n  r_profile = here::here(\"man/scaffolding/.Rprofile\"),\n  install_dependencies = TRUE\n)\n#\u003e Installing package into ‘/usr/local/lib/R/site-library’\n#\u003e (as ‘lib’ is unspecified)\n#\u003e trying URL 'https://r-lib.github.io/p/pak/stable/source/linux-gnu/aarch64/src/contrib/../../../../../linux/aarch64/pak_0.8.0_R-4-4_aarch64-linux.tar.gz'\n#\u003e Content type 'application/gzip' length 8847947 bytes (8.4 MB)\n#\u003e ==================================================\n#\u003e downloaded 8.4 MB\n#\u003e \n#\u003e * installing *binary* package ‘pak’ ...\n#\u003e * DONE (pak)\n#\u003e \n#\u003e The downloaded source packages are in\n#\u003e \t‘/tmp/RtmpNxnaJk/downloaded_packages’\n#\u003e ✔ Updated metadata database: 3.07 MB in 8 files.\n#\u003e ✔ Updating metadata database ... done\n#\u003e  \n#\u003e → Will install 1 package.\n#\u003e → Will download 1 CRAN package (6.10 kB).\n#\u003e + praise   1.0.0 [bld][dl] (6.10 kB)\n#\u003e   \n#\u003e ℹ Getting 1 pkg (6.10 kB)\n#\u003e ✔ Got praise 1.0.0 (source) (6.10 kB)\n#\u003e ℹ Building praise 1.0.0\n#\u003e ✔ Built praise 1.0.0 (403ms)\n#\u003e ✔ Installed praise 1.0.0  (7ms)\n#\u003e ✔ 1 pkg: added 1, dld 1 (6.10 kB) [3.8s]\n#\u003e You are exquisite!\n```\n\nHowever, as noted above, this fails if `install_dependencies = FALSE`.\n```{r, error = TRUE}\nfour \u003c- jetty::run(\n  \\() 2 + 2,\n  r_profile = here::here(\"man/scaffolding/.Rprofile\")\n)\n```\n\n#### Multiple .Rprofile or .Renviron files\n\nCurrently jetty only supports single `.Rprofile` or `.Renviron` files. So,\nfor example, if a user has a project-specific .Rprofile in the current\nworking directory at `./.Rprofile` and then a user-specific .Rprofile at\n`~/.Rprofile`, jetty will only source `./.Rprofile` and will ignore\n`~/.Rprofile`. This is a feature I plan to add before long.\n\n## Executing R scripts in a Docker container\n\nWhile the primary goal of jetty is to execute a function or\ncode chunk in an R subprocess running within a Docker container, it also\nsupports the execution of entire scripts via the `run_script()` function. This\nfeature may be useful when you want to execute a script in an isolated\nenvironment such as for reproducible scientific code. It is particularly\nhelpful when executing scripts that require specific R packages,\ndifferent versions of R, or a clean environment to avoid conflicts with your\nsystem's setup.\n\nIn order to allow seamless interactions between the Docker subprocess\nand the local file system, the user must specify an execution context—a\nlocal directory that will be mounted into the Docker container. This\ncontext directory ensures that the script can access files within it, enabling\nthe script to read data from or write results back to that directory.\nThe context directory is important because it limits the script's file access to this\ndirectory, preventing it from interacting with files outside of the\nspecified scope.\n\nFor example, suppose we are working within an R project and the script we want\nto execute needs access to all files within the project. We\ncan achieve this by setting the context directory as the full project directory:\n```{r, eval = FALSE}\njetty::run_script(\n  file = here::here(\"code/awesome_script.R\"),\n  context = here::here()\n)\n```\n\n`run_script()` and `run()` share a lot of functionality. For example,\nif the script above relies on packages that aren't installed in the Docker\ncontainer, you can instruct jetty to install these packages\nat runtime:\n```{r, eval = FALSE}\njetty::run_script(\n  file = here::here(\"code/awesome_script.R\"),\n  context = here::here(),\n  install_dependencies = TRUE\n)\n```\n\nAll the features discussed above for synchronous, one-off R processes also apply to `run_script()`.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmolitor%2Fjetty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmolitor%2Fjetty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmolitor%2Fjetty/lists"}