From 5758d67622c0e810137db81aad4ae0273d11e06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Mon, 25 May 2026 17:13:45 +0200 Subject: [PATCH 1/5] Export a cooldown filter --- NAMESPACE | 1 + R/available_packages.R | 13 +++++++++++++ R/filter_cooldown.R | 22 ++++++++++++++++++++++ man/cooldown.Rd | 29 +++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 R/filter_cooldown.R create mode 100644 man/cooldown.Rd diff --git a/NAMESPACE b/NAMESPACE index 99579f0..5a4f53b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export(action_disallow) +export(cooldown) export(last_rejected) export(last_rejected_permit) export(package_filter) diff --git a/R/available_packages.R b/R/available_packages.R index 54c58d9..558684d 100644 --- a/R/available_packages.R +++ b/R/available_packages.R @@ -76,6 +76,12 @@ package_filter <- local({ # define NAs in case where metric_db is missing known metrics metric_defaults <- as.list(rep_len(NA, length.out = length(metrics()))) names(metric_defaults) <- names(metrics()) + + if (is.na(metric_db)) { + db <- as.data.frame(metric_defaults) + db$Package <- NA_character_ + metric_db <- db + } # build our evaluation environemnt and evaluate filter expression db <- db[!is.na(db[, "Package"]), ] @@ -178,7 +184,14 @@ available_metric_fields <- function(repos = getOption("repos")) { #' @importFrom val.meter class_package_matrix class_metric_data_frame available_metrics <- function(repos = opt("repos")) { + if (!length(repos)) { + return(NA_character_) + } + is_metric_db <- vlapply(repos, repo_is_metric_db) + if (!length(is_metric_db)) { + return(NA_character_) + } db <- available.packages( repos = repos[is_metric_db], fields = available_metric_fields(repos = repos), diff --git a/R/filter_cooldown.R b/R/filter_cooldown.R new file mode 100644 index 0000000..a6e4b21 --- /dev/null +++ b/R/filter_cooldown.R @@ -0,0 +1,22 @@ +#' Filter by date +#' +#' Implement a cooldown filter so that only packages that are older than a given threshold are installed. +#' +#' This helps to prevent installing packages recently published with a bug or an infiltration. +#' For the same reason it prevents installing updates and patches of recently fixed packages. +#' @param accepted A date of some time in the past until which published packages are accepted. +#' @param ... Other arguments passed to package_filter. +#' +#' @returns A filter +#' @export +#' +#' @examples +#' ap_wo <- available.packages() +#' dim(ap_wo) +#' ap <- available.packages(filters = cooldown()) +#' dim(ap) +cooldown <- function(accepted = Sys.Date() - 2*7, ...){ + stopifnot(is.Date(accepted)) + stopifnot(accepted < Sys.Date()) + package_filter(as.Date(Date) <= accepted, ...) + } \ No newline at end of file diff --git a/man/cooldown.Rd b/man/cooldown.Rd new file mode 100644 index 0000000..858c0a6 --- /dev/null +++ b/man/cooldown.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/filter_cooldown.R +\name{cooldown} +\alias{cooldown} +\title{Filter by date} +\usage{ +cooldown(accepted = Sys.Date() - 2 * 7, ...) +} +\arguments{ +\item{accepted}{A date of some time in the past until which published packages are accepted.} + +\item{...}{Other arguments passed to package_filter.} +} +\value{ +A filter +} +\description{ +Implement a cooldown filter so that only packages that are older than a given threshold are installed. +} +\details{ +This helps to prevent installing packages recently published with a bug or an infiltration. +For the same reason it prevents installing updates and patches of recently fixed packages. +} +\examples{ +ap_wo <- available.packages() +dim(ap_wo) +ap <- available.packages(filters = cooldown()) +dim(ap) +} From 1991707a0680894e19cadf74bae1c58d6be601b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Wed, 3 Jun 2026 12:48:14 +0200 Subject: [PATCH 2/5] Fix minor stuff --- R/filter_cooldown.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/filter_cooldown.R b/R/filter_cooldown.R index a6e4b21..67452d0 100644 --- a/R/filter_cooldown.R +++ b/R/filter_cooldown.R @@ -1,13 +1,13 @@ #' Filter by date #' #' Implement a cooldown filter so that only packages that are older than a given threshold are installed. -#' -#' This helps to prevent installing packages recently published with a bug or an infiltration. +#' +#' This helps to prevent installing packages recently published with a bug or an infiltration. #' For the same reason it prevents installing updates and patches of recently fixed packages. #' @param accepted A date of some time in the past until which published packages are accepted. #' @param ... Other arguments passed to package_filter. #' -#' @returns A filter +#' @returns A filter #' @export #' #' @examples @@ -15,8 +15,8 @@ #' dim(ap_wo) #' ap <- available.packages(filters = cooldown()) #' dim(ap) -cooldown <- function(accepted = Sys.Date() - 2*7, ...){ - stopifnot(is.Date(accepted)) +cooldown <- function(accepted = Sys.Date() - 2 * 7, ...) { + stopifnot(is(accepted, "Date")) stopifnot(accepted < Sys.Date()) package_filter(as.Date(Date) <= accepted, ...) - } \ No newline at end of file +} From d8c57c071200b3ca2a429bc61cb0a5c661774d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= Date: Thu, 4 Jun 2026 15:37:17 +0200 Subject: [PATCH 3/5] Update filtering step so it works --- NAMESPACE | 1 + R/available_packages.R | 43 ++++++++++++++++++++++++------------------ R/filter_cooldown.R | 2 +- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 5a4f53b..b132c0b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,4 +14,5 @@ importFrom(utils,packageName) importFrom(utils,packageVersion) importFrom(val.meter,class_metric_data_frame) importFrom(val.meter,class_package_matrix) +importFrom(val.meter,metric_coerce) importFrom(val.meter,metrics) diff --git a/R/available_packages.R b/R/available_packages.R index 558684d..efc76c1 100644 --- a/R/available_packages.R +++ b/R/available_packages.R @@ -41,7 +41,7 @@ #' # attempt to install it #' install.packages(filtered_pkg) #' } -#' +#' @importFrom val.meter metric_coerce #' @export package_filter <- local({ filter_id <- 0L @@ -77,23 +77,23 @@ package_filter <- local({ metric_defaults <- as.list(rep_len(NA, length.out = length(metrics()))) names(metric_defaults) <- names(metrics()) - if (is.na(metric_db)) { - db <- as.data.frame(metric_defaults) - db$Package <- NA_character_ - metric_db <- db + if (!is.na(metric_db)) { + # build our evaluation environemnt and evaluate filter expression + db <- db[!is.na(db[, "Package"]), ] + db <- as.data.frame(db) + met <- convert(class_package_matrix(metric_db), class_metric_data_frame) + met$Metric <- TRUE + + db <- merge(db, met, by = c("Package", "Version", "MD5sum"), all = TRUE) + rownames(db) <- db[, "Package"] + defaults <- metric_defaults() + } else { + defaults <- NA + db <- as.data.frame(db) } - # build our evaluation environemnt and evaluate filter expression - db <- db[!is.na(db[, "Package"]), ] - db <- as.data.frame(db) - met <- convert(class_package_matrix(metric_db), class_metric_data_frame) - met$Metric <- TRUE - - db <- merge(db, met, by = c("Package", "Version", "MD5sum"), all = TRUE) - rownames(db) <- db[, "Package"] - # evaluate filter - envir <- build_filter_envir(values = db, envir = envir) + envir <- build_filter_envir(values = db, envir = envir, defaults = defaults) db$Include <- eval(cond, envir = envir) db$Exception[db$Package %in% exceptions] <- "allow list" @@ -130,10 +130,17 @@ build_filter_envir <- function( defaults = metric_defaults(), envir = parent.frame() ) { - defaults_envir <- with(defaults, environment()) - parent.env(defaults_envir) <- envir + if (is.matrix(values)) { + values <- as.data.frame(values) + } value_envir <- with(values, environment()) - parent.env(value_envir) <- defaults_envir + if (!is.na(defaults)) { + defaults_envir <- with(defaults, environment()) + parent.env(defaults_envir) <- envir + parent.env(value_envir) <- defaults_envir + } else { + parent.env(value_envir) <- envir + } value_envir } diff --git a/R/filter_cooldown.R b/R/filter_cooldown.R index 67452d0..cc34d56 100644 --- a/R/filter_cooldown.R +++ b/R/filter_cooldown.R @@ -18,5 +18,5 @@ cooldown <- function(accepted = Sys.Date() - 2 * 7, ...) { stopifnot(is(accepted, "Date")) stopifnot(accepted < Sys.Date()) - package_filter(as.Date(Date) <= accepted, ...) + package_filter(as.Date(Published) <= accepted, envir = environment(), ...) } From 20a0d2a7acc2b4c3cf0971990a36a95a6bddcd69 Mon Sep 17 00:00:00 2001 From: dgkf <18220321+dgkf@users.noreply.github.com> Date: Thu, 20 Aug 2026 16:43:03 -0400 Subject: [PATCH 4/5] minor cleanup --- R/available_packages.R | 17 +++++++++++++---- R/filter_cooldown.R | 33 +++++++++++++++++++++------------ man/cooldown.Rd | 28 ++++++++++++++++++---------- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/R/available_packages.R b/R/available_packages.R index efc76c1..5dde5e8 100644 --- a/R/available_packages.R +++ b/R/available_packages.R @@ -60,6 +60,8 @@ package_filter <- local({ add = TRUE, envir = parent.frame() ) { + force(envir) + metric_db <- db cond <- substitute(cond) @@ -76,14 +78,14 @@ package_filter <- local({ # define NAs in case where metric_db is missing known metrics metric_defaults <- as.list(rep_len(NA, length.out = length(metrics()))) names(metric_defaults) <- names(metrics()) - + if (!is.na(metric_db)) { # build our evaluation environemnt and evaluate filter expression db <- db[!is.na(db[, "Package"]), ] db <- as.data.frame(db) met <- convert(class_package_matrix(metric_db), class_metric_data_frame) met$Metric <- TRUE - + db <- merge(db, met, by = c("Package", "Version", "MD5sum"), all = TRUE) rownames(db) <- db[, "Package"] defaults <- metric_defaults() @@ -93,7 +95,12 @@ package_filter <- local({ } # evaluate filter - envir <- build_filter_envir(values = db, envir = envir, defaults = defaults) + envir <- build_filter_envir( + values = db, + envir = envir, + defaults = defaults + ) + db$Include <- eval(cond, envir = envir) db$Exception[db$Package %in% exceptions] <- "allow list" @@ -194,7 +201,7 @@ available_metrics <- function(repos = opt("repos")) { if (!length(repos)) { return(NA_character_) } - + is_metric_db <- vlapply(repos, repo_is_metric_db) if (!length(is_metric_db)) { return(NA_character_) @@ -208,3 +215,5 @@ available_metrics <- function(repos = opt("repos")) { # drop rows with missing Package field (used to drop `Format: ` header) db[!is.na(db[, "Package"]), ] } + + diff --git a/R/filter_cooldown.R b/R/filter_cooldown.R index cc34d56..836cf65 100644 --- a/R/filter_cooldown.R +++ b/R/filter_cooldown.R @@ -1,22 +1,31 @@ #' Filter by date #' -#' Implement a cooldown filter so that only packages that are older than a given threshold are installed. +#' Implement a cooldown filter so that only packages that are older than a given +#' threshold are installed. #' -#' This helps to prevent installing packages recently published with a bug or an infiltration. -#' For the same reason it prevents installing updates and patches of recently fixed packages. -#' @param accepted A date of some time in the past until which published packages are accepted. -#' @param ... Other arguments passed to package_filter. +#' This helps to prevent installing packages recently published with a bug or an +#' infiltration. For the same reason it prevents installing updates and patches +#' of recently fixed packages. #' -#' @returns A filter -#' @export +#' @param accepted A date of some time in the past until which published +#' packages are accepted. +#' @param ... Other arguments passed to [`package_filter`]. +#' +#' @returns A [`package_filter`] #' #' @examples -#' ap_wo <- available.packages() -#' dim(ap_wo) -#' ap <- available.packages(filters = cooldown()) -#' dim(ap) +#' # entire available packages set +#' ap_complete <- available.packages() +#' nrow(ap_complete) +#' +#' # available packages with cooldown filter applied +#' ap <- available.packages(fields = "Published", filters = cooldown()) +#' ap <- subset(as.data.frame(ap), as.logical(Include)) +#' nrow(ap) +#' +#' @export cooldown <- function(accepted = Sys.Date() - 2 * 7, ...) { stopifnot(is(accepted, "Date")) stopifnot(accepted < Sys.Date()) - package_filter(as.Date(Published) <= accepted, envir = environment(), ...) + package_filter(as.Date(Published) <= accepted, ...) } diff --git a/man/cooldown.Rd b/man/cooldown.Rd index 858c0a6..c95f21c 100644 --- a/man/cooldown.Rd +++ b/man/cooldown.Rd @@ -7,23 +7,31 @@ cooldown(accepted = Sys.Date() - 2 * 7, ...) } \arguments{ -\item{accepted}{A date of some time in the past until which published packages are accepted.} +\item{accepted}{A date of some time in the past until which published +packages are accepted.} -\item{...}{Other arguments passed to package_filter.} +\item{...}{Other arguments passed to \code{\link{package_filter}}.} } \value{ -A filter +A \code{\link{package_filter}} } \description{ -Implement a cooldown filter so that only packages that are older than a given threshold are installed. +Implement a cooldown filter so that only packages that are older than a given +threshold are installed. } \details{ -This helps to prevent installing packages recently published with a bug or an infiltration. -For the same reason it prevents installing updates and patches of recently fixed packages. +This helps to prevent installing packages recently published with a bug or an +infiltration. For the same reason it prevents installing updates and patches +of recently fixed packages. } \examples{ -ap_wo <- available.packages() -dim(ap_wo) -ap <- available.packages(filters = cooldown()) -dim(ap) +# entire available packages set +ap_complete <- available.packages() +nrow(ap_complete) + +# available packages with cooldown filter applied +ap <- available.packages(fields = "Published", filters = cooldown()) +ap <- subset(as.data.frame(ap), as.logical(Include)) +nrow(ap) + } From 867b1f8024a86ff4fc2a5fb1cab78da47767d116 Mon Sep 17 00:00:00 2001 From: dgkf <18220321+dgkf@users.noreply.github.com> Date: Thu, 20 Aug 2026 16:49:09 -0400 Subject: [PATCH 5/5] chore: update pkgdown --- pkgdown/_pkgdown.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index ddc13c2..676d6c3 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -66,6 +66,13 @@ reference: - contents: - package_filter + - title: > + Available filters + desc: > + A selection of ready-made filters + - contents: + - cooldown + - title: > Actions desc: >