feat(local): aggregate blocked-on-dependency reporting by dependency (#131)
Some checks failed
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline failed
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/manual/weekly-patch-proposals Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/manual/trial-build-registry/7 Pipeline failed
ci/crow/manual/trial-build-registry/5 Pipeline failed
ci/crow/manual/trial-build-registry/3 Pipeline failed
ci/crow/manual/trial-build-registry/13 Pipeline failed
ci/crow/manual/trial-build-registry/1 Pipeline failed
ci/crow/manual/trial-build-registry/11 Pipeline failed
ci/crow/manual/trial-build-registry/4 Pipeline failed
ci/crow/manual/trial-build-registry/2 Pipeline failed
ci/crow/manual/trial-build-registry/6 Pipeline failed
ci/crow/manual/trial-build-registry/9 Pipeline failed
ci/crow/manual/trial-build-registry/17 Pipeline failed
ci/crow/manual/trial-build-registry/15 Pipeline failed
ci/crow/manual/trial-build-registry/10 Pipeline failed
ci/crow/manual/trial-build-registry/14 Pipeline failed
ci/crow/manual/trial-build-registry/16 Pipeline failed
ci/crow/manual/trial-build-registry/12 Pipeline failed
ci/crow/manual/trial-build-registry/8 Pipeline failed
ci/crow/manual/trial-build-registry/18 Pipeline failed
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/14 Pipeline was successful
ci/crow/cron/process-updates/15 Pipeline was successful
ci/crow/cron/process-updates/16 Pipeline was successful
Some checks failed
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline failed
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/manual/weekly-patch-proposals Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/manual/trial-build-registry/7 Pipeline failed
ci/crow/manual/trial-build-registry/5 Pipeline failed
ci/crow/manual/trial-build-registry/3 Pipeline failed
ci/crow/manual/trial-build-registry/13 Pipeline failed
ci/crow/manual/trial-build-registry/1 Pipeline failed
ci/crow/manual/trial-build-registry/11 Pipeline failed
ci/crow/manual/trial-build-registry/4 Pipeline failed
ci/crow/manual/trial-build-registry/2 Pipeline failed
ci/crow/manual/trial-build-registry/6 Pipeline failed
ci/crow/manual/trial-build-registry/9 Pipeline failed
ci/crow/manual/trial-build-registry/17 Pipeline failed
ci/crow/manual/trial-build-registry/15 Pipeline failed
ci/crow/manual/trial-build-registry/10 Pipeline failed
ci/crow/manual/trial-build-registry/14 Pipeline failed
ci/crow/manual/trial-build-registry/16 Pipeline failed
ci/crow/manual/trial-build-registry/12 Pipeline failed
ci/crow/manual/trial-build-registry/8 Pipeline failed
ci/crow/manual/trial-build-registry/18 Pipeline failed
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/14 Pipeline was successful
ci/crow/cron/process-updates/15 Pipeline was successful
ci/crow/cron/process-updates/16 Pipeline was successful
## Why With cascade detection (#128) live, the latest `auto-apply-patches` run did exactly the right thing — **proposed nothing** (`No auto-proposable candidates`) because every failure is a dependency cascade, and it surfaced the ~30 root-cause dependencies to fix. But the "Blocked on a dependency" list printed **one line per fingerprint group**, so the same dependency repeated (rstan ×4, lpsymphony ×4, salso ×2, BH ×2, GO.db ×2, RcppCWB ×2, …), burying the priority. ## What Aggregate blocked packages across all groups **by the dependency they wait on**: - Expose `blocked_map` (package → dependency) from `build_triage_report()`. - Add `blocked_by_dependency()` — dedupes dependents (a package in two groups counts once) and ranks dependencies by how many distinct dependents they block. - Proposer and tracker (log + issue) now print one line per dependency, sorted by impact. Replaces the per-group `blocked_summary`. ## Result (same data, aggregated) ``` Blocked on a dependency (3 dependencies block 6 dependents; fix the dependency, not each dependent): RcppParallel 3 dependent(s) rstan 2 dependent(s) sf 1 dependent(s) ``` So the real run becomes a crisp, ranked worklist: RcppParallel (894), sf (128), rstan (~96), Rfast (33), clarabel/DescTools (26), Rglpk (22), xgboost (18), … ## Verified New test covers cross-group aggregation, dedup (a dependent in two groups counted once), the example cap, and ranking. 112 tests pass; hooks pass. Reviewed-on: #131
This commit is contained in:
parent
118a92889f
commit
6c03f278ec
5 changed files with 68 additions and 39 deletions
|
|
@ -385,6 +385,7 @@ build_triage_report <- function(
|
|||
applies_to = sig$applies_to,
|
||||
blocked_on = blocked_on,
|
||||
blocked_packages = blocked_packages,
|
||||
blocked_map = if (length(blocked_map) > 0L) blocked_map else NULL,
|
||||
fingerprint = names(fp_tab)[[1L]],
|
||||
fingerprint_variants = length(fp_tab),
|
||||
build_count = nrow(g),
|
||||
|
|
|
|||
|
|
@ -160,24 +160,38 @@ unclassified_summary <- function(report, max_groups = 30L, max_pkgs = 15L) {
|
|||
)
|
||||
}
|
||||
|
||||
# Groups with packages blocked on a dependency (a package-specific fix pinned
|
||||
# via `applies_to`, or a data-driven cascade where the package fails building a
|
||||
# dependency): report the dependency + how many dependents wait on it, so fixing
|
||||
# it once is recognised as clearing the batch.
|
||||
blocked_summary <- function(report, max_pkgs = 15L) {
|
||||
bl <- Filter(
|
||||
function(g) length(g$blocked_packages %||% character(0L)) > 0L,
|
||||
report
|
||||
)
|
||||
lapply(bl, function(g) {
|
||||
pkgs <- g$blocked_packages
|
||||
# Aggregate blocked packages across ALL groups by the dependency they wait on,
|
||||
# so one dependency (RcppParallel, rstan, sf, ...) is a single line -- deduped
|
||||
# and ranked by how many distinct dependents it blocks -- instead of repeating
|
||||
# once per fingerprint group. Reads each group's `blocked_map` (package -> the
|
||||
# dependency it is blocked on). Returns records sorted by dependent count desc,
|
||||
# each with up to `max_pkgs` example dependents.
|
||||
blocked_by_dependency <- function(report, max_pkgs = 15L) {
|
||||
acc <- list() # dependency -> character vector of dependent packages
|
||||
for (g in report) {
|
||||
bm <- g$blocked_map
|
||||
if (is.null(bm) || length(bm) == 0L) {
|
||||
next
|
||||
}
|
||||
for (pkg in names(bm)) {
|
||||
for (dep in as.character(unlist(bm[[pkg]]))) {
|
||||
acc[[dep]] <- unique(c(acc[[dep]], pkg))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (length(acc) == 0L) {
|
||||
return(list())
|
||||
}
|
||||
out <- lapply(names(acc), function(dep) {
|
||||
pkgs <- acc[[dep]]
|
||||
list(
|
||||
blocked_on = g$blocked_on,
|
||||
dependency = dep,
|
||||
n_packages = length(pkgs),
|
||||
packages = utils::head(pkgs, max_pkgs),
|
||||
packages_truncated = length(pkgs) > max_pkgs
|
||||
)
|
||||
})
|
||||
out[order(-vapply(out, function(x) x$n_packages, integer(1L)))]
|
||||
}
|
||||
|
||||
# Does a registry entry's `platforms` apply to a build on `os` (e.g.
|
||||
|
|
|
|||
|
|
@ -127,13 +127,13 @@ if (length(retire) > 0L) {
|
|||
# ---------------------------------------------------------------------------
|
||||
# Blind spots: failures the classifier could not auto-propose.
|
||||
# ---------------------------------------------------------------------------
|
||||
blocked <- blocked_summary(report)
|
||||
blocked <- blocked_by_dependency(report)
|
||||
unmatched <- unclassified_summary(report)
|
||||
|
||||
cat("\nBlocked on a dependency (fix the dependency, not each dependent):\n")
|
||||
if (length(blocked) > 0L) {
|
||||
for (b in blocked) {
|
||||
cat(sprintf(" %s: %d dependent(s) waiting\n", b$blocked_on, b$n_packages))
|
||||
cat(sprintf(" %-20s %5d dependent(s)\n", b$dependency, b$n_packages))
|
||||
}
|
||||
} else {
|
||||
cat(" (none)\n")
|
||||
|
|
@ -207,8 +207,8 @@ if (do_issue) {
|
|||
body_lines <- c(
|
||||
body_lines,
|
||||
sprintf(
|
||||
"- **%s**: %d dependent(s) waiting (e.g. %s%s)",
|
||||
b$blocked_on,
|
||||
"- **%s**: %d dependent(s) (e.g. %s%s)",
|
||||
b$dependency,
|
||||
b$n_packages,
|
||||
toString(b$packages),
|
||||
if (isTRUE(b$packages_truncated)) ", ..." else ""
|
||||
|
|
|
|||
|
|
@ -148,21 +148,20 @@ for (r in report) {
|
|||
}
|
||||
}
|
||||
|
||||
# Groups blocked on a dependency (e.g. RcppParallel dependents) are reported,
|
||||
# not proposed: fixing the named dependency clears them all at once.
|
||||
blocked <- Filter(
|
||||
function(r) length(r$blocked_packages) > 0L,
|
||||
report
|
||||
)
|
||||
# Packages blocked on a dependency are reported (aggregated by dependency,
|
||||
# ranked by impact), not proposed: fixing the named dependency clears the batch.
|
||||
blocked <- blocked_by_dependency(report)
|
||||
if (length(blocked) > 0L) {
|
||||
cat("\nBlocked on a dependency (fix the dependency, not each dependent):\n")
|
||||
for (r in blocked) {
|
||||
n_blocked_pkgs <- length(unique(unlist(
|
||||
lapply(report, function(r) r$blocked_packages)
|
||||
)))
|
||||
cat(sprintf(
|
||||
" %s: %d package(s) fail because %s does not build\n",
|
||||
toString(r$blocked_on),
|
||||
length(r$blocked_packages),
|
||||
toString(r$blocked_on)
|
||||
"\nBlocked on a dependency (%d dependencies block %d dependents; fix the dependency, not each dependent):\n",
|
||||
length(blocked),
|
||||
n_blocked_pkgs
|
||||
))
|
||||
for (b in blocked) {
|
||||
cat(sprintf(" %-20s %5d dependent(s)\n", b$dependency, b$n_packages))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -146,20 +146,35 @@ test_that("unclassified_summary ranks unknown groups and caps output", {
|
|||
expect_identical(capped$dropped_groups, 1L)
|
||||
})
|
||||
|
||||
test_that("blocked_summary lists each dependency and its dependent count", {
|
||||
test_that("blocked_by_dependency aggregates across groups, deduped and ranked", {
|
||||
# RcppParallel dependents split across platforms/fingerprints -> separate
|
||||
# groups, but one aggregated line; a dependent seen twice is counted once.
|
||||
failures <- data.frame(
|
||||
name = c("ACEsimFit", "AovBay", "AdaptGauss"),
|
||||
platform = "ubuntu-2604",
|
||||
name = c("ACEsimFit", "AovBay", "AdaptGauss", "ACEsimFit", "loner"),
|
||||
platform = c(
|
||||
"ubuntu-2604",
|
||||
"ubuntu-2604",
|
||||
"alpine-324",
|
||||
"alpine-324",
|
||||
"ubuntu-2604"
|
||||
),
|
||||
arch = "amd64",
|
||||
error_text = "Error: USE_TBB=Linux is not supported on this toolchain",
|
||||
error_text = c(
|
||||
rep("Error: USE_TBB=Linux is not supported on this toolchain", 4L),
|
||||
"Failed to build source package rstan.\nfatal error: tbb/tbb_stddef.h"
|
||||
),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
report <- build_triage_report(failures, registered_pkgs = character(0L))
|
||||
b <- blocked_summary(report, max_pkgs = 2L)
|
||||
expect_length(b, 1L)
|
||||
expect_identical(b[[1L]]$blocked_on, "RcppParallel")
|
||||
expect_identical(b[[1L]]$n_packages, 3L)
|
||||
expect_true(b[[1L]]$packages_truncated)
|
||||
agg <- blocked_by_dependency(report, max_pkgs = 2L)
|
||||
deps <- vapply(agg, function(b) b$dependency, character(1L))
|
||||
expect_true("RcppParallel" %in% deps && "rstan" %in% deps)
|
||||
rcpp <- Filter(function(b) b$dependency == "RcppParallel", agg)[[1L]]
|
||||
# ACEsimFit appears in two groups -> counted once (3 distinct dependents).
|
||||
expect_identical(rcpp$n_packages, 3L)
|
||||
expect_true(rcpp$packages_truncated) # capped at max_pkgs = 2
|
||||
# Ranked by dependent count: RcppParallel (3) before rstan (1).
|
||||
expect_identical(deps[[1L]], "RcppParallel")
|
||||
})
|
||||
|
||||
test_that("entry_applies_to_os matches codename, family, and wildcard", {
|
||||
|
|
|
|||
Loading…
Reference in a new issue