diff --git a/NEWS.md b/NEWS.md index 62681a07..d5d42c79 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # Development version +## Inline scripts + * `rig run` now support self-contained scripts, that declare the packages and the R version they need in a `# /// script` comment block. rig then runs the script in its own environment, in the cache directory, and @@ -16,6 +18,12 @@ solve the dependencies again, e.g. after `rig proj add`, as long as they still fit `rproj.toml`. Use `--upgrade` to pick the latest versions. +* `rig proj init`, `rig proj add` and `rig proj remove` have a new + `--script` option, to create and edit the `# /// script` block of an R + script, instead of a project's `rproj.toml` (#414). + +## Other new features + * `rig add` now has a `--json` option. It prints information about the installed R version (or the already installed version that was kept), including whether it is a new installation and whether it is the default. diff --git a/src/args.rs b/src/args.rs index 420e2941..945e0c07 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1522,6 +1522,14 @@ pub fn rig_app() -> Command { .short('r') .num_args(1) .required(false), + ) + .arg( + Arg::new("script") + .help("Create the inline metadata block of this R script, instead of a project") + .long("script") + .value_name("SCRIPT") + .num_args(1) + .required(false), ), ) .subcommand( @@ -1602,6 +1610,15 @@ pub fn rig_app() -> Command { .help("Add as a dev (development) dependency") .long("dev") .num_args(0) + .required(false) + .conflicts_with("script"), + ) + .arg( + Arg::new("script") + .help("Add the packages to the inline metadata of this R script, instead of rproj.toml") + .long("script") + .value_name("SCRIPT") + .num_args(1) .required(false), ) .arg( @@ -1633,6 +1650,14 @@ pub fn rig_app() -> Command { .required(true) .num_args(1..), ) + .arg( + Arg::new("script") + .help("Remove the packages from the inline metadata of this R script, instead of rproj.toml") + .long("script") + .value_name("SCRIPT") + .num_args(1) + .required(false), + ) .arg( Arg::new("no-lock") .help("Only update rproj.toml, do not update rproj.lock") @@ -3314,6 +3339,27 @@ mod tests { assert_eq!(sub.get_one::("platform").unwrap(), "macos-arm64"); } + #[test] + fn proj_init_add_remove_take_a_script() { + for cmd in [ + vec!["rig", "proj", "init", "--script", "a.R"], + vec!["rig", "proj", "add", "--script", "a.R", "cli"], + vec!["rig", "proj", "remove", "--script", "a.R", "cli"], + ] { + let matches = rig_app().try_get_matches_from(&cmd).unwrap(); + let (_name, sub) = matches.subcommand().unwrap(); + let (_name, sub) = sub.subcommand().unwrap(); + assert_eq!( + sub.get_one::("script").map(String::as_str), + Some("a.R") + ); + } + // script blocks have no dependency groups + assert!(rig_app() + .try_get_matches_from(["rig", "proj", "add", "--script", "a.R", "--dev", "cli"]) + .is_err()); + } + #[test] fn proj_sync_frozen_flag() { let matches = rig_app() diff --git a/src/help-generated.in b/src/help-generated.in index 7e874852..2984af94 100644 --- a/src/help-generated.in +++ b/src/help-generated.in @@ -74,7 +74,7 @@ const HELP_PPM_URL: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\ const ABOUT_PPM: &str = "Query Posit Package Manager (experimental)"; const HELP_PPM: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Posit Package Manager information.\n\n\u{1b}[1m\u{1b}[34mPPM server:\u{1b}[39m\u{1b}[22m\n By default rig reports on the public PPM instance at\n \u{1b}[32mhttps://packagemanager.posit.co\u{1b}[39m. Set the \u{1b}[32mPACKAGEMANAGER_ADDRESS\u{1b}[39m environment\n variable to the base URL of your own PPM instance to report on that\n instead. \u{1b}[32mrig ppm url\u{1b}[39m prints whichever one is in effect.\n\n For now \u{1b}[32mrig ppm builds\u{1b}[39m ignores this environment variable and reads a\n package build index derived from the public instance."; const ABOUT_PROJ_ADD: &str = "Add dependencies to \u{1b}[32mrproj.toml\u{1b}[39m"; -const HELP_PROJ_ADD: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Add one or more R packages to \u{1b}[32mrproj.toml\u{1b}[39m, rig's project and package\n manifest, then update \u{1b}[32mrproj.lock\u{1b}[39m and install the new packages into the\n project library. Adding a package that the manifest already lists updates\n its version requirement.\n\n A package is given as \u{1b}[32m\u{1b}[39m or \u{1b}[32m@\u{1b}[39m:\n\n rig proj add dplyr\n rig proj add dplyr@1.1.0\n rig proj add 'cli@>= 3.6' 'rlang@>= 1.0, < 2.0'\n\n Without a version, the package is added as \u{1b}[32m\"*\"\u{1b}[39m, i.e. any version. The\n concrete version rig picks is recorded in \u{1b}[32mrproj.lock\u{1b}[39m, so a project does not\n need a version requirement for every dependency; use one when the project\n really needs a particular version.\n\n Because \u{1b}[32m@\u{1b}[39m and the comparison operators are meaningful to most shells, quote\n a specification that contains a space or a \u{1b}[32m>\u{1b}[39m character, as in the examples\n above.\n\n\u{1b}[1m\u{1b}[34mGit, GitHub, GitLab and URL sources:\u{1b}[39m\u{1b}[22m\n A package can also be added straight from a git repository, pak's package\n reference syntax:\n\n rig proj add r-lib/crayon\n rig proj add r-lib/crayon@84be6207\n rig proj add r-lib/crayon@some-branch\n rig proj add r-lib/crayon#41\n rig proj add r-lib/crayon@*release\n rig proj add gitlab::group/project@main\n rig proj add 'git::https://gitlab.com/example/pkg.git@main'\n rig proj add https://cran.rstudio.com/src/contrib/processx_3.9.0.tar.gz\n\n A bare \u{1b}[32m/\u{1b}[39m (optionally \u{1b}[32mgithub::/\u{1b}[39m) is a GitHub\n reference; \u{1b}[32m//\u{1b}[39m points at a package in a subdirectory of\n the repository. After the path, \u{1b}[32m@\u{1b}[39m pins a branch, tag or commit,\n \u{1b}[32m#\u{1b}[39m a pull request, and \u{1b}[32m@*release\u{1b}[39m the latest release.\n\n \u{1b}[32mgitlab::/\u{1b}[39m is the same idea for GitLab, including nested\n subgroups (\u{1b}[32mgitlab:://\u{1b}[39m); \u{1b}[32m@\u{1b}[39m pins a branch,\n tag or commit, and \u{1b}[32m/-/\u{1b}[39m points at a subdirectory. Merge requests and\n \u{1b}[32m@*release\u{1b}[39m are not supported for GitLab. A self-hosted instance is\n \u{1b}[32mgitlab::\u{1b}[39m, e.g.\n \u{1b}[32mgitlab::https://gitlab.example.com/group/project\u{1b}[39m.\n\n A \u{1b}[32mgit::\u{1b}[39m reference works with any git host, not only GitHub or GitLab.\n\n \u{1b}[32murl::\u{1b}[39m (or a bare \u{1b}[32m\u{1b}[39m) points straight at a\n package source archive.\n\n The package name is read from the fetched repository's (or archive's) own\n \u{1b}[32mDESCRIPTION\u{1b}[39m (which may differ from the repository name), and the\n dependency is pinned by commit, or by the archive's sha256 for a \u{1b}[32murl::\u{1b}[39m\n source, not by version range: \u{1b}[32mrig proj add\u{1b}[39m resolves the reference right\n away, and \u{1b}[32mrproj.lock\u{1b}[39m records it.\n\n rig fetches a git/GitHub/GitLab source with the system \u{1b}[32mgit\u{1b}[39m, which must be\n installed and on \u{1b}[32mPATH\u{1b}[39m. A private repository authenticates exactly the way\n a plain \u{1b}[32mgit clone\u{1b}[39m would on your machine: a configured credential helper\n (Keychain, Windows Credential Manager, \u{1b}[32mgit credential-store\u{1b}[39m, etc.),\n \u{1b}[32m.netrc\u{1b}[39m, an SSH agent, or credentials already embedded in the URL. There is\n no separate rig-specific token setting. A \u{1b}[32murl::\u{1b}[39m source is a plain HTTP(S)\n download and needs no such authentication.\n\n\u{1b}[1m\u{1b}[34mLocal sources:\u{1b}[39m\u{1b}[22m\n A package that is already on this machine, in a source directory or a\n package file, can be added straight from there:\n\n rig proj add ../mypkg\n rig proj add ~/works/mypkg\n rig proj add mypkg_1.0.0.tar.gz\n rig proj add local::mypkg\n\n The path is recorded in \u{1b}[32mrproj.toml\u{1b}[39m relative to the project's own directory\n (not the directory \u{1b}[32mrig proj add\u{1b}[39m was run from), so the manifest stays\n correct after the project is moved or checked out elsewhere, as long as the\n local package stays in the same place relative to it.\n\n\u{1b}[1m\u{1b}[34mVersion requirements:\u{1b}[39m\u{1b}[22m\n - \u{1b}[32m^1.2.3\u{1b}[39m is \u{1b}[3mcompatible with\u{1b}[23m 1.2.3, i.e. \u{1b}[32m>= 1.2.3, < 2.0.0\u{1b}[39m.\n - \u{1b}[32m1.2.3\u{1b}[39m, a bare version, means the same as \u{1b}[32m^1.2.3\u{1b}[39m.\n - \u{1b}[32m~1.2.3\u{1b}[39m is \u{1b}[32m>= 1.2.3, < 1.3.0\u{1b}[39m.\n - \u{1b}[32m>= 1.2\u{1b}[39m, \u{1b}[32m> 1.2\u{1b}[39m, \u{1b}[32m<= 2.0\u{1b}[39m, \u{1b}[32m< 2.0\u{1b}[39m and \u{1b}[32m= 1.2.3\u{1b}[39m are a single bound.\n - \u{1b}[32m>= 1.0, < 2.0\u{1b}[39m: a comma means \u{1b}[3mand\u{1b}[23m, so both bounds hold.\n - \u{1b}[32m*\u{1b}[39m is any version.\n\n A bare version is written into the manifest in its explicit \u{1b}[32m^\u{1b}[39m spelling, so\n the file reads the same way whether or not you know that a bare version\n means \u{1b}[3mcompatible with\u{1b}[23m.\n\n The caret and tilde forms bump one component of the version and zero the\n ones after it: the leftmost non-zero component for \u{1b}[32m^\u{1b}[39m (\u{1b}[32m^0.2.3\u{1b}[39m is \u{1b}[32m>= 0.2.3, <\n 0.3.0\u{1b}[39m), the second component for \u{1b}[32m~\u{1b}[39m. R versions can have any number of\n components, so \u{1b}[32m^1.1.0.9000\u{1b}[39m is \u{1b}[32m>= 1.1.0.9000, < 2.0.0.0\u{1b}[39m.\n\n\u{1b}[1m\u{1b}[34mOptions:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--dev\u{1b}[39m adds the packages as development dependencies, into the\n \u{1b}[32m[dependency-groups.dev]\u{1b}[39m table instead of \u{1b}[32m[dependencies]\u{1b}[39m. These are\n installed by default, and left out by \u{1b}[32mrig proj sync --no-dev\u{1b}[39m.\n\n \u{1b}[32m--no-sync\u{1b}[39m updates \u{1b}[32mrproj.toml\u{1b}[39m and \u{1b}[32mrproj.lock\u{1b}[39m, but does not install anything.\n\n \u{1b}[32m--no-lock\u{1b}[39m only updates \u{1b}[32mrproj.toml\u{1b}[39m. Nothing is resolved or installed, so\n this also works offline.\n\n\u{1b}[1m\u{1b}[34mFiles:\u{1b}[39m\u{1b}[22m\n Only \u{1b}[32mrproj.toml\u{1b}[39m is edited. Changes are merged into the existing file, so\n comments and custom formatting are preserved where possible.\n\n If resolving the dependencies fails (most often because a package name is\n misspelled, and no repository has such a package) \u{1b}[32mrproj.toml\u{1b}[39m is restored to\n what it was, so a failed \u{1b}[32mrig proj add\u{1b}[39m does not leave the project with a\n dependency that cannot be installed."; +const HELP_PROJ_ADD: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Add one or more R packages to \u{1b}[32mrproj.toml\u{1b}[39m, rig's project and package\n manifest, then update \u{1b}[32mrproj.lock\u{1b}[39m and install the new packages into the\n project library. Adding a package that the manifest already lists updates\n its version requirement.\n\n A package is given as \u{1b}[32m\u{1b}[39m or \u{1b}[32m@\u{1b}[39m:\n\n rig proj add dplyr\n rig proj add dplyr@1.1.0\n rig proj add 'cli@>= 3.6' 'rlang@>= 1.0, < 2.0'\n\n Without a version, the package is added as \u{1b}[32m\"*\"\u{1b}[39m, i.e. any version. The\n concrete version rig picks is recorded in \u{1b}[32mrproj.lock\u{1b}[39m, so a project does not\n need a version requirement for every dependency; use one when the project\n really needs a particular version.\n\n Because \u{1b}[32m@\u{1b}[39m and the comparison operators are meaningful to most shells, quote\n a specification that contains a space or a \u{1b}[32m>\u{1b}[39m character, as in the examples\n above.\n\n\u{1b}[1m\u{1b}[34mGit, GitHub, GitLab and URL sources:\u{1b}[39m\u{1b}[22m\n A package can also be added straight from a git repository, pak's package\n reference syntax:\n\n rig proj add r-lib/crayon\n rig proj add r-lib/crayon@84be6207\n rig proj add r-lib/crayon@some-branch\n rig proj add r-lib/crayon#41\n rig proj add r-lib/crayon@*release\n rig proj add gitlab::group/project@main\n rig proj add 'git::https://gitlab.com/example/pkg.git@main'\n rig proj add https://cran.rstudio.com/src/contrib/processx_3.9.0.tar.gz\n\n A bare \u{1b}[32m/\u{1b}[39m (optionally \u{1b}[32mgithub::/\u{1b}[39m) is a GitHub\n reference; \u{1b}[32m//\u{1b}[39m points at a package in a subdirectory of\n the repository. After the path, \u{1b}[32m@\u{1b}[39m pins a branch, tag or commit,\n \u{1b}[32m#\u{1b}[39m a pull request, and \u{1b}[32m@*release\u{1b}[39m the latest release.\n\n \u{1b}[32mgitlab::/\u{1b}[39m is the same idea for GitLab, including nested\n subgroups (\u{1b}[32mgitlab:://\u{1b}[39m); \u{1b}[32m@\u{1b}[39m pins a branch,\n tag or commit, and \u{1b}[32m/-/\u{1b}[39m points at a subdirectory. Merge requests and\n \u{1b}[32m@*release\u{1b}[39m are not supported for GitLab. A self-hosted instance is\n \u{1b}[32mgitlab::\u{1b}[39m, e.g.\n \u{1b}[32mgitlab::https://gitlab.example.com/group/project\u{1b}[39m.\n\n A \u{1b}[32mgit::\u{1b}[39m reference works with any git host, not only GitHub or GitLab.\n\n \u{1b}[32murl::\u{1b}[39m (or a bare \u{1b}[32m\u{1b}[39m) points straight at a\n package source archive.\n\n The package name is read from the fetched repository's (or archive's) own\n \u{1b}[32mDESCRIPTION\u{1b}[39m (which may differ from the repository name), and the\n dependency is pinned by commit, or by the archive's sha256 for a \u{1b}[32murl::\u{1b}[39m\n source, not by version range: \u{1b}[32mrig proj add\u{1b}[39m resolves the reference right\n away, and \u{1b}[32mrproj.lock\u{1b}[39m records it.\n\n rig fetches a git/GitHub/GitLab source with the system \u{1b}[32mgit\u{1b}[39m, which must be\n installed and on \u{1b}[32mPATH\u{1b}[39m. A private repository authenticates exactly the way\n a plain \u{1b}[32mgit clone\u{1b}[39m would on your machine: a configured credential helper\n (Keychain, Windows Credential Manager, \u{1b}[32mgit credential-store\u{1b}[39m, etc.),\n \u{1b}[32m.netrc\u{1b}[39m, an SSH agent, or credentials already embedded in the URL. There is\n no separate rig-specific token setting. A \u{1b}[32murl::\u{1b}[39m source is a plain HTTP(S)\n download and needs no such authentication.\n\n\u{1b}[1m\u{1b}[34mLocal sources:\u{1b}[39m\u{1b}[22m\n A package that is already on this machine, in a source directory or a\n package file, can be added straight from there:\n\n rig proj add ../mypkg\n rig proj add ~/works/mypkg\n rig proj add mypkg_1.0.0.tar.gz\n rig proj add local::mypkg\n\n The path is recorded in \u{1b}[32mrproj.toml\u{1b}[39m relative to the project's own directory\n (not the directory \u{1b}[32mrig proj add\u{1b}[39m was run from), so the manifest stays\n correct after the project is moved or checked out elsewhere, as long as the\n local package stays in the same place relative to it.\n\n\u{1b}[1m\u{1b}[34mVersion requirements:\u{1b}[39m\u{1b}[22m\n - \u{1b}[32m^1.2.3\u{1b}[39m is \u{1b}[3mcompatible with\u{1b}[23m 1.2.3, i.e. \u{1b}[32m>= 1.2.3, < 2.0.0\u{1b}[39m.\n - \u{1b}[32m1.2.3\u{1b}[39m, a bare version, means the same as \u{1b}[32m^1.2.3\u{1b}[39m.\n - \u{1b}[32m~1.2.3\u{1b}[39m is \u{1b}[32m>= 1.2.3, < 1.3.0\u{1b}[39m.\n - \u{1b}[32m>= 1.2\u{1b}[39m, \u{1b}[32m> 1.2\u{1b}[39m, \u{1b}[32m<= 2.0\u{1b}[39m, \u{1b}[32m< 2.0\u{1b}[39m and \u{1b}[32m= 1.2.3\u{1b}[39m are a single bound.\n - \u{1b}[32m>= 1.0, < 2.0\u{1b}[39m: a comma means \u{1b}[3mand\u{1b}[23m, so both bounds hold.\n - \u{1b}[32m*\u{1b}[39m is any version.\n\n A bare version is written into the manifest in its explicit \u{1b}[32m^\u{1b}[39m spelling, so\n the file reads the same way whether or not you know that a bare version\n means \u{1b}[3mcompatible with\u{1b}[23m.\n\n The caret and tilde forms bump one component of the version and zero the\n ones after it: the leftmost non-zero component for \u{1b}[32m^\u{1b}[39m (\u{1b}[32m^0.2.3\u{1b}[39m is \u{1b}[32m>= 0.2.3, <\n 0.3.0\u{1b}[39m), the second component for \u{1b}[32m~\u{1b}[39m. R versions can have any number of\n components, so \u{1b}[32m^1.1.0.9000\u{1b}[39m is \u{1b}[32m>= 1.1.0.9000, < 2.0.0.0\u{1b}[39m.\n\n\u{1b}[1m\u{1b}[34mScripts:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--script script.R\u{1b}[39m adds the packages to the \u{1b}[32m# /// script\u{1b}[39m block of an R\n script, instead of \u{1b}[32mrproj.toml\u{1b}[39m, see scripts with inline dependencies. It\n creates the block if the script has none. The packages are given the same way as\n for a project, and a local path is recorded relative to the script's directory.\n A script has no dependency groups, so \u{1b}[32m--script\u{1b}[39m does not work with \u{1b}[32m--dev\u{1b}[39m.\n\n Then rig sets up the script's environment in its cache directory, the same way\n \u{1b}[32mrig run script.R\u{1b}[39m would, so the next \u{1b}[32mrig run\u{1b}[39m can start right away.\n \u{1b}[32m--no-sync\u{1b}[39m and \u{1b}[32m--no-lock\u{1b}[39m work the same way as for a project. If resolving the\n dependencies fails, the script is restored to what it was.\n\n\u{1b}[1m\u{1b}[34mOptions:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--dev\u{1b}[39m adds the packages as development dependencies, into the\n \u{1b}[32m[dependency-groups.dev]\u{1b}[39m table instead of \u{1b}[32m[dependencies]\u{1b}[39m. These are\n installed by default, and left out by \u{1b}[32mrig proj sync --no-dev\u{1b}[39m.\n\n \u{1b}[32m--no-sync\u{1b}[39m updates \u{1b}[32mrproj.toml\u{1b}[39m and \u{1b}[32mrproj.lock\u{1b}[39m, but does not install anything.\n\n \u{1b}[32m--no-lock\u{1b}[39m only updates \u{1b}[32mrproj.toml\u{1b}[39m. Nothing is resolved or installed, so\n this also works offline.\n\n\u{1b}[1m\u{1b}[34mFiles:\u{1b}[39m\u{1b}[22m\n Only \u{1b}[32mrproj.toml\u{1b}[39m is edited. Changes are merged into the existing file, so\n comments and custom formatting are preserved where possible.\n\n If resolving the dependencies fails (most often because a package name is\n misspelled, and no repository has such a package) \u{1b}[32mrproj.toml\u{1b}[39m is restored to\n what it was, so a failed \u{1b}[32mrig proj add\u{1b}[39m does not leave the project with a\n dependency that cannot be installed."; const ABOUT_PROJ_DEPS: &str = "Show project dependencies"; const HELP_PROJ_DEPS: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Show the dependencies of an R project, in a table.\n\n rig reads the project manifest, \u{1b}[32mrproj.toml\u{1b}[39m, in the current directory.\n\n Add \u{1b}[32m--dev\u{1b}[39m to include every dependency group and optional-dependency extra\n together -- unlike \u{1b}[32mrig proj sync\u{1b}[39m, which selects each one\n separately with \u{1b}[32m--group\u{1b}[39m/\u{1b}[32m--extra\u{1b}[39m, this is an all-or-nothing view of the\n manifest.\n\n Use \u{1b}[32m--json\u{1b}[39m for machine readable output.\n\n The plain listing only reads the manifest, so it needs neither R nor the\n package repositories.\n\n\u{1b}[1m\u{1b}[34mRecursive dependencies:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--recursive\u{1b}[39m (\u{1b}[32m-r\u{1b}[39m) shows the whole dependency closure: not only the packages\n the project needs directly, but also the packages \u{1b}[3mthose\u{1b}[23m need, and so on.\n Each package appears once, with the version currently on CRAN, the \u{1b}[32mDepth\u{1b}[39m\n column giving its distance from the project, and the \u{1b}[32mNeeded by\u{1b}[39m column\n naming the packages that pull it in. This needs the package metadata of the\n repositories, which rig downloads if it does not have it yet.\n\n See \u{1b}[32mrig proj tree\u{1b}[39m to show the dependency tree of the project.\n\n rig follows the dependencies of the \u{1b}[3mlatest\u{1b}[23m version of every package in\n the closure, so a version requirement that would force an older version,\n with different dependencies, is not taken into account."; const ABOUT_PROJ_EXPORT: &str = "Create a DESCRIPTION file from rproj.toml"; @@ -82,11 +82,11 @@ const HELP_PROJ_EXPORT: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[ const ABOUT_PROJ_IMPORT: &str = "Create rproj.toml from a DESCRIPTION file"; const HELP_PROJ_IMPORT: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Read a \u{1b}[32mDESCRIPTION\u{1b}[39m file and create \u{1b}[32mrproj.toml\u{1b}[39m, rig's project and package\n manifest, from it. Fails if \u{1b}[32mrproj.toml\u{1b}[39m already exists, use \u{1b}[32m--dependencies\u{1b}[39m\n to merge dependencies into an existing file instead.\n\n | \u{1b}[32mDESCRIPTION\u{1b}[39m field. | \u{1b}[32mrproj.toml\u{1b}[39m field. |\n |---------------------|------------------------------|\n | \u{1b}[32mPackage:\u{1b}[39m | \u{1b}[32m[project].name\u{1b}[39m |\n | \u{1b}[32mVersion:\u{1b}[39m | \u{1b}[32m[project].version\u{1b}[39m |\n | \u{1b}[32mTitle:\u{1b}[39m | \u{1b}[32m[project].title\u{1b}[39m |\n | \u{1b}[32mDescription:\u{1b}[39m | \u{1b}[32m[project].description\u{1b}[39m |\n | \u{1b}[32mLicense:\u{1b}[39m | \u{1b}[32m[project].license\u{1b}[39m |\n | \u{1b}[32mType:\u{1b}[39m | \u{1b}[32m[project].type\u{1b}[39m |\n | \u{1b}[32mURL:\u{1b}[39m | \u{1b}[32m[project.urls].homepage\u{1b}[39m |\n | \u{1b}[32mURL:\u{1b}[39m | \u{1b}[32m[project.urls].source\u{1b}[39m |\n | \u{1b}[32mBugReports:\u{1b}[39m | \u{1b}[32m[project.urls].bugreports\u{1b}[39m |\n | \u{1b}[32mAuthors@R\u{1b}[39m | \u{1b}[32m[project].authors\u{1b}[39m |\n | \u{1b}[32mMaintainer:\u{1b}[39m | \u{1b}[32m[project].authors\u{1b}[39m |\n | \u{1b}[32mDepends\u{1b}[39m | \u{1b}[32m[dependencies]\u{1b}[39m |\n | \u{1b}[32mImports\u{1b}[39m | \u{1b}[32m[dependencies]\u{1b}[39m |\n | \u{1b}[32mLinkingTo\u{1b}[39m | \u{1b}[32m[linking-dependencies]\u{1b}[39m |\n | \u{1b}[32mSuggests\u{1b}[39m | \u{1b}[32m[dependency-groups.dev]\u{1b}[39m |\n | \u{1b}[32mEnhances\u{1b}[39m | \u{1b}[32m[dependency-groups.enhances]\u{1b}[39m |\n | \u{1b}[32mConfig/Needs/\u{1b}[39m | \u{1b}[32m[dependency-groups.]\u{1b}[39m |\n | \u{1b}[32mConfig/Needs/Optional/\u{1b}[39m | \u{1b}[32m[optional-dependencies.]\u{1b}[39m |\n\n \u{1b}[32mAuthors@R\u{1b}[39m is parsed into \u{1b}[32m[project].authors\u{1b}[39m, one entry per \u{1b}[32mperson()\u{1b}[39m call, in\n order; this is a best-effort parser for common \u{1b}[32mperson()\u{1b}[39m usage (name, \u{1b}[32memail\u{1b}[39m,\n \u{1b}[32mrole\u{1b}[39m, and an \u{1b}[32mORCID\u{1b}[39m/\u{1b}[32mROR\u{1b}[39m \u{1b}[32mcomment\u{1b}[39m), not a full R parser, so unusual calls are\n skipped with a warning. If \u{1b}[32mAuthors@R\u{1b}[39m is absent, the simpler \u{1b}[32mMaintainer:\n Name \u{1b}[39m field is used instead, as a single author with role \u{1b}[32mcre\u{1b}[39m.\n\n \u{1b}[32mConfig/Needs/\u{1b}[39m depepdencies are parsed specially, an entry that is not\n a plain package name (with an optional version requirement) is kept\n verbatim as \u{1b}[32mref = \"...\"\u{1b}[39m. E.g. \u{1b}[32mtidyverse/tidytemplate\u{1b}[39m becomes \u{1b}[32mtidytemplate =\n { ref = \"tidyverse/tidytemplate\" }\u{1b}[39m. \u{1b}[32mrig proj export\u{1b}[39m writes these back\n unchanged. Note that only the \u{1b}[32mdev\u{1b}[39m and \u{1b}[32menhances\u{1b}[39m groups are installed by\n rig, so a \u{1b}[32mConfig/Needs/*\u{1b}[39m group is carried in the manifest, but not solved\n or installed by \u{1b}[32mrig proj lock\u{1b}[39m and \u{1b}[32mrig proj sync\u{1b}[39m. This behavior will be\n improved in the future.\n\n \u{1b}[32mConfig/Needs/Optional/\u{1b}[39m fields are parsed the same way, into\n \u{1b}[32m[optional-dependencies.]\u{1b}[39m instead of \u{1b}[32m[dependency-groups.]\u{1b}[39m. A\n package listed there is also \u{1b}[32mSuggests\u{1b}[39m-listed in the same \u{1b}[32mDESCRIPTION\u{1b}[39m\n file (\u{1b}[32mrig proj export\u{1b}[39m writes it to both places, since \u{1b}[32mR CMD check\u{1b}[39m only\n allows using a package conditionally if it's \u{1b}[32mSuggests\u{1b}[39m-listed), so it also\n ends up in \u{1b}[32m[dependency-groups.dev]\u{1b}[39m, same as any other \u{1b}[32mSuggests\u{1b}[39m entry.\n\n\u{1b}[1m\u{1b}[34mFiles:\u{1b}[39m\u{1b}[22m\n A full import sets up a whole project, not just its manifest, so it creates\n the same files as \u{1b}[32mrig proj init\u{1b}[39m .\n\n \u{1b}[32m--r-version\u{1b}[39m sets the R version the project is set up for. It does not have\n to be installed, and it does not change what is written: the manifest's R\n requirement always comes from the \u{1b}[32mDESCRIPTION\u{1b}[39m file, and the \u{1b}[32m.rvenvlib/rvenv\u{1b}[39m\n shim package works with every R. The default is the current default R\n version, or the current R release if there is no default."; const ABOUT_PROJ_INIT: &str = "Create a new R project"; -const HELP_PROJ_INIT: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Set up an R project in the current directory: the \u{1b}[32mrproj.toml\u{1b}[39m manifest, plus\n the part of the project's virtual environment that belongs in version\n control.\n\n \u{1b}[32mrproj.toml\u{1b}[39m is rig's project manifest. It describes the project's metadata\n and its R and package dependencies.\n\n \u{1b}[32mrig proj init\u{1b}[39m writes a minimal skeleton: a \u{1b}[32m[project]\u{1b}[39m table with the name\n (taken from the current directory) and version, and a \u{1b}[32m[dependencies]\u{1b}[39m table\n with a single R requirement, that you then fill in.\n\n Use \u{1b}[32mrig proj import\u{1b}[39m instead to set up the same project from an existing\n \u{1b}[32mDESCRIPTION\u{1b}[39m file.\n\n\u{1b}[1m\u{1b}[34mFiles:\u{1b}[39m\u{1b}[22m\n \u{1b}[32mrig proj init\u{1b}[39m creates these, and nothing else. All of them are meant to be\n committed, so that a fresh clone of the project works right away:\n\n - \u{1b}[32mrproj.toml\u{1b}[39m - the manifest.\n - \u{1b}[32m.Renviron\u{1b}[39m, \u{1b}[32m.rvenvlib/rvenv\u{1b}[39m - boilerplate to set up R's libraries for the\n project when started from the project directory, in a terminal, of from\n an editor (RStudio, Positron, VS Code).\n - \u{1b}[32m.gitignore\u{1b}[39m - to ignore \u{1b}[32m.rvenv\u{1b}[39m which contains the project library and\n configuration files, created by \u{1b}[32mrig proj sync\u{1b}[39m.\n\n The R virtual environment in \u{1b}[32m.rvenv\u{1b}[39m, including the project library\n \u{1b}[32m.rvenv/lib\u{1b}[39m, is machine-specific and is created by \u{1b}[32mrig proj sync\u{1b}[39m . It can be\n deleted and rebuilt at any time.\n\n Note that \u{1b}[32mR --vanilla\u{1b}[39m ignores \u{1b}[32m.Renviron\u{1b}[39m, and so does not use the project\n library.\n\n\u{1b}[1m\u{1b}[34mOptions:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--r-version\u{1b}[39m sets the R version the project is for. It does not have to be\n installed. Defaults to the current default R version, or the current R\n release if there is no default.\n\n rig refuses to overwrite any of the files above; pass \u{1b}[32m--force\u{1b}[39m to replace\n them. The \u{1b}[32m.gitignore\u{1b}[39m block is the exception: rig never refuses on an\n existing \u{1b}[32m.gitignore\u{1b}[39m, it just merges its block into it (or adds one),\n leaving the rest of the file alone, and \u{1b}[32m--force\u{1b}[39m does not change that."; +const HELP_PROJ_INIT: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Set up an R project in the current directory: the \u{1b}[32mrproj.toml\u{1b}[39m manifest, plus\n the part of the project's virtual environment that belongs in version\n control.\n\n \u{1b}[32mrproj.toml\u{1b}[39m is rig's project manifest. It describes the project's metadata\n and its R and package dependencies.\n\n \u{1b}[32mrig proj init\u{1b}[39m writes a minimal skeleton: a \u{1b}[32m[project]\u{1b}[39m table with the name\n (taken from the current directory) and version, and a \u{1b}[32m[dependencies]\u{1b}[39m table\n with a single R requirement, that you then fill in.\n\n Use \u{1b}[32mrig proj import\u{1b}[39m instead to set up the same project from an existing\n \u{1b}[32mDESCRIPTION\u{1b}[39m file.\n\n\u{1b}[1m\u{1b}[34mFiles:\u{1b}[39m\u{1b}[22m\n \u{1b}[32mrig proj init\u{1b}[39m creates these, and nothing else. All of them are meant to be\n committed, so that a fresh clone of the project works right away:\n\n - \u{1b}[32mrproj.toml\u{1b}[39m - the manifest.\n - \u{1b}[32m.Renviron\u{1b}[39m, \u{1b}[32m.rvenvlib/rvenv\u{1b}[39m - boilerplate to set up R's libraries for the\n project when started from the project directory, in a terminal, of from\n an editor (RStudio, Positron, VS Code).\n - \u{1b}[32m.gitignore\u{1b}[39m - to ignore \u{1b}[32m.rvenv\u{1b}[39m which contains the project library and\n configuration files, created by \u{1b}[32mrig proj sync\u{1b}[39m.\n\n The R virtual environment in \u{1b}[32m.rvenv\u{1b}[39m, including the project library\n \u{1b}[32m.rvenv/lib\u{1b}[39m, is machine-specific and is created by \u{1b}[32mrig proj sync\u{1b}[39m . It can be\n deleted and rebuilt at any time.\n\n Note that \u{1b}[32mR --vanilla\u{1b}[39m ignores \u{1b}[32m.Renviron\u{1b}[39m, and so does not use the project\n library.\n\n\u{1b}[1m\u{1b}[34mScripts:\u{1b}[39m\u{1b}[22m\n \u{1b}[32mrig proj init --script script.R\u{1b}[39m sets up a single script instead of a project:\n it adds a \u{1b}[32m# /// script\u{1b}[39m block with an R requirement to the top of the script,\n or creates the script, if it does not exist yet:\n\n # /// script\n # [dependencies]\n # R = \">= 4.6\"\n # ///\n\n The block goes after a \u{1b}[32m#!\u{1b}[39m line, if the script has one. It does not create any\n other file. Add packages to the block with \u{1b}[32mrig proj add --script\u{1b}[39m, and run the\n script with \u{1b}[32mrig run\u{1b}[39m, which sets up an environment for it, see scripts with\n inline dependencies. rig refuses to replace a block that is already there, pass\n \u{1b}[32m--force\u{1b}[39m to replace it.\n\n\u{1b}[1m\u{1b}[34mOptions:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--r-version\u{1b}[39m sets the R version the project is for. It does not have to be\n installed. Defaults to the current default R version, or the current R\n release if there is no default.\n\n rig refuses to overwrite any of the files above; pass \u{1b}[32m--force\u{1b}[39m to replace\n them. The \u{1b}[32m.gitignore\u{1b}[39m block is the exception: rig never refuses on an\n existing \u{1b}[32m.gitignore\u{1b}[39m, it just merges its block into it (or adds one),\n leaving the rest of the file alone, and \u{1b}[32m--force\u{1b}[39m does not change that."; const ABOUT_PROJ_LOCK: &str = "Resolve project dependencies and write \u{1b}[32mrproj.lock\u{1b}[39m"; const HELP_PROJ_LOCK: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Resolve the dependencies of an R project to a concrete set of package\n versions, and write the result to \u{1b}[32mrproj.lock\u{1b}[39m.\n\n rig reads the project manifest, \u{1b}[32mrproj.toml\u{1b}[39m, in the current directory, and\n uses its built-in solver to find a compatible set of package versions from\n the configured repositories.\n\n \u{1b}[32mrig proj solve\u{1b}[39m does not run R.\n\n Use \u{1b}[32m--r-version\u{1b}[39m to solve for a specific R version.\n\n\u{1b}[1m\u{1b}[34mOptional dependencies and dependency groups:\u{1b}[39m\u{1b}[22m\n Every \u{1b}[32m[dependency-groups.*]\u{1b}[39m table (\u{1b}[32mdev\u{1b}[39m, \u{1b}[32menhances\u{1b}[39m, or any other name)\n and every \u{1b}[32m[optional-dependencies.*]\u{1b}[39m extra are optional dependencies:\n packages the project suggests or can take advantage of, but does not need to\n run. \u{1b}[32mrig proj lock\u{1b}[39m always solves all of them together with the project's\n hard dependencies, in one solve, so \u{1b}[32mrproj.lock\u{1b}[39m is complete -- a version\n picked for a shared package is the same whether it got pulled in as a hard\n or an optional dependency, and every group and extra is available to install\n without a new solve. A \u{1b}[32m[dependency-groups.*]\u{1b}[39m table can also \u{1b}[32minclude-groups\n = [...]\u{1b}[39m other groups, pulling in their packages too; \u{1b}[32mrig proj lock\u{1b}[39m follows\n this when solving, and rejects a cycle (a group that includes itself,\n directly or through others).\n\n \u{1b}[32mrig proj sync\u{1b}[39m is where a subset of this is picked for\n installation -- by default \u{1b}[32mmain\u{1b}[39m plus the \u{1b}[32mdev\u{1b}[39m group, more with\n \u{1b}[32m--group\u{1b}[39m/\u{1b}[32m--all-groups\u{1b}[39m/\u{1b}[32m--extra\u{1b}[39m/\u{1b}[32m--all-extras\u{1b}[39m.\n\n\u{1b}[1m\u{1b}[34mThe R version:\u{1b}[39m\u{1b}[22m\n Without \u{1b}[32m--r-version\u{1b}[39m rig solves for the default R version, provided the\n manifest's own \u{1b}[32mR\u{1b}[39m requirement allows it. If it does not, rig takes the\n newest installed R version that does, and failing that the current R\n release. The version it picks does not have to be installed: \u{1b}[32mrig proj lock\u{1b}[39m\n never runs R, and \u{1b}[32mrig proj sync\u{1b}[39m installs the R version the lock file names.\n\n\u{1b}[1m\u{1b}[34mSource and binary packages:\u{1b}[39m\u{1b}[22m\n The solver considers binary packages as well as source packages, and\n prefers a binary build when one is available for the same version. By\n default a binary build never changes \u{1b}[3mwhich version\u{1b}[23m rig picks: the newest\n suitable version wins, and a binary of it is used if there is one. Use\n \u{1b}[32m--prefer-binary\u{1b}[39m to let an older version win instead, when the newest one\n has no binary but an older one does, typically because a version was\n released so recently that it has not been built yet. Only the three newest\n versions of a package are considered; \u{1b}[32m--prefer-binary=5\u{1b}[39m considers five.\n Versions held back this way are marked in the output.\n\n By default rig solves for this machine plus the three other common\n platforms (macOS arm64, Windows x86_64 and GNU Linux x86_64), and for\n source packages only, so the lock file also works on platforms without\n binary packages. \u{1b}[32mrig proj sync\u{1b}[39m only uses the source target if no other\n target matches the machine. Use\n \u{1b}[32m--platform\u{1b}[39m to solve for a different set instead, e.g. a single specific\n distro:\n\n rig proj lock --platform ubuntu-24.04\n\n Use \u{1b}[32m--add-platform\u{1b}[39m instead to add a platform to that default set rather\n than replacing it, e.g. to also solve for one extra distro on top of the\n usual five. \u{1b}[32m--add-platform\u{1b}[39m can be repeated:\n\n rig proj lock --add-platform ubuntu-24.04 --add-platform linux-fedora-42\n\n \u{1b}[32m--platform\u{1b}[39m/\u{1b}[32m--add-platform\u{1b}[39m accept:\n\n - \u{1b}[32mmacos-arm64\u{1b}[39m, \u{1b}[32mwindows-x86_64\u{1b}[39m -- an OS plus arch, for the two\n non-Linux platforms.\n - \u{1b}[32mubuntu-24.04\u{1b}[39m, \u{1b}[32mfedora-42\u{1b}[39m, \u{1b}[32mopensuse-15.6\u{1b}[39m -- a Linux distro and version,\n matched against P3M's build list.\n - \u{1b}[32mmanylinux_2_28-arm64\u{1b}[39m, \u{1b}[32mjammy-x86_64\u{1b}[39m -- a P3M platform name and arch\n directly, e.g. copied from another \u{1b}[32mrproj.lock\u{1b}[39m's \u{1b}[32mplatform\u{1b}[39m field.\n - \u{1b}[32msource\u{1b}[39m -- source packages only, for any platform.\n\n A Linux distro/version or platform name P3M has no specific build for falls\n back to its generic manylinux build for the given arch, rather than failing.\n\n rig also falls back to source packages when there are no binaries for a\n platform at all.\n\n rig keeps the repository metadata and the binary package indices it solves\n from in its cache, and refreshes them once a day. Use \u{1b}[32m--no-cache\u{1b}[39m to ignore\n the cache, or clean the cache with \u{1b}[32mrig cached clean\u{1b}[39m.\n\n\u{1b}[1m\u{1b}[34mSticky lock files:\u{1b}[39m\u{1b}[22m\n A \u{1b}[32mrig proj lock\u{1b}[39m run that finds an existing \u{1b}[32mrproj.lock\u{1b}[39m already satisfying\n \u{1b}[32mrproj.toml\u{1b}[39m reuses it as-is, for every package, instead of re-resolving\n anything. This applies to ordinary dependencies, an existing pin that still\n satisfies the manifest's version requirement is kept, even if a newer\n version has since been published, as well as to git/GitHub dependencies (see\n below).\n\n If \u{1b}[32mrproj.toml\u{1b}[39m changed, e.g. after \u{1b}[32mrig proj add\u{1b}[39m, rig solves the\n dependencies again, but it still keeps the versions \u{1b}[32mrproj.lock\u{1b}[39m pins, as\n long as they fit. So adding a package only changes the versions it needs\n changed, and does not upgrade the rest of the project. A platform or R\n version that is new to the lock file gets the same versions as the other\n platforms of the same R version, where possible.\n\n Use \u{1b}[32mrig proj lock --upgrade\u{1b}[39m to ignore the existing lock file and\n re-resolve every dependency instead, picking the latest version that still\n satisfies \u{1b}[32mrproj.toml\u{1b}[39m.\n\n Use \u{1b}[32m--upgrade-package\u{1b}[39m to upgrade only some packages, and keep the rest:\n\n rig proj lock --upgrade-package cli\n rig proj lock -P cli,glue\n\n The packages they depend on are only upgraded if they need to be. You can\n also give a version requirement, with the same \u{1b}[32m@\u{1b}[39m syntax\n as \u{1b}[32mrig proj add\u{1b}[39m, e.g. to upgrade or downgrade to a specific version:\n\n rig proj lock -P 'cli@>= 3.6.4'\n rig proj lock -P 'cli@=3.6.2'\n\n The requirement only applies to this run, it is not written to\n \u{1b}[32mrproj.toml\u{1b}[39m. Later \u{1b}[32mrig proj lock\u{1b}[39m runs keep the version it picked, as\n long as it fits \u{1b}[32mrproj.toml\u{1b}[39m. A git/GitHub dependency named in\n \u{1b}[32m--upgrade-package\u{1b}[39m is checked against its remote again, like with\n \u{1b}[32m--upgrade\u{1b}[39m.\n\n\u{1b}[1m\u{1b}[34mExcluding newer package versions:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--exclude-newer\u{1b}[39m makes the solver ignore CRAN package versions published\n after a date, so you can lock the project as CRAN was on that day, or skip\n versions released in the last few days:\n\n rig proj lock --exclude-newer 2025-06-01\n rig proj lock --exclude-newer \"7 days\"\n\n It takes a date (\u{1b}[32m2025-06-01\u{1b}[39m), an RFC 3339 timestamp (only its UTC date is\n used), or a span back from today, e.g. \u{1b}[32m7 days\u{1b}[39m, \u{1b}[32m2 weeks\u{1b}[39m or \u{1b}[32mP1M\u{1b}[39m. A\n version's publication date is the day it first appeared in a Posit Package\n Manager snapshot, so the cutoff is a day, and it cannot be earlier than\n 2017-10-10, the first snapshot. It only applies to CRAN packages, not to\n git, GitHub, URL or local dependencies, and binary builds of a version are\n used no matter when they were built.\n\n To make it part of the project, set it in \u{1b}[32mrproj.toml\u{1b}[39m; \u{1b}[32m--exclude-newer\u{1b}[39m\n overrides it:\n\n [tool.rig]\n exclude-newer = \"2025-06-01\"\n\n \u{1b}[32mrproj.lock\u{1b}[39m records the cutoff in its own \u{1b}[32m[tool.rig]\u{1b}[39m table, and a lock\n solved with a different one is solved again. This keeps the pinned versions\n that are not newer than the new cutoff, see \"Sticky lock files\" above. For a span, the lock records the\n span as well, and the lock is reused as long as the span stays the same, so\n the cutoff does not move every day. Use \u{1b}[32m--upgrade\u{1b}[39m to solve again with a\n fresh cutoff.\n\n\u{1b}[1m\u{1b}[34mGit, GitHub and URL dependencies:\u{1b}[39m\u{1b}[22m\n A \u{1b}[32mgit::\u{1b}[39m/\u{1b}[32mgithub::\u{1b}[39m dependency pinned to a branch, a pull request, or no ref\n at all (the default branch's tip) is only resolved against its remote the\n first time it's locked. Once \u{1b}[32mrproj.lock\u{1b}[39m records a commit for it, later\n \u{1b}[32mrig proj lock\u{1b}[39m runs reuse that commit as-is rather than re-checking whether\n the branch moved on every lock. \u{1b}[32mrig proj lock --upgrade\u{1b}[39m re-checks every\n git/GitHub dependency's ref and moves the pin forward if it changed. A\n \u{1b}[32mrev\u{1b}[39m/\u{1b}[32mtag\u{1b}[39m pins an exact commit already, so there's nothing for \u{1b}[32m--upgrade\u{1b}[39m\n to move. \u{1b}[32mrelease = true\u{1b}[39m is sticky the same way: once locked, later runs\n keep the release it pinned instead of asking GitHub which release is latest\n every time. \u{1b}[32mrig proj lock --upgrade\u{1b}[39m re-checks and moves the pin forward if\n a newer release exists.\n\n A \u{1b}[32murl::\u{1b}[39m dependency names one exact archive rather than a movable ref, so\n there's nothing for \u{1b}[32m--upgrade\u{1b}[39m to move either: every \u{1b}[32mrig proj lock\u{1b}[39m run\n downloads it (the download itself is cached) and records its sha256 in\n \u{1b}[32mrproj.lock\u{1b}[39m, which then changes only if the archive's contents change. Set\n \u{1b}[32mhash\u{1b}[39m on a \u{1b}[32murl\u{1b}[39m dependency to pin the expected sha256 and catch that.\n\n The \u{1b}[32mrproj.lock\u{1b}[39m file records, for every package, whether it is a source or a\n binary package and the URL it is downloaded from. It also records where the\n file is cached, which is per \u{1b}[3mbuild\u{1b}[23m rather than per version: a repository\n can offer several binaries of one version for one platform and R version,\n and they are cached side by side.\n\n\u{1b}[1m\u{1b}[34mWorkspaces:\u{1b}[39m\u{1b}[22m\n A manifest with a \u{1b}[32m[workspace]\u{1b}[39m table is the root of a workspace: a monorepo\n of several projects or packages, listed as path patterns in \u{1b}[32mmembers\u{1b}[39m, that\n share one \u{1b}[32mrproj.lock\u{1b}[39m and one package library. \u{1b}[32mexclude\u{1b}[39m drops directories a\n \u{1b}[32mmembers\u{1b}[39m pattern would otherwise match, and the root manifest is always a\n member of its own workspace.\n\n \u{1b}[32mrig proj lock\u{1b}[39m in a workspace (from the root or from any member directory)\n reads every member and resolves them all in one solve, so that every member\n ends up with the same version of every shared dependency, and writes one\n \u{1b}[32mrproj.lock\u{1b}[39m at the workspace root. A member that depends on a sibling member\n is resolved against that sibling's own dependencies. The members themselves\n are directories rather than packages to download, so they are not recorded\n in the lock file.\n\n The R version rig solves for has to satisfy every member's \u{1b}[32mR\u{1b}[39m requirement,\n not just the root's.\n\n \u{1b}[32m[workspace.dependencies]\u{1b}[39m declares shared version requirements. A member\n inherits one by name, instead of spelling out its own requirement:\n\n # rproj.toml, the workspace root\n [workspace]\n members = [\"packages/*\"]\n\n [workspace.dependencies]\n cli = \">= 3.6.0\"\n\n # packages/mypkg/rproj.toml, a member\n [dependencies]\n cli = { workspace = true }\n\n An entry no member inherits has no effect on the solve; it is a\n declaration, not a request. Whether a member attaches a package (\u{1b}[32mattach\u{1b}[39m) is\n still the member's own business, and is kept when the rest of the entry is\n inherited."; const ABOUT_PROJ_REMOVE: &str = "Remove dependencies from \u{1b}[32mrproj.toml\u{1b}[39m"; -const HELP_PROJ_REMOVE: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Remove one or more R packages from \u{1b}[32mrproj.toml\u{1b}[39m, rig's project and package\n manifest, then update \u{1b}[32mrproj.lock\u{1b}[39m and the project library to match. A\n package is removed wherever it is listed: \u{1b}[32m[dependencies]\u{1b}[39m,\n \u{1b}[32m[linking-dependencies]\u{1b}[39m, or any \u{1b}[32m[dependency-groups.*]\u{1b}[39m table.\n\n Naming a package that is not a dependency in \u{1b}[32mrproj.toml\u{1b}[39m is an error, and\n none of the named packages are removed if any of them is not found, so a\n typo cannot silently remove the wrong set of packages.\n\n\u{1b}[1m\u{1b}[34mOptions:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--no-sync\u{1b}[39m updates \u{1b}[32mrproj.toml\u{1b}[39m and \u{1b}[32mrproj.lock\u{1b}[39m, but does not touch the project\n library.\n\n \u{1b}[32m--no-lock\u{1b}[39m only updates \u{1b}[32mrproj.toml\u{1b}[39m. Nothing is resolved or installed, so\n this also works offline.\n\n\u{1b}[1m\u{1b}[34mFiles:\u{1b}[39m\u{1b}[22m\n Only \u{1b}[32mrproj.toml\u{1b}[39m is edited. Changes are merged into the existing file, so\n comments and custom formatting are preserved where possible.\n\n If resolving the remaining dependencies fails, \u{1b}[32mrproj.toml\u{1b}[39m is restored to\n what it was, so a failed \u{1b}[32mrig proj remove\u{1b}[39m does not leave the project with a\n manifest that cannot be locked."; +const HELP_PROJ_REMOVE: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Remove one or more R packages from \u{1b}[32mrproj.toml\u{1b}[39m, rig's project and package\n manifest, then update \u{1b}[32mrproj.lock\u{1b}[39m and the project library to match. A\n package is removed wherever it is listed: \u{1b}[32m[dependencies]\u{1b}[39m,\n \u{1b}[32m[linking-dependencies]\u{1b}[39m, or any \u{1b}[32m[dependency-groups.*]\u{1b}[39m table.\n\n Naming a package that is not a dependency in \u{1b}[32mrproj.toml\u{1b}[39m is an error, and\n none of the named packages are removed if any of them is not found, so a\n typo cannot silently remove the wrong set of packages.\n\n\u{1b}[1m\u{1b}[34mScripts:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--script script.R\u{1b}[39m removes the packages from the \u{1b}[32m# /// script\u{1b}[39m block of an R\n script, instead of \u{1b}[32mrproj.toml\u{1b}[39m, see scripts with inline dependencies. Then rig\n sets up the script's new environment, the same way \u{1b}[32mrig proj add --script\u{1b}[39m does.\n\n\u{1b}[1m\u{1b}[34mOptions:\u{1b}[39m\u{1b}[22m\n \u{1b}[32m--no-sync\u{1b}[39m updates \u{1b}[32mrproj.toml\u{1b}[39m and \u{1b}[32mrproj.lock\u{1b}[39m, but does not touch the project\n library.\n\n \u{1b}[32m--no-lock\u{1b}[39m only updates \u{1b}[32mrproj.toml\u{1b}[39m. Nothing is resolved or installed, so\n this also works offline.\n\n\u{1b}[1m\u{1b}[34mFiles:\u{1b}[39m\u{1b}[22m\n Only \u{1b}[32mrproj.toml\u{1b}[39m is edited. Changes are merged into the existing file, so\n comments and custom formatting are preserved where possible.\n\n If resolving the remaining dependencies fails, \u{1b}[32mrproj.toml\u{1b}[39m is restored to\n what it was, so a failed \u{1b}[32mrig proj remove\u{1b}[39m does not leave the project with a\n manifest that cannot be locked."; const ABOUT_PROJ_RENV_EXPORT: &str = "Write an renv.lock file from rproj.toml"; const HELP_PROJ_RENV_EXPORT: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Resolve the dependencies of the project in the current directory (its\n \u{1b}[32mrproj.toml\u{1b}[39m manifest) with rig's built-in solver, for one \u{1b}[32m(R version,\n platform)\u{1b}[39m target, and write the result as \u{1b}[32mrenv.lock\u{1b}[39m.\n\n Unlike \u{1b}[32mrig proj lock\u{1b}[39m , this only ever solves a single target, since\n \u{1b}[32mrenv.lock\u{1b}[39m has no multi-target concept.\n\n Without \u{1b}[32m--r-version\u{1b}[39m, rig solves for the default R version, provided the\n manifest's own \u{1b}[32mR\u{1b}[39m requirement allows it; otherwise the newest installed R\n version that does, and failing that the current R release. Without\n \u{1b}[32m--platform\u{1b}[39m, rig solves for this machine.\n\n\u{1b}[1m\u{1b}[34mExamples:\u{1b}[39m\u{1b}[22m\n rig proj renv export\n rig proj renv export --r-version 4.5 --platform ubuntu-24.04"; const ABOUT_PROJ_RENV_IMPORT: &str = "Create rproj.toml from an renv.lock file"; @@ -126,7 +126,7 @@ const HELP_RTOOLS_RM: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22 const ABOUT_RTOOLS: &str = "Manage Rtools installations"; const HELP_RTOOLS: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Manage Rtools installations (Windows only).\n\n Rtools is the collection of build tools (compilers, \u{1b}[32mmake\u{1b}[39m, etc.) needed to\n build R packages from source on Windows. Each R version needs a matching\n Rtools version.\n\n \u{1b}[32mrig rtools list\u{1b}[39m lists the installed Rtools versions. \u{1b}[32mrig rtools add\u{1b}[39m\n installs Rtools, by default every version needed by the currently installed\n R versions. \u{1b}[32mrig rtools rm\u{1b}[39m removes Rtools versions.\n\n On non-Windows platforms this command does nothing and is hidden."; const ABOUT_RUN: &str = "Run R, an R script or an R project"; -const HELP_RUN: &str = "\u{1b}[1m\u{1b}[34mDescription:\u{1b}[39m\u{1b}[22m\n Run R, an R script or an R project, using the selected R version.\n\n All of these examples allow an \u{1b}[32m--r-version\u{1b}[39m argument, to use a specific R\n version.\n\n rig run # start R\n rig run # run an R script\n rig run -f # run an R script\n rig run -e # evaluate an R expression\n rig run ::