fix(local): stop proposing per-dependent patches for dependency-cascade failures #121
4 changed files with 87 additions and 1 deletions
|
|
@ -15,6 +15,11 @@
|
||||||
# would require a brand-new source diff for a previously-unseen package stay
|
# would require a brand-new source diff for a previously-unseen package stay
|
||||||
# `auto = FALSE` -> classified, but always routed to human triage, per the
|
# `auto = FALSE` -> classified, but always routed to human triage, per the
|
||||||
# issue's guardrail against shipping autonomous novel source diffs.
|
# issue's guardrail against shipping autonomous novel source diffs.
|
||||||
|
# `applies_to` (optional) pins a package-specific fix to the package it targets:
|
||||||
|
# a signature whose registry patch belongs to one package (e.g. RcppParallel's
|
||||||
|
# disable-tbb patch) is only proposed for that package. Other packages matching
|
||||||
|
# the signature are downstream failures blocked on that dependency, not
|
||||||
|
# individually patchable, so they are reported but never proposed an entry.
|
||||||
#
|
#
|
||||||
# Seeded from the existing registry entries and known recurring failures; add
|
# Seeded from the existing registry entries and known recurring failures; add
|
||||||
# a row here as new signatures are confirmed. Order matters: the first match
|
# a row here as new signatures are confirmed. Order matters: the first match
|
||||||
|
|
@ -45,6 +50,14 @@ build_signatures <- function() {
|
||||||
tier = "patch",
|
tier = "patch",
|
||||||
confidence = "high",
|
confidence = "high",
|
||||||
auto = TRUE,
|
auto = TRUE,
|
||||||
|
# The fix is a RcppParallel-specific source patch, so it is only ever
|
||||||
|
# proposed for RcppParallel itself. The hundreds of packages that fail
|
||||||
|
# merely because their RcppParallel *dependency* did not build carry
|
||||||
|
# RcppParallel's error text and match this signature too -- they are
|
||||||
|
# blocked on RcppParallel, not individually patchable (applying this diff
|
||||||
|
# to their source is meaningless). `applies_to` pins the proposal so those
|
||||||
|
# downstream failures are never proposed a bogus per-package entry.
|
||||||
|
applies_to = "RcppParallel",
|
||||||
fix = "apply the curated RcppParallel/disable-tbb.patch so the bundled TBB build is skipped and the TinyThread backend is used",
|
fix = "apply the curated RcppParallel/disable-tbb.patch so the bundled TBB build is skipped and the TinyThread backend is used",
|
||||||
example = "RcppParallel",
|
example = "RcppParallel",
|
||||||
registry = list(
|
registry = list(
|
||||||
|
|
@ -278,9 +291,23 @@ build_triage_report <- function(
|
||||||
unregistered <- setdiff(pkgs, registered_pkgs)
|
unregistered <- setdiff(pkgs, registered_pkgs)
|
||||||
auto_proposable <- isTRUE(sig$auto) && sig$matched
|
auto_proposable <- isTRUE(sig$auto) && sig$matched
|
||||||
|
|
||||||
|
# A signature whose fix is package-specific (`applies_to`) may only be
|
||||||
|
# proposed for that package. Everything else matching it is a downstream
|
||||||
|
# failure blocked on that package (e.g. RcppParallel dependents carrying
|
||||||
|
# RcppParallel's own error text) -- never propose those a bogus entry.
|
||||||
|
proposable <- unregistered
|
||||||
|
blocked_on <- NULL
|
||||||
|
if (!is.null(sig$applies_to)) {
|
||||||
|
proposable <- intersect(unregistered, sig$applies_to)
|
||||||
|
downstream <- setdiff(pkgs, sig$applies_to)
|
||||||
|
if (length(downstream) > 0L) {
|
||||||
|
blocked_on <- sig$applies_to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proposed <- list()
|
proposed <- list()
|
||||||
if (auto_proposable) {
|
if (auto_proposable) {
|
||||||
for (p in unregistered) {
|
for (p in proposable) {
|
||||||
proposed[[p]] <- propose_registry_entry_list(
|
proposed[[p]] <- propose_registry_entry_list(
|
||||||
sig,
|
sig,
|
||||||
p,
|
p,
|
||||||
|
|
@ -297,6 +324,8 @@ build_triage_report <- function(
|
||||||
tier = sig$tier,
|
tier = sig$tier,
|
||||||
confidence = sig$confidence,
|
confidence = sig$confidence,
|
||||||
suggested_fix = sig$fix,
|
suggested_fix = sig$fix,
|
||||||
|
applies_to = sig$applies_to,
|
||||||
|
blocked_on = blocked_on,
|
||||||
fingerprint = names(fp_tab)[[1L]],
|
fingerprint = names(fp_tab)[[1L]],
|
||||||
fingerprint_variants = length(fp_tab),
|
fingerprint_variants = length(fp_tab),
|
||||||
build_count = nrow(g),
|
build_count = nrow(g),
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,13 @@ for (r in report) {
|
||||||
)
|
)
|
||||||
cat(paste0(" ", gsub("\n", "\n ", j)), "\n", sep = "")
|
cat(paste0(" ", gsub("\n", "\n ", j)), "\n", sep = "")
|
||||||
}
|
}
|
||||||
|
} else if (!is.null(r$blocked_on)) {
|
||||||
|
cat(sprintf(
|
||||||
|
" (blocked on %s -- these %d package(s) fail because that dependency does not build; fix %s, do not patch each dependent)\n",
|
||||||
|
toString(r$blocked_on),
|
||||||
|
length(r$packages),
|
||||||
|
toString(r$blocked_on)
|
||||||
|
))
|
||||||
} else if (r$auto_proposable) {
|
} else if (r$auto_proposable) {
|
||||||
cat(" (all affected packages already have a registry entry)\n")
|
cat(" (all affected packages already have a registry entry)\n")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,21 @@ 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) !is.null(r$blocked_on), report)
|
||||||
|
if (length(blocked) > 0L) {
|
||||||
|
cat("\nBlocked on a dependency (fix the dependency, not each dependent):\n")
|
||||||
|
for (r in blocked) {
|
||||||
|
cat(sprintf(
|
||||||
|
" %s: %d package(s) fail because %s does not build\n",
|
||||||
|
toString(r$blocked_on),
|
||||||
|
length(r$packages),
|
||||||
|
toString(r$blocked_on)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# A package that maps to more than one auto-proposable signature is ambiguous
|
# A package that maps to more than one auto-proposable signature is ambiguous
|
||||||
# (conflicting fix tiers) and would collide on the same registry key; route it
|
# (conflicting fix tiers) and would collide on the same registry key; route it
|
||||||
# to human triage instead of emitting both.
|
# to human triage instead of emitting both.
|
||||||
|
|
|
||||||
|
|
@ -85,3 +85,38 @@ test_that("propose_registry_entry fills a schema-valid entry for a known lever",
|
||||||
# No template -> no proposal (unclassified path).
|
# No template -> no proposal (unclassified path).
|
||||||
expect_null(propose_registry_entry(classify_error("weird"), "x", "alpine"))
|
expect_null(propose_registry_entry(classify_error("weird"), "x", "alpine"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test_that("RcppParallel dependents are blocked, not proposed a per-package patch", {
|
||||||
|
# Hundreds of packages fail on ubuntu-2604 only because their RcppParallel
|
||||||
|
# dependency fails to build, so they carry RcppParallel's TBB error text.
|
||||||
|
failures <- data.frame(
|
||||||
|
name = c("ACEsimFit", "AovBay", "AdaptGauss"),
|
||||||
|
platform = "ubuntu-2604",
|
||||||
|
arch = "amd64",
|
||||||
|
error_text = "Error: USE_TBB=Linux is not supported on this toolchain",
|
||||||
|
stringsAsFactors = FALSE
|
||||||
|
)
|
||||||
|
report <- build_triage_report(failures, registered_pkgs = character(0L))
|
||||||
|
grp <- Filter(function(g) g$signature == "rcppparallel-bundled-tbb", report)[[
|
||||||
|
1L
|
||||||
|
]]
|
||||||
|
# No bogus per-dependent entries (the patch targets RcppParallel's source).
|
||||||
|
expect_null(grp$proposed_entries)
|
||||||
|
expect_identical(grp$blocked_on, "RcppParallel")
|
||||||
|
})
|
||||||
|
|
||||||
|
test_that("RcppParallel itself is still proposed when it is the failing package", {
|
||||||
|
failures <- data.frame(
|
||||||
|
name = "RcppParallel",
|
||||||
|
platform = "ubuntu-2604",
|
||||||
|
arch = "amd64",
|
||||||
|
error_text = "Error: USE_TBB=Linux is not supported on this toolchain",
|
||||||
|
stringsAsFactors = FALSE
|
||||||
|
)
|
||||||
|
report <- build_triage_report(failures, registered_pkgs = character(0L))
|
||||||
|
grp <- report[[1L]]
|
||||||
|
expect_identical(grp$signature, "rcppparallel-bundled-tbb")
|
||||||
|
expect_false(is.null(grp$proposed_entries))
|
||||||
|
expect_true("RcppParallel" %in% names(grp$proposed_entries))
|
||||||
|
expect_null(grp$blocked_on) # RcppParallel is the target, not a dependent
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue