Some checks failed
ci/crow/manual/auto-apply-patches Pipeline was successful
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
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/cron/process-updates/11 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/cron/process-updates/6 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/cron/process-updates/5 Pipeline was successful
ci/crow/manual/build-all-versions-install-deps/2 Pipeline was successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/manual/trial-build-registry/13 Pipeline was successful
ci/crow/manual/trial-build-registry/15 Pipeline was successful
ci/crow/manual/trial-build-registry/11 Pipeline was successful
ci/crow/manual/trial-build-registry/7 Pipeline was successful
ci/crow/manual/trial-build-registry/9 Pipeline was successful
ci/crow/manual/trial-build-registry/1 Pipeline was successful
ci/crow/manual/trial-build-registry/5 Pipeline was successful
ci/crow/manual/trial-build-registry/3 Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/manual/trial-build-registry/17 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline failed
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/manual/build-all-versions/8 Pipeline failed
ci/crow/manual/build-all-versions/7 Pipeline failed
ci/crow/cron/process-updates/2 Pipeline failed
ci/crow/manual/build-all-versions/6 Pipeline failed
ci/crow/manual/build-all-versions/5 Pipeline failed
ci/crow/manual/trial-build-registry/8 Pipeline was successful
ci/crow/manual/trial-build-registry/2 Pipeline was successful
ci/crow/manual/trial-build-registry/10 Pipeline was successful
ci/crow/manual/trial-build-registry/14 Pipeline was successful
ci/crow/manual/trial-build-registry/12 Pipeline was successful
ci/crow/manual/trial-build-registry/16 Pipeline was successful
ci/crow/manual/trial-build-registry/6 Pipeline was successful
ci/crow/manual/trial-build-registry/4 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/manual/trial-build-registry/18 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
## Problem
The first real `auto-apply-patches` run classified, limited to the top-10, and validated the candidate registry cleanly, then died at the push step:
```
Validating candidate registry:
Patch registry OK (13 entries).
sh: syntax error: unexpected "("
Error in git("commit", "-m", ...): git commit -m feat(patches): auto-propose 10 registry entries ... failed
```
Root cause: R's `system2()` with captured output (`stdout=TRUE`) runs the command through `/bin/sh`, and the arguments were passed **unquoted**. The commit message `feat(patches): ...` contains `()`, which the shell tried to interpret. The same class of bug affects the `^{commit}` and `ref:path` git refs in the trial-build gate.
## Fix
`shQuote()` every git argument:
- `propose-patches.R` -- the `git()` helper used by `--open-pr` (commit, push, checkout).
- `trial-build-registry.R` -- the base-ref reads (`rev-parse ... ^{commit}`, `ls-tree`, `show ref:path`).
## Verification
- Reproduced against a real git repo: the unquoted call fails with status 2 (the same `unexpected "("`); the `shQuote`d call commits successfully, and `rev-parse HEAD^{commit}` resolves.
- 105 tests pass; all pre-commit hooks pass.
Everything else in that run was correct: 842,659 failing builds classified, RcppParallel's 895 dependents correctly reported as blocked (not proposed), top-10 tbb-stddef candidates selected, 69 deferred, registry validated. Only the shell quoting was broken.
Reviewed-on: #126
152 lines
4.1 KiB
R
152 lines
4.1 KiB
R
#!/usr/bin/env Rscript
|
|
|
|
# Merge gate for the auto-patch PR (issue #115, step 3): for every registry
|
|
# entry the PR ADDS that applies to this platform, trial-build the package with
|
|
# the registry applied, in this platform's own build-env image. Nothing is
|
|
# uploaded, archived, or written to the metadata DB.
|
|
#
|
|
# Exit 0 only if every new entry's package builds; exit 1 if any fails, so it
|
|
# gates the PR. A platform with no new entries is a fast no-op.
|
|
#
|
|
# Usage (inside a build-env image):
|
|
# PLATFORM=ubuntu-2604 Rscript local/trial-build-registry.R [base_ref]
|
|
# base_ref git ref to diff the registry against (default: origin/main)
|
|
|
|
options(error = function() {
|
|
cat("ERROR:", geterrmessage(), "\n", file = stdout())
|
|
q(status = 1)
|
|
})
|
|
|
|
suppressPackageStartupMessages({
|
|
library(jsonlite, quietly = TRUE)
|
|
library(bincraft, quietly = TRUE)
|
|
})
|
|
|
|
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_
|
|
})
|
|
script_dir <- if (is.na(script_path)) "local" else dirname(script_path)
|
|
source(file.path(script_dir, "proposal-tracking-lib.R"))
|
|
|
|
args <- commandArgs(trailingOnly = TRUE)
|
|
base_ref <- if (length(args) >= 1L) {
|
|
args[[1L]]
|
|
} else {
|
|
Sys.getenv("BASE_REF", "origin/main")
|
|
}
|
|
os <- Sys.getenv("PLATFORM", "")
|
|
if (!nzchar(os)) {
|
|
stop("PLATFORM env var is not set (e.g. ubuntu-2604).")
|
|
}
|
|
|
|
patches_dir <- file.path(script_dir, "patches")
|
|
registry_file <- file.path(patches_dir, "registry.json")
|
|
current <- if (file.exists(registry_file)) {
|
|
jsonlite::fromJSON(registry_file, simplifyVector = FALSE)
|
|
} else {
|
|
list()
|
|
}
|
|
# Read the registry at base_ref. Fail loud if the ref or file can't be read:
|
|
# silently treating the base as empty would trial-build the WHOLE registry
|
|
# instead of just the entries the branch adds.
|
|
registry_rel <- "local/patches/registry.json"
|
|
# system2() with captured output runs via /bin/sh, so shell-quote the git args
|
|
# (refs contain "^{}" and ":" that the shell would otherwise mangle).
|
|
ref_ok <- suppressWarnings(system2(
|
|
"git",
|
|
shQuote(c(
|
|
"rev-parse",
|
|
"--verify",
|
|
"--quiet",
|
|
sprintf("%s^{commit}", base_ref)
|
|
)),
|
|
stdout = TRUE,
|
|
stderr = FALSE
|
|
))
|
|
if (!is.null(attr(ref_ok, "status"))) {
|
|
stop(sprintf("base ref %s does not resolve to a commit.", base_ref))
|
|
}
|
|
in_base <- suppressWarnings(system2(
|
|
"git",
|
|
shQuote(c("ls-tree", base_ref, "--", registry_rel)),
|
|
stdout = TRUE,
|
|
stderr = FALSE
|
|
))
|
|
file_in_base <- length(in_base) > 0L && any(nzchar(in_base))
|
|
base_json <- suppressWarnings(system2(
|
|
"git",
|
|
shQuote(c("show", sprintf("%s:%s", base_ref, registry_rel))),
|
|
stdout = TRUE,
|
|
stderr = FALSE
|
|
))
|
|
show_ok <- is.null(attr(base_json, "status"))
|
|
if (file_in_base && !show_ok) {
|
|
stop(sprintf(
|
|
"could not read %s at %s; refusing to build the whole registry.",
|
|
registry_rel,
|
|
base_ref
|
|
))
|
|
}
|
|
base <- if (show_ok && length(base_json) > 0L) {
|
|
jsonlite::fromJSON(paste(base_json, collapse = "\n"), simplifyVector = FALSE)
|
|
} else {
|
|
list() # file genuinely absent at base -> every entry is new
|
|
}
|
|
|
|
pkgs <- new_registry_packages(current, base, os = os)
|
|
if (length(pkgs) == 0L) {
|
|
cat(sprintf(
|
|
"No new registry entries apply to %s; nothing to trial-build.\n",
|
|
os
|
|
))
|
|
q(status = 0)
|
|
}
|
|
|
|
cat(sprintf(
|
|
"Trial-building %d new registry %s on %s (vs %s):\n %s\n",
|
|
length(pkgs),
|
|
if (length(pkgs) == 1L) "entry" else "entries",
|
|
os,
|
|
base_ref,
|
|
toString(pkgs)
|
|
))
|
|
|
|
results <- vapply(
|
|
pkgs,
|
|
function(pkg) {
|
|
cat(sprintf("\n=== trial build: %s ===\n", pkg))
|
|
tryCatch(
|
|
{
|
|
bincraft::build_binary_package(
|
|
pkg,
|
|
tag_limit = 1L,
|
|
patches = patches_dir,
|
|
archive = FALSE,
|
|
upload = FALSE,
|
|
store_build_metadata = FALSE
|
|
)
|
|
TRUE
|
|
},
|
|
error = function(e) {
|
|
cat(sprintf("FAILED %s: %s\n", pkg, conditionMessage(e)))
|
|
FALSE
|
|
}
|
|
)
|
|
},
|
|
logical(1L)
|
|
)
|
|
|
|
failed <- pkgs[!results]
|
|
cat(sprintf(
|
|
"\n%d/%d passed on %s.%s\n",
|
|
sum(results),
|
|
length(results),
|
|
os,
|
|
if (length(failed) > 0L) sprintf(" Failed: %s", toString(failed)) else ""
|
|
))
|
|
if (length(failed) > 0L) {
|
|
q(status = 1)
|
|
}
|
|
q(status = 0)
|