rig proj

Manage R projects (experimental).

A project is a directory with a package manifest, typically a DESCRIPTION file, that declares the R packages the project depends on. rig proj resolves those dependencies against the configured package repositories and can install them into a project library.

rig proj import sets up a project from an existing DESCRIPTION file: it writes rproj.toml from it, and creates the same .rvenv layout as rig proj init. rig proj add adds a dependency to rproj.toml, then updates the lockfile and installs it. rig proj deps shows the direct and recursive dependencies of the project. rig proj tree shows the recursive dependencies as a tree, so you can see how each package is pulled in. rig proj lock resolves the full dependency tree to a concrete set of package versions and writes the result to rproj.lock. rig proj sync installs the dependencies rproj.lock resolved into a package library.

See rig proj renv to interoperate with renv’s own renv.lock format.

Dependencies are resolved with rig’s built-in solver, so R does not need to be running for rig proj deps, rig proj tree and rig proj lock.

rig proj is currently experimental, and might change in future versions. Feedback is appreciated.

Subcommands

add
Add dependencies to rproj.toml
deps
Show project dependencies
export
Create a DESCRIPTION file from rproj.toml
import
Create rproj.toml from a DESCRIPTION file
init
Create a new R project
lock
Resolve project dependencies and write rproj.lock
remove
Remove dependencies from rproj.toml (alias: rm)
renv
Interoperate with renv, R’s package management tool
sync
Install the dependencies rproj.lock resolved
tree
Dependency tree of a project

rig proj add

Add one or more R packages to rproj.toml, rig’s project and package manifest, then update rproj.lock and install the new packages into the project library. Adding a package that the manifest already lists updates its version requirement.

A package is given as <package> or <package>@<version>:

rig proj add dplyr
rig proj add dplyr@1.1.0
rig proj add 'cli@>= 3.6' 'rlang@>= 1.0, < 2.0'

Without a version, the package is added as "*", i.e. any version. The concrete version rig picks is recorded in rproj.lock, so a project does not need a version requirement for every dependency; use one when the project really needs a particular version.

Because @ and the comparison operators are meaningful to most shells, quote a specification that contains a space or a > character, as in the examples above.

Version requirements

  • ^1.2.3 is compatible with 1.2.3, i.e. >= 1.2.3, < 2.0.0.
  • 1.2.3, a bare version, means the same as ^1.2.3.
  • ~1.2.3 is >= 1.2.3, < 1.3.0.
  • >= 1.2, > 1.2, <= 2.0, < 2.0 and = 1.2.3 are a single bound.
  • >= 1.0, < 2.0 — a comma means and, so both bounds hold.
  • * is any version.

A bare version is written into the manifest in its explicit ^ spelling, so the file reads the same way whether or not you know that a bare version means compatible with.

The caret and tilde forms bump one component of the version and zero the ones after it: the leftmost non-zero component for ^ (^0.2.3 is >= 0.2.3, < 0.3.0), the second component for ~. R versions can have any number of components, so ^1.1.0.9000 is >= 1.1.0.9000, < 2.0.0.0.

Options

--dev adds the packages as development dependencies, into the [dependency-groups.test] table instead of [dependencies]. These are installed by default, and left out by rig proj lock --no-dev and rig proj sync --no-dev.

--no-sync updates rproj.toml and rproj.lock, but does not install anything.

--no-lock only updates rproj.toml. Nothing is resolved or installed, so this also works offline; the manifest and the lockfile are out of step until you run rig proj lock.

Files

Only rproj.toml is edited, and it is rewritten in full, so any comments or custom formatting in it are not preserved.

If resolving the dependencies fails — most often because a package name is misspelled, and no repository has such a package — rproj.toml is restored to what it was, so a failed rig proj add does not leave the project with a dependency that cannot be installed.

Usage

rig proj add [OPTIONS] <PACKAGE>...

Arguments

<PACKAGE>...
Packages to add, as or @, e.g. dplyr or ‘dplyr@>= 1.1.0’

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
--dev
Add as a dev (development) dependency
-h, --help
Print help (see a summary with ‘-h’)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
--no-lock
Only update rproj.toml, do not update rproj.lock
--no-sync
Do not install the added packages
--user
Run in user mode (overrides RIG_MODE and config)

rig proj deps

Show the dependencies of an R project, in a table: every package the project depends on, the dependency type (Depends, Imports, LinkingTo) and the version requirement, if it has one.

rig reads the project manifest, rproj.toml, in the current directory. Add --dev to include development dependencies. Use --json for machine readable output.

The plain listing only reads the manifest, so it needs neither R nor the package repositories.

Recursive dependencies

--recursive (-r) shows the whole dependency closure: not only the packages the project needs directly, but also the packages those need, and so on. Each package appears once, with the version currently on CRAN, the Depth column giving its distance from the project, and the Needed by column naming the packages that pull it in. This needs the package metadata of the repositories, which rig downloads if it does not have it yet.

rig proj tree shows the same closure as a tree, laid out by the shape of the dependency graph, so you can see how each package is pulled in and not only that it is.

A recursive listing only ever follows hard dependencies, also below a development dependency added by --dev, so --dev --recursive means the project’s own dev dependencies plus everything they need to be installed.

rig follows the dependencies of the latest version of every package in the closure, so a version requirement that would force an older version, with different dependencies, is not taken into account. Use rig proj lock for a resolution that is consistent across versions.

Usage

rig proj deps [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
--dev
Include dev (development) dependencies
-h, --help
Print help (see a summary with ‘-h’)
--json
JSON output
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
-r, --recursive
Show recursive (transitive) dependencies
--user
Run in user mode (overrides RIG_MODE and config)

rig proj export

Read rproj.toml, rig’s project and package manifest, and write a DESCRIPTION file from it, the reverse of rig proj import.

[project] maps back to Package:, Version:, Title:, Description:, License: and Type: (defaulting to Package if [project].type is unset; written as-is otherwise, even for type = "project", since a DESCRIPTION is written regardless of whether the manifest describes an installable package). [project.authors] becomes Authors@R, one person() call per entry. [project.urls]’s homepage/source keys become URL:, and bugreports becomes BugReports:.

[dependencies] becomes Depends/Imports (an entry with attach = true, and R itself, become Depends; the rest become Imports); [linking-dependencies] becomes LinkingTo; the test and enhances dependency groups become Suggests and Enhances. Every other dependency group becomes a Config/Needs/<name> field, an entry that names a package reference (ref = "...") written out as that reference, verbatim.

DESCRIPTION’s dependency syntax only supports a single version comparison per package (pkg (>= 1.2.3)), unlike rproj.toml, which can express a two-sided range (e.g. ^1.2.3 means >= 1.2.3, < 2.0.0). When a dependency has both a lower and an upper bound, only the lower bound is written; rig prints a warning listing which packages were affected.

By default rig writes DESCRIPTION in the current directory; use --output to write to a different file. rig refuses to overwrite an existing file unless --force is given.

Usage

rig proj export [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
-f, --force
Overwrite the output file if it already exists
-h, --help
Print help (see a summary with ‘-h’)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
-o, --output <output>
Output file to write (e.g. DESCRIPTION)
--user
Run in user mode (overrides RIG_MODE and config)

rig proj import

Read a DESCRIPTION file and create rproj.toml, rig’s project and package manifest, from it: [project] (name, version, title, description, license, authors, urls) as well as dependencies. Fails if rproj.toml already exists, since populating the full [project] metadata block is not a well-defined merge onto an existing, possibly hand-edited, manifest; use --dependencies to merge into an existing file instead (see below).

Package:, Version:, Title:, Description: and License: map to the matching [project] fields. Type: becomes [project].type (defaulting to package, since a DESCRIPTION always describes one). URL: becomes [project.urls] (the first URL as homepage, the second as source), and BugReports: becomes [project.urls].bugreports.

Authors@R is parsed into [project].authors, one entry per person() call, in order; this is a best-effort parser for common person() usage (name, email, role, and an ORCID/ROR comment), not a full R parser, so unusual calls are skipped with a warning. If Authors@R is absent, the simpler Maintainer: Name <email> field is used instead, as a single author with role cre.

Depends and Imports land in the [dependencies] table (Depends packages are marked to attach on load); LinkingTo also lands in [linking-dependencies]. Suggests is imported into [dependency-groups.test] and Enhances into [dependency-groups.enhances].

Every Config/Needs/<name> field becomes a dependency group of the same name, e.g. Config/Needs/website becomes [dependency-groups.website]. Unlike a DESCRIPTION dependency field, these list package references, not just package names, so an entry that is not a plain package name (with an optional version requirement) is kept verbatim as ref = "...", under the package name the reference implies: tidyverse/tidytemplate becomes tidytemplate = { ref = "tidyverse/tidytemplate" }. rig proj export writes these back unchanged. Note that only the test and enhances groups are installed, so a Config/Needs/* group is carried in the manifest, but not solved or installed by rig proj lock and rig proj sync.

By default rig reads DESCRIPTION in the current directory; use --input to point to a different file.

Files

A full import sets up a whole project, not just its manifest, so it creates the same files as rig proj init: rproj.toml, plus the part of the project’s virtual environment that belongs in version control — .Renviron, a marked block in .gitignore and the .rvenvlib/rvenv shim package. See rig proj init for what each of them is for. .rvenv itself is machine-specific and is created by rig proj sync.

--dependencies only writes rproj.toml and never touches .rvenv.

--r-version sets the R version the project is set up for. It does not have to be installed, and it does not change what is written: the manifest’s R requirement always comes from the DESCRIPTION file, and the .rvenvlib/rvenv shim package works with every R. The default is the current default R version, or the current R release if there is no default.

rig refuses to overwrite any of the .rvenv files above; pass --force to replace them, and it does not lift the refusal to overwrite an existing rproj.toml. The .gitignore block is the exception: rig never refuses on an existing .gitignore, it just merges its block into it (or adds one), leaving the rest of the file alone, and --force does not change that.

The --dependencies option

--dependencies restores the old, dependency-only behavior: only [dependencies], [linking-dependencies] and the dependency groups are merged from the DESCRIPTION file; [project] metadata is left alone. Unlike the default full import, this does not require rproj.toml to be missing: if it exists, its dependencies are merged into it (importing a package already listed overwrites its entry with the version requirement from the DESCRIPTION file); if it does not exist, a minimal manifest is created first, named after the DESCRIPTION file’s Package: field. Because rproj.toml is rewritten in full, any comments or custom formatting in an existing file are not preserved.

Usage

rig proj import [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
--dependencies
Only merge dependencies, not metadata (the old behavior)
-f, --force
Overwrite existing project files
-h, --help
Print help (see a summary with ‘-h’)
-i, --input <input>
DESCRIPTION file to import (e.g. DESCRIPTION)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
-r, --r-version <r-version>
R version of the project (default: the version the DESCRIPTION file requires, the default R version, or the current R release)
--user
Run in user mode (overrides RIG_MODE and config)

rig proj init

Set up an R project in the current directory: the rproj.toml manifest, plus the part of the project’s virtual environment (.rvenv) that belongs in version control.

rproj.toml is rig’s modern project and package file. It describes the project’s metadata and its R and package dependencies, and can do everything a DESCRIPTION file can, plus dependency groups, optional dependencies, workspaces and declared scripts. rig proj init writes a minimal skeleton — a [project] table with the name (taken from the current directory) and version, and a [dependencies] table with a single R requirement — that you then fill in. Use rig proj import instead to set up the same project from an existing DESCRIPTION file.

Files

rig proj init creates these, and nothing else. All of them are meant to be committed, so that a fresh clone of the project works right away:

  • rproj.toml — the manifest. Its R requirement is >= <major>.<minor> of the project’s R version.
  • .Renviron — loads the rvenv package below in every R session started in the project. This is what makes the project work in an editor (RStudio, Positron, VS Code), which starts R itself.
  • .gitignore — a marked # rig rvenv start / # rig rvenv end block that ignores all of .rvenv. An existing .gitignore is not replaced: rig only adds or refreshes its own block, and leaves the rest of the file alone.
  • .rvenvlib/rvenv — a small R package that rig writes and manages. It is not a dependency of your project, and it lives in rig’s own library rather than in the project library, which holds only your project’s packages. .Renviron loads it in every R session started in the project, where it points R at the project library, .rvenv/lib, as an absolute path — so that R processes started from a subdirectory still use it — and warns while the project is out of sync with rproj.lock.

.rvenv itself, including the project library .rvenv/lib, is machine-specific and is created by rig proj sync, which installs the project’s dependencies into it. It can be deleted and rebuilt at any time; .rvenvlib is the only part of the environment that is committed.

Note that R --vanilla ignores .Renviron, and so does not use the project library.

Options

--r-version sets the R version the project is for. It does not have to be installed. Defaults to the current default R version, or the current R release if there is no default.

rig refuses to overwrite any of the files above; pass --force to replace them. The .gitignore block is the exception: rig never refuses on an existing .gitignore, it just merges its block into it (or adds one), leaving the rest of the file alone, and --force does not change that.

Usage

rig proj init [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
-f, --force
Overwrite existing project files
-h, --help
Print help (see a summary with ‘-h’)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
-r, --r-version <r-version>
R version of the project (default: the default R version, or the current R release)
--user
Run in user mode (overrides RIG_MODE and config)

rig proj lock

Resolve the dependencies of an R project to a concrete set of package versions, and write the result to rproj.lock.

rig reads the project manifest, rproj.toml, in the current directory, and uses its built-in solver to find a compatible set of package versions from the configured repositories, without running R.

Development dependencies are included by default. Use --r-version to solve for a specific R version and --no-dev to leave out development dependencies. See rig proj renv export to also write an renv.lock file.

--r-version and --platform each take a comma-separated list, to solve for several R versions and/or platforms in one rproj.lock file — rig solves the cross product of every version given against every platform given, and writes one target per combination. For example:

rig proj lock --r-version 4.5,4.6
rig proj lock --platform macos,ubuntu-24.04

Without --platform, rig locks for four platforms at once: this machine, Windows, a generic glibc Linux build (P3M’s distro-independent “manylinux” build, which covers any glibc-based x86_64 distro P3M has no specific build for), and macOS on arm64 — the common set of platforms a project needs to run on beyond the machine it was locked on. Pass --platform to lock for a different set instead, e.g. a single platform.

rig proj sync then picks the target whose platform matches the OS it runs on (the highest R version among them if more than one matches), so this default already covers deploying to a Linux server or CI from a macOS or Windows laptop: rig proj sync on each machine picks its own entry from the same file.

Workspaces

A manifest with a [workspace] table is the root of a workspace: a monorepo of several projects or packages, listed as path patterns in members, that share one rproj.lock and one package library. exclude drops directories a members pattern would otherwise match, and the root manifest is always a member of its own workspace.

rig proj lock in a workspace — from the root or from any member directory — reads every member and resolves them all in one solve, so that every member ends up with the same version of every shared dependency, and writes one rproj.lock at the workspace root. A member that depends on a sibling member is resolved against that sibling’s own dependencies. The members themselves are directories rather than packages to download, so they are not recorded in the lock file.

The R version rig solves for has to satisfy every member’s R requirement, not just the root’s.

[workspace.dependencies] declares shared version requirements. A member inherits one by name, instead of spelling out its own requirement:

# rproj.toml, the workspace root
[workspace]
members = ["packages/*"]
[workspace.dependencies]
cli = ">= 3.6.0"
# packages/mypkg/rproj.toml, a member
[dependencies]
cli = { workspace = true }

An entry no member inherits has no effect on the solve; it is a declaration, not a request. Whether a member attaches a package (attach) is still the member’s own business, and is kept when the rest of the entry is inherited.

The R version

Without --r-version rig solves for the default R version, provided the manifest’s own R requirement allows it. If it does not, rig takes the newest installed R version that does, and failing that the current R release. The version it picks does not have to be installed: rig proj lock never runs R, and rig proj sync installs the R version the lock file names.

Source and binary packages

The solver considers binary packages as well as source packages, and prefers a binary build when one is available for the same version. Which artifact each package is installed from is part of what the solve decides, because a binary is only usable together with the exact versions of its LinkingTo dependencies that it was compiled against. If those versions conflict with the rest of the project, rig picks another build of that package, or falls back to its source tarball.

By default a binary build never changes which version rig picks: the newest suitable version wins, and a binary of it is used if there is one. Pass --prefer-binary to let an older version win instead, when the newest one has no binary but an older one does — typically because a version was released so recently that it has not been built yet. Only the three newest versions of a package are considered; --prefer-binary=5 considers five. Versions held back this way are marked in the output.

Trading a version away for a binary is not free: the binary pins its LinkingTo dependencies to the versions it was compiled against, and those dependencies then prefer their own binaries in turn, so a whole project can end up on older versions.

By default rig solves for this machine plus three other platforms (see above). Use --platform to solve for a different set instead, e.g. a single specific distro:

rig proj lock --platform ubuntu-24.04

--platform source solves for source packages only, and does not download any binary package metadata. rig also falls back to source packages when there are no binaries for a platform at all. There is then nothing for --prefer-binary to prefer, and rig ignores it.

rig keeps the repository metadata and the binary package indices it solves from in its cache, and refreshes them once a day. --no-cache downloads them again instead, and writes nothing to the cache, which is the way to solve against a package that was published minutes ago. It is a good deal slower, because the metadata it re-downloads is large. See rig config.

The rproj.lock file records, for every package, whether it is a source or a binary package and the URL it is downloaded from. It also records where the file is cached, which is per build rather than per version: a repository can offer several binaries of one version for one platform and R version, and they are cached side by side.

Usage

rig proj lock [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
-h, --help
Print help (see a summary with ‘-h’)
--json
JSON output
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
--no-dev
Leave out dev (development) dependencies
--platform <platform>
Platform(s) to solve binary packages for, e.g. macos, windows, ubuntu-24.04, or a full platform string like aarch64-unknown-linux-gnu-ubuntu-24.04. Comma-separated to solve for several (e.g. --platform macos,windows). Combined with --r-version as a cross product, one target per combination. Use --platform source to solve for source packages only. Default: this machine, windows, generic glibc Linux (x86_64), and macos-arm64.
--prefer-binary[=<prefer-binary>]
Prefer an older version that has a binary package over a newer one that does not. Optionally give how many of the newest versions to consider, e.g. --prefer-binary=5 (default: 3).
-r, --r-version <r-version>
R version(s) to solve dependencies for, comma-separated to solve for several (e.g. --r-version 4.5,4.6). Combined with --platform as a cross product, one target per combination.
--user
Run in user mode (overrides RIG_MODE and config)

rig proj remove

Alias: rig proj rm

Remove one or more R packages from rproj.toml, rig’s project and package manifest, then update rproj.lock and the project library to match. A package is removed wherever it is listed — [dependencies], [linking-dependencies], or any [dependency-groups.*] table (e.g. the test group --dev adds to in rig proj add) — so there is no need to say which table a package is in.

rig proj remove dplyr
rig proj remove dplyr rlang

Naming a package that is not a dependency in rproj.toml is an error, and none of the named packages are removed if any of them is not found, so a typo cannot silently remove the wrong set of packages.

Options

--no-sync updates rproj.toml and rproj.lock, but does not touch the project library.

--no-lock only updates rproj.toml. Nothing is resolved or installed, so this also works offline; the manifest and the lockfile are out of step until you run rig proj lock.

Files

Only rproj.toml is edited, and it is rewritten in full, so any comments or custom formatting in it are not preserved.

If resolving the remaining dependencies fails, rproj.toml is restored to what it was, so a failed rig proj remove does not leave the project with a manifest that cannot be locked.

Usage

rig proj remove [OPTIONS] <PACKAGE>...

Arguments

<PACKAGE>...
Packages to remove

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
-h, --help
Print help (see a summary with ‘-h’)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
--no-lock
Only update rproj.toml, do not update rproj.lock
--no-sync
Do not re-sync the project library
--user
Run in user mode (overrides RIG_MODE and config)

rig proj renv

Convert between rig’s own project format (rproj.toml) and renv.lock, the lockfile renv uses.

rig proj renv export solves the current project’s dependencies and writes the result as renv.lock, for interop with renv or a service that consumes it (e.g. Posit Connect).

rig proj renv import reads an existing renv.lock and creates (or merges dependencies into) rproj.toml, to bring a project that uses renv into rig proj.

Subcommands

export
Write an renv.lock file from rproj.toml
import
Create rproj.toml from an renv.lock file

rig proj renv export

Resolve the dependencies of the project in the current directory (its rproj.toml manifest) with rig’s built-in solver, for one (R version, platform) target, and write the result as renv.lock.

Unlike rig proj lock, this only ever solves a single target, since renv.lock has no multi-target concept.

Without --r-version, rig solves for the default R version, provided the manifest’s own R requirement allows it; otherwise the newest installed R version that does, and failing that the current R release – the same logic rig proj lock uses. Without --platform, rig solves for this machine.

rig proj renv export
rig proj renv export --r-version 4.5 --platform ubuntu-24.04

Usage

rig proj renv export [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
-h, --help
Print help (see a summary with ‘-h’)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
--platform <platform>
Platform to solve binary packages for, e.g. macos, windows, ubuntu-24.04 (default: this machine). Use --platform source to solve for source packages only.
-r, --r-version <r-version>
R version to solve dependencies for (default: same logic as rig proj lock)
--user
Run in user mode (overrides RIG_MODE and config)

rig proj renv import

Read an renv.lock file and create rproj.toml, rig’s project manifest, from it: one dependency per locked package (with a ^-pinned version requirement), and an R requirement from the lockfile’s R version. Fails if rproj.toml already exists; use --dependencies to merge into an existing file instead (see below).

renv.lock has no project metadata (name, title, authors, …), so a new manifest is named after the current directory.

By default rig reads renv.lock in the current directory; use --input to point to a different file.

After importing, run rig proj lock to solve the dependencies and write rproj.lock.

Files

A full import sets up a whole project, not just its manifest, so it creates the same files as rig proj init: rproj.toml, plus the part of the project’s virtual environment that belongs in version control — .Renviron, a marked block in .gitignore and the .rvenvlib/rvenv shim package. See rig proj init for what each of them is for. .rvenv itself is machine-specific and is created by rig proj sync.

--dependencies only writes rproj.toml and never touches .rvenv.

--r-version sets the R version the project is set up for. It does not have to be installed, and it does not change what is written: the manifest’s R requirement always comes from the lockfile, and the .rvenvlib/rvenv shim package works with every R. The default is the default R version.

rig refuses to overwrite any of the .rvenv files above; pass --force to replace them, and it does not lift the refusal to overwrite an existing rproj.toml. The .gitignore block is the exception: rig never refuses on an existing .gitignore, it just merges its block into it (or adds one), leaving the rest of the file alone, and --force does not change that.

The --dependencies option

--dependencies does not require rproj.toml to be missing: if it exists, its dependencies are merged into it (importing a package already listed overwrites its entry with the version requirement from renv.lock); if it does not exist, a minimal manifest is created first. Because rproj.toml is rewritten in full, any comments or custom formatting in an existing file are not preserved.

Usage

rig proj renv import [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
--dependencies
Only merge dependencies, not metadata
-f, --force
Overwrite existing project files
-h, --help
Print help (see a summary with ‘-h’)
-i, --input <input>
renv.lock file to import (e.g. renv.lock)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
-r, --r-version <r-version>
R version of the project (default: the version in the renv.lock file)
--user
Run in user mode (overrides RIG_MODE and config)

rig proj sync

Bring an R project’s environment in line with its rproj.lock: install the resolved dependencies, and write the rest of the .rvenv layout.

rig looks for the project in the current directory and its parents, reads its rproj.lock (written by rig proj lock) and installs the packages into the project library, .rvenv/lib, creating it if it is not there yet. If the project has no rproj.lock yet, rig runs rig proj lock with its default options first, to create one. The project itself has to have been set up by rig proj init or rig proj import, so rig proj sync fails if .rvenvlib is missing. Pass --library to install somewhere else instead.

Development dependencies are installed by default. --no-dev leaves them out. --max-concurrent limits the number of simultaneous installations (default: 8).

By default, sync also removes any package that is in the project library but not in rproj.lock – e.g. one dropped from rproj.toml, or a leftover from before --no-dev. Pass --inexact to leave those packages alone instead.

Workspaces

In a workspace (see rig proj lock) every member shares one rproj.lock and one package library, both at the workspace root. rig proj sync from a member directory therefore syncs the whole workspace, and installs the union of every member’s dependencies into the root’s .rvenv/lib. With --no-dev it is every member’s non-development dependencies that are kept.

The repositories to install from are the workspace root’s [[repository]] tables; a member that declares its own is warned about and ignored, since there is only one library to fill.

The members themselves are directories, not packages rig installs. Building a member and installing it into the shared library is not something rig proj sync does yet.

The R version

The lock file records the R version its solve is valid for, and that is the R rig installs the packages with – not whatever R is on the PATH. It has to be that very version: another patch release of the same minor version would run the packages, but it is not the R the project was solved for, so rig does not quietly use it.

If that R version is not installed, rig installs it first, the way rig add would; pass --no-install-r to fail instead, e.g. in CI. rig never rewrites rproj.lock to an R version that is already installed – run rig proj lock to change the R version a project is locked for.

Several targets in one lock file

rig proj lock solves for several (R version, platform) targets in one rproj.lock by default (this machine, Windows, generic glibc Linux, and macOS arm64), and --r-version/--platform take a comma-separated list to solve for a different set. rig proj sync picks the target whose platform matches the OS it runs on – a target for a different OS is simply inert, which is what makes locking for a Linux deployment target from a macOS laptop work: each machine’s rig proj sync picks its own entry from the same file.

If more than one target matches this machine’s OS (typically because the project locks for several R versions), rig picks the highest R version among them, with no need for extra flags. Pass --r-version and/or --platform to pick a different one of the matching targets instead. rig proj sync fails if none of the lock file’s targets match this machine at all.

What sync writes

Everything below .rvenv is machine-specific and is not committed. The project library is filled in from the lock file, and the rest is rewritten on every sync:

  • .rvenv/bin/R and .rvenv/bin/Rscript, wrapper scripts that set the project’s environment and then hand over to the real R. Run them directly, or put .rvenv/bin on your PATH. They also pass R CMD ... through.
  • .rvenv/bin/activate and its activate.csh / activate.fish / activate.bat / Activate.ps1 siblings, for the shells that prefer to be activated. Source the one for your shell, and deactivate when you are done. Activation is a convenience, not a requirement: the wrappers work without it, and an R session started by an IDE picks the project up through the project’s .Renviron.
  • .rvenv/rvenv.cfg, which records the R version, the platform and the architecture the environment was built for. rig warns when it syncs an environment that was built for a different R.
  • .rvenv/etc/repositories, which the wrappers point R_REPOSITORIES at. It lists P3M first, at the binary URL of the platform the lock file was solved for, so that an install.packages() in the environment installs the same binary packages rig proj sync does. The repositories from rproj.toml follow it, at lower precedence. A lock file solved for source packages only has no P3M entry, and then the file holds the rproj.toml repositories alone (CRAN, if it names none).

R --vanilla ignores the project’s .Renviron, so it only stays inside the project when started through the wrappers.

After a successful sync rig records the lock file it installed from in .rvenv/lib/.synced. The rvenv package in .rvenvlib compares the two, and warns in every R session while the project library does not match rproj.lock.

Usage

rig proj sync [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
-h, --help
Print help (see a summary with ‘-h’)
--inexact
Do not remove packages from the library that are not in rproj.lock (default: remove them)
-l, --library <library>
Library path where packages should be installed (default: .rvenv/lib)
--max-concurrent <max-concurrent>
Maximum number of concurrent installations (default: 8)
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
--no-dev
Do not install dev (development) dependencies
--no-install-r
Fail if the R version the lockfile needs is not installed, instead of installing it
--platform <platform>
Which of rproj.lock’s targets to sync, when more than one matches this machine. Selects among the targets already in rproj.lock, does not trigger a new solve.
-r, --r-version <r-version>
Which of rproj.lock’s targets to sync, when more than one matches this machine (default: the highest R version). Selects among the targets already in rproj.lock, does not trigger a new solve.
--user
Run in user mode (overrides RIG_MODE and config)

rig proj tree

Show everything an R project needs, directly or indirectly, as a tree: the same closure rig proj deps --recursive lists in a flat table, laid out by the shape of the dependency graph.

myproject 0.1.0 — 3 direct, 24 total
├── R (>= 4.1) [D]
├── cli 3.6.4
│   ├── R (>= 3.4) [D]
│   └── utils
└── dplyr 1.1.4 (>= 1.1.0)
    ├── cli 3.6.4 (>= 3.4.0) (*)
    └── vctrs 0.6.5 (>= 0.6.4)
        └── cpp11 0.5.2 [L]
[Suggests]
└── testthat 3.2.3 (>= 3.1.5)

The first line names the project and its version, how many dependencies it declares directly and how many distinct packages there are in the whole tree. Each line below names a package, the version currently in the repositories, and the version requirement it is needed with, if it has one.

rig reads the project manifest, rproj.toml, in the current directory. Unlike rig proj deps, the tree needs the package metadata of the repositories, which rig downloads if it does not have it yet. It does not need R. --json gives machine readable output, as one nested object.

Reading the tree

A package that several others need is expanded only once, under its first occurrence; later occurrences are marked (*), meaning “its dependencies are above”. --dev adds the project’s development dependencies, in their own [Suggests] and [Enhances] sections; --no-base leaves out R and the base packages. Among the hard dependencies, Imports is not marked, [D] is a Depends, [L] a LinkingTo, [DL] both.

--why <package> (alias --explain) inverts the tree, so that the named package is the root and the tree grows towards the packages that need it, down to the project itself. Each line then says how that package needs the one above it, hence needs.

rig pkg tree, which shows the same tree for a package in the repositories, describes all of this in full.

rig follows the dependencies of the latest version of every package in the tree, so a version requirement that would force an older version, with different dependencies, is not taken into account. Use rig proj lock for a resolution that is consistent across versions.

Usage

rig proj tree [OPTIONS]

Options

--admin
Run in admin mode (overrides RIG_MODE and config)
--dev
Include dev (development) dependencies
-h, --help
Print help (see a summary with ‘-h’)
--json
JSON output
--no-base
Leave out R and the base packages
--no-cache
Do not read or write rig’s cache (overrides RIG_NO_CACHE and config)
--user
Run in user mode (overrides RIG_MODE and config)
--why <PACKAGE>
Invert the tree: show what pulls this package in [aliases: explain]