Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
265c48a
Copy over SSO code from pak
gaborcsardi May 8, 2026
13bfbdf
Wire up PPM SSO auth
gaborcsardi May 8, 2026
3be1c0e
Rewrite ppm_sso_app w/o httr2
gaborcsardi May 8, 2026
60a20ac
Avoid |>
gaborcsardi May 8, 2026
7cc7abf
Use httr2 instead of curl
gaborcsardi May 8, 2026
3179a85
Avoid openssl dependency
gaborcsardi May 8, 2026
170b36e
Refactor PPM SSO
gaborcsardi May 11, 2026
e2e0370
Fix tests for Bioconductor & PPM updates
gaborcsardi May 11, 2026
81fd015
Fix snapshot test
gaborcsardi May 11, 2026
f71a62c
Fix snapshot tests
gaborcsardi May 11, 2026
8a9bcd3
Merge branch 'main' into feature/ppm-sso
gaborcsardi May 12, 2026
7ead6c2
New webfakes app w/o auth0
gaborcsardi May 12, 2026
8d8c231
PPM SSO improvements
gaborcsardi May 12, 2026
3043656
Update pkgdown reference for ppm_sso* functions
gaborcsardi May 12, 2026
864ab23
Fix spell check
gaborcsardi May 12, 2026
4e148dd
Fix snapshot tests
gaborcsardi May 12, 2026
c1039a1
Do not suppress warnings from repo_add()
gaborcsardi May 13, 2026
c76466a
Better ppm_sso_status() w/x credentials
gaborcsardi May 13, 2026
2e4772b
Update ppm sso token file keys
gaborcsardi May 13, 2026
d066882
Refactor ppm_sso_device_flow() for pak
gaborcsardi May 13, 2026
03e327d
PPM SSO: fix parsing response
gaborcsardi May 13, 2026
9fea7cf
PPM SSO: improve messaging when called from pak
gaborcsardi May 13, 2026
ecb7f93
PPM SSO: fix browser open from pak
gaborcsardi May 13, 2026
7b0f2c4
Fix a redirected URL
gaborcsardi May 13, 2026
3ddf313
Docs for ppm_sso_status()
gaborcsardi May 13, 2026
07b4dcf
PPM SSO docs examples
gaborcsardi May 13, 2026
542124b
Refine docs
gaborcsardi May 13, 2026
d9a5ad9
Fix adding first token to a token file
gaborcsardi May 13, 2026
805c731
Some PPM SSO tests
gaborcsardi May 16, 2026
37baeeb
Merge branch 'main' into feature/ppm-sso
gaborcsardi Jun 1, 2026
e37f56a
Update for ts -> tsitter renaming
gaborcsardi Jun 1, 2026
c73330c
Merge branch 'main' into feature/ppm-sso
gaborcsardi Jun 1, 2026
63ae3c6
PPM SSO apps can redirect to a proper CRAN-like server
gaborcsardi Jun 1, 2026
0dd67f3
Env var to change PPM SSO token file
gaborcsardi Jun 5, 2026
5393c73
Fix tests on new R devel
gaborcsardi Jun 5, 2026
18006e1
Merge branch 'main' into feature/ppm-sso
gaborcsardi Jun 11, 2026
4b0e035
Merge branch 'main' into feature/ppm-sso
gaborcsardi Jun 16, 2026
c36ec9f
Rewrite HTTP options handling
gaborcsardi Jul 10, 2026
4cc23c1
Fix tests if some HTTP options are already set
gaborcsardi Jul 10, 2026
7dfbf81
Update format-suggest action version
gaborcsardi Jul 10, 2026
3f59272
Reformat with newer air
gaborcsardi Jul 10, 2026
a684689
GHA: update actions/checkout versions everywhere
gaborcsardi Jul 10, 2026
6e5db98
Reformat with newer air
gaborcsardi Jul 10, 2026
a38f49c
Update embedded async, to get HTTP retries
gaborcsardi Jul 13, 2026
be78805
Run tests in parallel
gaborcsardi Jul 13, 2026
8ae7aab
R ignore .claude
gaborcsardi Jul 13, 2026
5fb81a5
Need to utils:: qualify a read.table call
gaborcsardi Jul 13, 2026
9133641
Update embedded async, simplify http tests
gaborcsardi Jul 13, 2026
f5ddc28
Do not run async tests in parallel
gaborcsardi Jul 13, 2026
9f7facc
Try without parallel tests
gaborcsardi Jul 13, 2026
2de169f
No need to suggest covr
gaborcsardi Jul 13, 2026
0dfd36c
Avoid retries in HTTP failure tests
gaborcsardi Jul 13, 2026
12a775c
Update embedded async (tests)
gaborcsardi Jul 13, 2026
6865757
Merge branch 'main' into feature/ppm-sso
gaborcsardi Jul 13, 2026
a0f4234
Configure retries with option or env var
gaborcsardi Jul 14, 2026
3c7f2c1
Improve error for corrupt cache
gaborcsardi Jul 15, 2026
bd57c92
Do not fail when cleaning pkg cache with corrupt RDS
gaborcsardi Jul 15, 2026
3044b14
Improve error message for corrupt cache
gaborcsardi Jul 15, 2026
68d8a1b
Avoid duplicate / in repo URLs
gaborcsardi Jul 15, 2026
9fdc423
Fix test snapshot, used on older R
gaborcsardi Jul 15, 2026
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
5 changes: 5 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Imports:
processx (>= 3.3.0.9001),
R6,
tools,
tsitter,
tstoml,
utils
Suggests:
debugme,
Expand All @@ -38,6 +40,9 @@ Suggests:
webfakes (>= 1.1.5),
withr,
zip
Remotes:
r-lib/tsitter,
gaborcsardi/tstoml
Config/Needs/website: tidyverse/tidytemplate
Config/testthat/edition: 3
Config/usethis/last-upkeep: 2025-04-30
Expand Down
5 changes: 5 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Generated by roxygen2: do not edit by hand

S3method("[",pkgcache_repo_status_summary)
S3method(format,ppm_sso_status)
S3method(print,pkgcache_repo_status_summary)
S3method(print,ppm_sso_status)
S3method(summary,pkgcache_repo_status)
export(bioc_devel_version)
export(bioc_release_version)
Expand Down Expand Up @@ -41,6 +43,9 @@ export(ppm_platforms)
export(ppm_r_versions)
export(ppm_repo_url)
export(ppm_snapshots)
export(ppm_sso_login)
export(ppm_sso_logout)
export(ppm_sso_status)
export(repo_add)
export(repo_auth)
export(repo_get)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# pkgcache (development version)

* Better behavior for a corrupt package cache database: better error
message, and cleaning the cache does not fail in this case
(https://github.com/r-lib/pak/issues/884).

* pkgcache now treats `PACKAGES` entries with `Path` and/or `File`
fields correctly (#141, @jeroen).

Expand Down
41 changes: 23 additions & 18 deletions R/async-http.R
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
# The embedded async `http_get()` etc. only look at the `async_http_*` options
# (via `get_default_curl_options()`). `set_pkgcache_curl_options()` reads the
# pkgcache-level options and environment variables and sets the corresponding
# curl options, so that pkgcache's HTTP requests honor them. For each curl
# option it looks, in decreasing priority, at the `pkgcache_*` option, the
# `PKGCACHE_*` environment variable, the `pkg_http_*` option and the
# `PKG_HTTP_*` environment variable.
#
# It only sets an option when it is actually configured. An unset option must
# not be turned into an explicit curl option, otherwise it would override the
# `async_http_*` option and the async default further downstream in
# `get_default_curl_options()`.

set_pkgcache_curl_options <- function(options) {
set_pkgcache_http_options <- function(options, retry = NULL) {
nms <- c(
"timeout",
"connecttimeout",
Expand Down Expand Up @@ -41,16 +28,34 @@ set_pkgcache_curl_options <- function(options) {
options[[nm]] <- as.integer(v)
}
}
options

if (is.null(retry)) {
v <- getopt("retry")
retry <- if (is.null(v)) TRUE else coerce_retry(v)
}

list(options = options, retry = retry)
}

coerce_retry <- function(v) {
if (!is.character(v)) {
return(v)
}
if (toupper(v) %in% c("TRUE", "FALSE")) {
as.logical(v)
} else {
as.integer(v)
}
}

# Wrap an embedded async HTTP function so that it applies the pkgcache-level
# HTTP options and environment variables (see `set_pkgcache_curl_options()`)
# HTTP options and environment variables (see `set_pkgcache_http_options()`)
# to every request.
wrap_pkgcache_http <- function(fun) {
force(fun)
mark_as_async(function(url, ..., options = list()) {
fun(url, ..., options = set_pkgcache_curl_options(options))
mark_as_async(function(url, ..., options = list(), retry = NULL) {
http <- set_pkgcache_http_options(options, retry)
fun(url, ..., options = http$options, retry = http$retry)
})
}

Expand Down
39 changes: 36 additions & 3 deletions R/auth.R
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ repo_auth_headers <- function(
# - host URL w/o username
# We try each with and without a keyring username
urls <- unique(unlist(
parsed_url[c("repouserurl", "repourl", "hostuserurl", "hosturl")]
parsed_url[c("repouserurl", "repourl", "hostuserurl", "hosturl", "host")]
))

if (use_cache) {
Expand All @@ -199,10 +199,18 @@ repo_auth_headers <- function(
error = NULL
)

pwd <- repo_auth_netrc(parsed_url$host, parsed_url$username)
pwd <- repo_auth_sso(parsed_url$repourl, parsed_url$username)
if (!is.null(pwd)) {
res$auth_domain <- parsed_url$host
res$source <- paste0(".netrc")
res$source <- "SSO"
}

if (is.null(pwd)) {
pwd <- repo_auth_netrc(parsed_url$host, parsed_url$username)
if (!is.null(pwd)) {
res$auth_domain <- parsed_url$host
res$source <- paste0(".netrc")
}
}

if (is.null(pwd) && !requireNamespace("keyring", quietly = TRUE)) {
Expand Down Expand Up @@ -461,3 +469,28 @@ repo_auth_netrc <- function(host, username) {

NULL
}

repo_auth_sso <- function(repourl, username) {
ppm_url <- Sys.getenv("PACKAGEMANAGER_ADDRESS", NA_character_)
if (is.na(ppm_url)) {
return(NULL)
}

if (!startsWith(repourl, ppm_url)) {
return(NULL)
}

token <- tryCatch(
ppm_sso_auth(repourl),
error = function(e) {
cli::cli_alert_warning(
"PPM SSO authentication failed for repo {.url {repourl}}: {conditionMessage(e)}"
)
cli::cli_alert_info(
"Try calling {.code ppm_sso_login()} directly."
)
NULL
}
)
token
}
2 changes: 2 additions & 0 deletions R/metadata-cache.R
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,8 @@ cmc__get_repos <- function(repos, bioc, cran_mirror, r_version, auth = TRUE) {
}
}

# avoid duplicate //
res$url <- sub("/+$", "", res$url)
res <- res[!duplicated(res$url), ]
if (auth) {
res <- add_auth_status(res)
Expand Down
1 change: 1 addition & 0 deletions R/onload.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## nocov start

pkgenv <- new.env(parent = emptyenv())
pkgenv$ppm_sso_cache <- new.env(parent = emptyenv())

pkgenv$r_versions <- list(
list(version = "0.60", date = "1997-12-04T08:47:58.000000Z"),
Expand Down
46 changes: 37 additions & 9 deletions R/package-cache.R
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ package_cache <- R6Class(
l <- private$lock(exclusive = FALSE)
on.exit(filelock::unlock(l), add = TRUE)
dbfile <- get_db_file(private$path)
readRDS(dbfile)
read_db_rds(dbfile)
},

find = function(..., .list = NULL) {
Expand Down Expand Up @@ -152,7 +152,7 @@ package_cache <- R6Class(
l <- private$lock(exclusive = TRUE)
on.exit(filelock::unlock(l), add = TRUE)
dbfile <- get_db_file(private$path)
db <- readRDS(dbfile)
db <- read_db_rds(dbfile)

extra <- c(list(...), .list)

Expand Down Expand Up @@ -429,13 +429,23 @@ package_cache <- R6Class(
l <- private$lock(exclusive = TRUE)
on.exit(filelock::unlock(l), add = TRUE)
dbfile <- get_db_file(private$path)

ex <- private$find_locked(..., .list = .list)
if (nrow(ex) != 0) {
unlink(file.path(private$path, ex$path))
db <- delete_from_data_frame(readRDS(dbfile), ..., .list = .list)
save_rds(db, dbfile)
pkgs <- c(list(...), .list)

if (length(pkgs) == 0) {
files <- list.files(private$path, recursive = TRUE, full.names = TRUE)
files <- setdiff(files, get_lock_file(private$path))
unlink(files, recursive = TRUE, force = TRUE)
unlink(dbfile, force = TRUE)
} else {
db <- read_db_rds(dbfile)
idx <- find_in_data_frame(db, .list = pkgs)
if (length(idx) != 0) {
unlink(file.path(private$path, db$path[idx]))
save_rds(db[-idx, ], dbfile)
}
}

invisible()
}
),

Expand All @@ -449,7 +459,7 @@ package_cache <- R6Class(
},
find_locked = function(..., .list = NULL) {
dbfile <- get_db_file(private$path)
db <- readRDS(dbfile)
db <- read_db_rds(dbfile)

idx <- find_in_data_frame(db, ..., .list = .list)
db[idx, ]
Expand All @@ -468,6 +478,24 @@ get_lock_file <- function(path) {
file.path(path, ".db.lock")
}

read_db_rds <- function(dbfile) {
tryCatch(
suppressWarnings(readRDS(dbfile)),
error = function(err) {
cond <- new_error(
"Failed to read the package cache database from ",
encodeString(dbfile, quote = "`"),
". The cache file is possibly corrupt, call ",
"`pkgcache::pkg_cache_delete_files()` or `pak::cache_clean()` ",
"to reset the cache, and then try again."
)
cond$dbfile <- dbfile
class(cond) <- c("pkgcache_corrupt_db_error", class(cond))
throw(cond, parent = err)
}
)
}

create_empty_db_file_if_needed <- function(path) {
mkdirp(path)

Expand Down
Loading