Skip to content

Commit ffb4007

Browse files
committed
Add a built-in for setting the PPM repo in a running session.
This commit adds a `.ps.set_ppm_repo()` built-in that works the same way as the existing CLI option, but allows the repo to be set at runtime. It is intended to support making the `positron.r.packageManagerRepository` setting apply without needing to restart/recreate existing R sessions. Part of posit-dev/positron#10965. Signed-off-by: Aaron Jacobs <aaron.jacobs@posit.co>
1 parent 6b0a994 commit ffb4007

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

crates/ark/src/modules/positron/repos.R

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ apply_repo_defaults <- function(
1919
repos <- defaults
2020
} else {
2121
if ("CRAN" %in% names(repos) && "CRAN" %in% names(defaults)) {
22-
# If a CRAN repository is set to @CRAN@, and a default provides a
23-
# specific URL, override it. This is the only instance in which we
24-
# replace an already-set repository option with a default.
25-
if (identical(repos[["CRAN"]], "@CRAN@")) {
22+
# If a CRAN repository is set to @CRAN@ or is marked as having been
23+
# updated by "the IDE" *and* a default provides a specific URL,
24+
# override it.
25+
#
26+
# This is the only instance in which we replace an already-set
27+
# repository option with a default.
28+
if (identical(repos[["CRAN"]], "@CRAN@") || isTRUE(attr(repos, "IDE"))) {
2629
repos[["CRAN"]] <- defaults[["CRAN"]]
2730

2831
# Flag this CRAN repository as being set by the IDE. This flag is
@@ -39,3 +42,25 @@ apply_repo_defaults <- function(
3942
}
4043
options(repos = repos)
4144
}
45+
46+
#' Set the Posit Package Manager repository
47+
#'
48+
#' Sets the Posit Package Manager repository URL for the current session. The
49+
#' URL will be processed to point to a Linux distribution-specific binary URL if
50+
#' applicable.
51+
#'
52+
#' This function only overrides the CRAN repository when Ark has previously set
53+
#' it or when it uses placeholder `"@CRAN@"`.
54+
#'
55+
#' @param url A PPM repository URL. Must be in the form
56+
#' `"https://host/repo/snapshot"`, e.g.,
57+
#' `"https://packagemanager.posit.co/cran/latest"`.
58+
#'
59+
#' @return `NULL`, invisibly.
60+
#'
61+
#' @export
62+
.ps.set_ppm_repo <- function(url) {
63+
# Use Ark's built-in PPM binary URL detection.
64+
url <- .ps.Call("ps_get_ppm_binary_url", url)
65+
apply_repo_defaults(c(CRAN = url))
66+
}

crates/ark/src/repos.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ use std::io::BufRead;
1313
use std::io::BufReader;
1414
use std::path::PathBuf;
1515

16+
use anyhow::Context;
1617
use harp::exec::RFunction;
1718
use harp::exec::RFunctionExt;
1819
use harp::RObject;
20+
use libr::SEXP;
1921

2022
use crate::modules::ARK_ENVS;
2123

@@ -346,6 +348,28 @@ fn get_ppm_binary_package_repo(repo_url: Option<url::Url>) -> String {
346348
}
347349
}
348350

351+
#[harp::register]
352+
pub extern "C-unwind" fn ps_get_ppm_binary_url(url: SEXP) -> anyhow::Result<SEXP> {
353+
let url_string = unsafe { RObject::view(url).to::<String>().context("`url` must be a string")? };
354+
if url_string.is_empty() {
355+
return Err(anyhow::anyhow!("Empty Package Manager URL provided"));
356+
}
357+
358+
// Validate the PPM URL structure.
359+
let parsed = url::Url::parse(&url_string).context("Invalid URL format")?;
360+
let segments = parsed
361+
.path_segments()
362+
.ok_or_else(|| anyhow::anyhow!("Package Manager URL must have a path"))?;
363+
if segments.count() != 2 {
364+
return Err(anyhow::anyhow!(
365+
"Package Manager URL must have exactly 2 path segments (e.g., /cran/latest)"
366+
));
367+
}
368+
369+
let final_url = get_ppm_binary_package_repo(Some(parsed));
370+
Ok(RObject::from(final_url).sexp)
371+
}
372+
349373
#[cfg(test)]
350374
mod tests {
351375
use super::*;

0 commit comments

Comments
 (0)