Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@
* `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
installs R and the packages as needed.
installs R and the packages as needed. `rig run --upgrade` and
`rig run --upgrade-package` upgrade the packages of a script's
environment.

* New `rig proj lock --upgrade-package` (`-P`) option, to upgrade only
some packages, and keep the versions `rproj.lock` pins for the rest. It
also takes a version requirement, e.g. `-P 'cli@>= 3.6.4'`, for this
run only (#410).

* `rig proj lock` now keeps the versions `rproj.lock` pins when it has to
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 add` now has a `--json` option. It prints information about the
installed R version (or the already installed version that was kept),
Expand Down
88 changes: 88 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,34 @@ pub fn rig_app() -> Command {
.required(false)
.conflicts_with_all(["cmd", "shell"]),
)
.arg(
Arg::new("upgrade")
.help(
"Re-resolve every dependency of a script with a `# /// script`\n\
block, instead of keeping the versions its environment has",
)
.long("upgrade")
.short('U')
.action(clap::ArgAction::SetTrue)
.required(false)
.conflicts_with_all(["eval", "cmd", "list", "shell", "app-type"]),
)
.arg(
Arg::new("upgrade-package")
.help(
"Upgrade only these packages in the environment of a script\n\
with a `# /// script` block. As <package> or\n\
<package>@<version>. Comma-separated, and can be repeated.",
)
.long("upgrade-package")
.short('P')
.value_name("PACKAGE")
.num_args(1)
.value_delimiter(',')
.action(clap::ArgAction::Append)
.required(false)
.conflicts_with_all(["upgrade", "eval", "cmd", "list", "shell", "app-type"]),
)
.arg(
Arg::new("command")
.help("R script, project script name, project or R CMD command to run, with parameters")
Expand Down Expand Up @@ -1769,6 +1797,24 @@ pub fn rig_app() -> Command {
.short('U')
.num_args(0)
.required(false),
)
.arg(
Arg::new("upgrade-package")
.help(
"Upgrade only these packages, and keep the versions\n\
rproj.lock pins for the rest, where they still fit.\n\
As <package> or <package>@<version>, e.g. cli or\n\
'cli@>= 3.6.4'. The version is not written to\n\
rproj.toml. Comma-separated, and can be repeated.",
)
.long("upgrade-package")
.short('P')
.value_name("PACKAGE")
.num_args(1)
.value_delimiter(',')
.action(clap::ArgAction::Append)
.required(false)
.conflicts_with("upgrade"),
),
)
.subcommand(
Expand Down Expand Up @@ -3096,6 +3142,48 @@ mod tests {
assert_eq!(platforms, vec!["macos", "ubuntu-24.04"]);
}

#[test]
fn proj_lock_upgrade_package_is_repeatable_and_comma_separated() {
let matches = rig_app()
.try_get_matches_from([
"rig",
"proj",
"lock",
"-P",
"cli,glue",
"--upgrade-package",
"rlang@>= 1.1",
])
.unwrap();
let (_name, sub) = matches.subcommand().unwrap();
let (_name, sub) = sub.subcommand().unwrap();
let pkgs: Vec<&String> = sub.get_many::<String>("upgrade-package").unwrap().collect();
assert_eq!(pkgs, vec!["cli", "glue", "rlang@>= 1.1"]);
}

#[test]
fn proj_lock_upgrade_and_upgrade_package_conflict() {
assert!(rig_app()
.try_get_matches_from(["rig", "proj", "lock", "-U", "-P", "cli"])
.is_err());
}

#[test]
fn run_upgrade_flags_are_for_scripts() {
let matches = rig_app()
.try_get_matches_from(["rig", "run", "-P", "cli", "script.R"])
.unwrap();
let (_name, sub) = matches.subcommand().unwrap();
let pkgs: Vec<&String> = sub.get_many::<String>("upgrade-package").unwrap().collect();
assert_eq!(pkgs, vec!["cli"]);
assert!(rig_app()
.try_get_matches_from(["rig", "run", "-U", "script.R"])
.is_ok());
assert!(rig_app()
.try_get_matches_from(["rig", "run", "-U", "-e", "1"])
.is_err());
}

#[test]
fn proj_lock_add_platform_is_repeatable_and_comma_separated() {
let matches = rig_app()
Expand Down
2 changes: 1 addition & 1 deletion src/dcf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ impl std::fmt::Display for DepVersionSpec {
// e.g. Depends, or it can be used for the combined dependencies of a
// package

#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PackageDependencies {
pub dependencies: Vec<DepVersionSpec>,
}
Expand Down
4 changes: 2 additions & 2 deletions src/help-generated.in

Large diffs are not rendered by default.

36 changes: 34 additions & 2 deletions src/help/proj-lock.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,41 @@ A `rig proj lock` run that finds an existing `rproj.lock` already satisfying
anything. This applies to ordinary dependencies, an existing pin that still
satisfies the manifest's version requirement is kept, even if a newer
version has since been published, as well as to git/GitHub dependencies (see
below). Use `rig proj lock --upgrade` to ignore the existing lock file and
below).

If `rproj.toml` changed, e.g. after `rig proj add`, rig solves the
dependencies again, but it still keeps the versions `rproj.lock` pins, as
long as they fit. So adding a package only changes the versions it needs
changed, and does not upgrade the rest of the project. A platform or R
version that is new to the lock file gets the same versions as the other
platforms of the same R version, where possible.

Use `rig proj lock --upgrade` to ignore the existing lock file and
re-resolve every dependency instead, picking the latest version that still
satisfies `rproj.toml`.

Use `--upgrade-package` to upgrade only some packages, and keep the rest:

```sh
rig proj lock --upgrade-package cli
rig proj lock -P cli,glue
```

The packages they depend on are only upgraded if they need to be. You can
also give a version requirement, with the same `<package>@<version>` syntax
as `rig proj add`, e.g. to upgrade or downgrade to a specific version:

```sh
rig proj lock -P 'cli@>= 3.6.4'
rig proj lock -P 'cli@=3.6.2'
```

The requirement only applies to this run, it is not written to
`rproj.toml`. Later `rig proj lock` runs keep the version it picked, as
long as it fits `rproj.toml`. A git/GitHub dependency named in
`--upgrade-package` is checked against its remote again, like with
`--upgrade`.

## Excluding newer package versions

`--exclude-newer` makes the solver ignore CRAN package versions published
Expand Down Expand Up @@ -130,7 +161,8 @@ exclude-newer = "2025-06-01"
```

`rproj.lock` records the cutoff in its own `[tool.rig]` table, and a lock
solved with a different one is not reused. For a span, the lock records the
solved with a different one is solved again. This keeps the pinned versions
that are not newer than the new cutoff, see "Sticky lock files" above. For a span, the lock records the
span as well, and the lock is reused as long as the span stays the same, so
the cutoff does not move every day. Use `--upgrade` to solve again with a
fresh cutoff.
Expand Down
10 changes: 10 additions & 0 deletions src/help/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ share one environment, and changing the block creates a new one. A script
with a block always uses its own environment, even inside a project.
`--r-version` selects the R version, which must fit the `R` requirement.

Like a project's lock file, the environment keeps the package versions it
was created with. Use `--upgrade` to solve the dependencies again, with the
latest versions that fit, or `--upgrade-package` to upgrade only some
packages, the same way as for [`rig proj lock`](proj.qmd#rig-proj-lock):

```sh
rig run --upgrade script.R
rig run --upgrade-package cli script.R
```

`rig cache clean --category scripts` deletes all script environments. A
script can start with `#!/usr/bin/env -S rig run`, to run it directly.

Expand Down
4 changes: 3 additions & 1 deletion src/pkg/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ use crate::pkgsource::{parse_pkg_source, PkgSource};
use crate::proj::{
dep_table_from_local, dep_table_from_remote, dep_table_from_url, download_lockfile_packages,
fetch_and_read_git_package, fetch_and_read_url_package, lockfile_package_info,
proj_binary_target, read_local_package, resolve_git_sources, sc_proj_solve_deps, BASE_PKGS,
proj_binary_target, read_local_package, resolve_git_sources, sc_proj_solve_deps, SolvePins,
BASE_PKGS,
};
use crate::repos::DbSourcePackageLoader;
use crate::rproj::{DepTable, RprojLockPackage, RprojLockTarget};
Expand Down Expand Up @@ -131,6 +132,7 @@ pub fn sc_pkg_install(
target,
prefer_binary,
exclude_newer.as_deref(),
&SolvePins::default(),
true,
)?;
OUTPUT.success("Solved dependencies");
Expand Down
Loading
Loading