feat(local): auto-propose registry patches and track the feedback loop (#117)
Implements steps 3 + 4 of #115, building on the classifier merged in #116. Now that bincraft **v4.4.3** applies registry `patch`/`makevars`/`configure_args` to the *target* package build (previously deps-only), a trial patched build is a meaningful acceptance gate, so the "propose" half is viable. ## Step 3 — propose, do not apply - **`local/propose-patches.R`** — for each classified, safe fix affecting a package with no current registry entry, emits a pre-filled `registry.json` entry and validates the candidate set against a *temporary* merged registry (the real one is never touched unless asked). - default: print candidates + validation, **take no action** - `--write`: append entries to `registry.json` + the proposals ledger (you commit + open the PR) - `--open-issue`: post/update a Forgejo tracking issue (reuses the weekly-audit `httr2` + `FORGEJO_TOKEN` pattern) - **`local/trial-build-patch.R`** — isolated bincraft build of one package with the registry applied (no upload/archive/metadata; `patchhash` keeps it out of the real cache). Exit 0/1, so it gates a CI step or manual pre-merge check. The human gate stays: nothing merges. Acceptance = `validate-patches.R` passes (checked automatically) **and** the trial build succeeds. Novel source diffs and unknown signatures are never proposed (they carry `auto = FALSE`). ## Step 4 — feedback loop - **`local/proposal-tracking.R`** (read-only) — signature hit rate (builds/pkgs/addressed/open per signature), proposed-vs-merged (a proposal counts merged once its package is in the registry), and retirement candidates (registry entries whose package no longer fails, i.e. likely fixed upstream). - **`local/proposal-tracking-lib.R`** — the pure metric/ledger helpers. ## Supporting changes - Refactored the classify helpers to expose a pure `build_triage_report()` + a list-returning entry builder; `failing-builds-report.R` now renders from the shared function (no behaviour change). - `validate-patches.R` gains optional `PATCH_DIR`/`REGISTRY_FILE` overrides (backward-compatible) so a candidate registry can be validated in isolation. - Documented the propose/trial-build/tracking workflow in `local/patches/README.md`. ## Verification - 71 unit tests pass (incl. new `test-proposal-tracking-lib.R`) under the Dockerized R 4.5.3 build env. - All pre-commit hooks pass (`air-format`, `validate-patches`, prettier, etc.). - Smoke-tested all three entrypoints end-to-end with a stubbed DB: dry-run, `--write` (produces a registry that passes the canonical validator + a valid ledger, then reverted), and the tracker. Closes #115 Reviewed-on: #117
This commit is contained in:
parent
f11ba7172f
commit
1c297c01bf
1 changed files with 990 additions and 119 deletions
65
local/trial-build-patch.R
Normal file
65
local/trial-build-patch.R
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env Rscript
|
||||
|
||||
# Acceptance gate for a proposed registry patch (issue #115, step 3): build one
|
||||
# package in isolation with the current `local/patches` registry applied, and
|
||||
# report whether it succeeds. Nothing is uploaded, archived, or written to the
|
||||
# metadata DB -- `patchhash` keeps the trial binary out of the real cache.
|
||||
#
|
||||
# Must run inside a build-env image (the same image the failing platform uses),
|
||||
# because it invokes the real compiler toolchain via bincraft.
|
||||
#
|
||||
# Usage:
|
||||
# Rscript local/trial-build-patch.R <package> [tag]
|
||||
#
|
||||
# Exit status: 0 if the patched build succeeds, 1 otherwise -- so it can gate a
|
||||
# CI step or a manual pre-merge check.
|
||||
|
||||
args <- commandArgs(trailingOnly = TRUE)
|
||||
if (length(args) < 1L) {
|
||||
stop("usage: Rscript local/trial-build-patch.R <package> [tag]")
|
||||
}
|
||||
package <- args[[1L]]
|
||||
tag <- if (length(args) >= 2L) args[[2L]] else NULL
|
||||
|
||||
script_path <- local({
|
||||
a <- commandArgs(trailingOnly = FALSE)
|
||||
f <- sub("^--file=", "", a[grepl("^--file=", a)])
|
||||
if (length(f) == 1L && nzchar(f)) normalizePath(f) else NA_character_
|
||||
})
|
||||
patches_dir <- file.path(
|
||||
if (is.na(script_path)) "local" else dirname(script_path),
|
||||
"patches"
|
||||
)
|
||||
|
||||
suppressPackageStartupMessages(library(bincraft, quietly = TRUE))
|
||||
|
||||
cat(sprintf(
|
||||
"Trial build: %s%s with registry %s (no upload/archive/metadata)\n",
|
||||
package,
|
||||
if (is.null(tag)) "" else sprintf(" @ %s", tag),
|
||||
patches_dir
|
||||
))
|
||||
|
||||
ok <- tryCatch(
|
||||
{
|
||||
bincraft::build_binary_package(
|
||||
package,
|
||||
tag_limit = 1L,
|
||||
patches = patches_dir,
|
||||
archive = FALSE,
|
||||
upload = FALSE,
|
||||
store_build_metadata = FALSE
|
||||
)
|
||||
TRUE
|
||||
},
|
||||
error = function(e) {
|
||||
cat(sprintf("Trial build FAILED: %s\n", conditionMessage(e)))
|
||||
FALSE
|
||||
}
|
||||
)
|
||||
|
||||
if (ok) {
|
||||
cat(sprintf("Trial build OK: %s builds with the proposed patch.\n", package))
|
||||
q(status = 0)
|
||||
}
|
||||
q(status = 1)
|
||||
Loading…
Reference in a new issue