Dependency Management with ‘roxygen’-Style Comments
Manage your source code dependencies by decorating your existing R code with special, ‘roxygen’-style comments.
#' @repo deps https://analythium.r-universe.dev
install.packages("deps", repos = "https://analythium.r-universe.dev")
#' @remote analythium/deps
remotes::install_github("analythium/deps")
Why this package?
There are many similar packages out there, some aimed at reproducibility (packrat, Require, switchr, versions, renv, capsule). Others are focused on dependency management (remotes, pak).
So why do we need another one?
Full reproducibility is an important and heavyweight aspiration that either needs locally cached package libraries, or an accurate snapshot mirroring the local system (i.e. exactly where the package was installed from). Full reproducibility is often required for reports, markdown based documents, scripts. A loosely defined project that is combined with strict versioning requirements, often erring on the side of “more dependencies are safer”.
As opposed to this, package-based development is the main use case for dependency management oriented packages. In this case, exact versions are only managed to the extent of avoiding breaking changes (given that testing can surface these). A package focused workflow combined with a “no breaking changes” philosophy to version requirements, leading to leaner installation.
What if you wanted to combine the best of both approaches? A loosely defined project with just strict-enough versioning requirements – and all this without having to write a DESCRIPTION
file. Because why would you need a DESCRIPTION
file when you have no package? Also, a DESCRIPTION
file won’t let you to pin an exact the package version or to specify alternate CRAN-like repositories…
The answer is deps: you add comments to your code, deps does the rest.
#' @remote analythium/rconfig@CRAN-v0.1.3
rconfig::config()
#' @repo sf https://r-spatial.r-universe.dev
library(sf)
#' @ver rgl 0.108.3
library(rgl)
Once you decorated your code, you can call deps::create()
to write your dependencies.json
file.
Use deps::install()
to install dependencies based on scanning a project folder or using and existing dependencies.json
file.
What deps does
Required packages are found using the renv::dependencies()
function. System dependencies can be specified as well, but are not actively sought out.
The packages list found by renv::dependencies()
is refined and modified by ‘roxygen’-style comments. But the packages need to be declared/used somewhere in the source code for the comments to take effect.
If no comment is provided for a given package, its source is assumed to be a CRAN repository, and its version to be the latest given the repositories used at the time of installation.
Tags
Tags are part of the ‘roxygen’-style comments:
#' @<tag> <parameter>
where #'
is followed by space, the tag starting with @
, space, and some parameters.
deps implements the following tags:
Tag | Description | Usage |
---|---|---|
@sys |
System requirement(s) | @sys req1,req2,... |
@remote |
Remote source(s) | @remote remote1,remote2,... |
@local |
Local source(s) | @local path1,path2,... |
@ver |
Versioned package | @ver pkg version |
@dev |
Development package(s) | @dev pkg1,pkg2,... |
@repo |
CRAN-like source | @repo pkg repo |
@repos |
Global CRAN-like repo(s) | @repos repo1,repo2,... |
@rver |
R version | @rver 4.1.3 |
System requirements
Known system requirements can be declared using the @sys
tag, packages can be separated by commas:
#' @sys curl,git
These packages are added to the list of requirements for the non-development packages.
Remote sources
Remotely sourced packages are declared using the @remote
tag following the remotes
specifications:
#' @remote analythium/rconfig@CRAN-v0.1.3
rconfig::config()
This is effectively a version pinning for remotely sourced packages.
Local packages
The @local
tag can be used to provide a path to local directory, or compressed file (tar, zip, tar.gz, tar.bz2, tgz2, or tbz) to install packages from:
#' @local mypackage_0.1.0.tar.gz
library(mypackage)
This is effectively the same as:
#' @remote local::mypackage_0.1.0.tar.gz
library(mypackage)
Development packages
Dev packages following the @dev
decorator are excluded from installation, but are not removed if already installed:
#' @dev devtools,roxygen2
requireNamespace("devtools")
Alternative repos
Use the @repo
tag to specify an alternative CRAN-like repository, e.g. from the r-universe:
Use the @repos
tag to set CRAN-like repositories for all package installation:
#' @repos http://cran.r-project.org,https://psolymos.r-universe.dev
Versioned packages
When CRAN packages require specific a version, use the @ver
tag according to the remotes::install_version()
function specifications:
R version
R version declared using the @rver
tag is recorded, but this information is not used during installation. The R version can be a major (3
), a major-minor (3.6
) or a major-minor-patch (3.6.3
) version. When the version is partially specified, the highest minor/patch version will be used. Use dots as separators.
Usage
The create()
function will crawl the project directory for package dependencies. It will amend the dependency list and package sources based on the comments. The summary is written into the dependencies.json
file. Optionally, the system requirements are written into the dependencies.json
file as well.
install()
will look for the dependencies.json
file in the root of the project directory and perform dependency installation if the file exists. If the file does not exist, it uses create()
to create that file before attempting installation but removes the dependencies.json
file afterwards, leaving no trace.
deps-cli
A CLI example is given in inst/examples/03-cli
:
Usage of the CLI as explained in deps-cli help
:
🚀 Quickly install R package dependencies on the command line
👉 MIT (c) Analythium Solutions Inc. 2022-2023
_ _ _
__| | ___ _ __ ___ ___| (_)
/ _` |/ _ \ '_ \/ __|_____ / __| | |
| (_| | __/ |_) \__ \_____| (__| | |
\__,_|\___| .__/|___/ \___|_|_|
|_|
🔗 See https://github.com/analythium/deps
deps-cli is based on deps 0.1.2 2023-03-23
Usage: deps-cli <command> [options]
Commands:
deps-cli help Print usage and exit
deps-cli version Print version and exit
deps-cli create Scan DIR and write dependencies.json
deps-cli sysreqs Install system requirements
deps-cli install Install R package dependencies
deps-cli all create & sysreqs & install in one go
Options:
--dir DIR Directory to scan, defaults to .
--upgrade Upgrade package dependencies
--silent Silent, no info printed
Examples:
deps-cli help
deps-cli version
deps-cli create
deps-cli create --silent
deps-cli sysreqs
deps-cli install --dir /root/app
deps-cli all --dir /root/app --upgrade
Analyze dependencies, install system and R dependencies in 1 line:
deps-cli install
looks for the following files before attempting to detect dependencies: renv.lock
, pkg.lock
, and DESCRIPTION
.
In a Dockerfile you can:
FROM rocker/r2u:24.04
RUN install.r remotes renv pak rconfig jsonlite yaml deps
RUN installGithub.r analythium/deps
RUN cp -p $(R RHOME)/site-library/deps/examples/03-cli/deps-cli.R /usr/local/bin/deps-cli
RUN chmod +x /usr/local/bin/deps-cli
COPY ... # your files
RUN deps-cli all
...
You can simply use the ghcr.io/analythium/deps:latest
as your parent image where the deps-cli
is already installed:
Examples
See the inst/examples
folder for more examples.
Notes
The deps package uses a 5 minutes timeout for downloads instead of the default 1 minute (getOption("timeout")
). When the timeout option or the R_DEFAULT_INTERNET_TIMEOUT
environment variable is set to a >5 minutes value, it will be respected and timeout will be set to the maximum of the three possible values.
The create()
function prompts the user asking confirmation before writing the dependencies.json
file. Use create(ask = FALSE)
to bypass the prompt.
License
MIT License © 2022-2023 Peter Solymos and Analythium Solutions Inc.