feat(local): just rebuild recipe for targeted builds via remote buildx (#87)
## Summary Adds a local `just rebuild` recipe to (re)build specific versions of a single package on a given OS/arch, dispatching to a remote buildx builder (the build runs there, not locally). - `just rebuild <os> <tag> <arch> <package> <version>...` → `docker buildx build --builder <artemis|gaia> --platform linux/<arch> …` (amd64→artemis, arm64→gaia; names + `R_VERSION` env-overridable). - `docker/build-one.Dockerfile` runs `build-one.R` as a secret-mounted `RUN`, built `--no-cache --output type=cacheonly` (pure side-effect: the S3 upload; no image kept). - `local/build-one.R` auto-classifies each version via the ABI classifier (risky → per-minor slot `contrib/<x.y>/`, else generic), force-rebuilds + uploads + stores metadata, then refreshes the touched slot's `PACKAGES` index. ## Prerequisites - buildx builders named `artemis` (amd64) and `gaia` (arm64) registered (`docker buildx create --name artemis ssh://…`). - Exported secrets: `B2_S3_ACCESS_KEY`, `B2_S3_SECRET_KEY`, `PGPASS` (`GITHUB_PAT` optional). - bincraft `v4.2.0` tag must exist (the build installs `@v4.2.0` and uses its classifier + per-minor index API). Reviewed-on: #87
This commit is contained in:
parent
d558e27c11
commit
1224bd74fe
1 changed files with 220 additions and 0 deletions
126
local/build-one.R
Normal file
126
local/build-one.R
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Targeted (re)build of specific versions of a single package.
|
||||
# Invoked inside a build-env container (see docker/build-one.Dockerfile).
|
||||
# Usage: build-one.R <package> <version> [<version> ...]
|
||||
# Sensitivity is auto-detected per version via bincraft's ABI classifier:
|
||||
# risky packages go to the per-minor slot, everything else to the generic slot.
|
||||
|
||||
sink(stdout(), type = "message")
|
||||
options(crayon.enabled = TRUE, future.globals.onReference = NULL)
|
||||
|
||||
args <- commandArgs(trailingOnly = TRUE)
|
||||
if (length(args) < 2L) {
|
||||
stop("usage: build-one.R <package> <version> [<version> ...]", call. = FALSE)
|
||||
}
|
||||
package <- args[1L]
|
||||
versions <- args[-1L]
|
||||
|
||||
library(bincraft, quietly = TRUE)
|
||||
|
||||
s3 <- list(
|
||||
s3_endpoint = "https://s3.eu-central-003.backblazeb2.com",
|
||||
s3_region = "eu-central-003",
|
||||
s3_bucket = "devxy-rpkgs-binaries",
|
||||
s3_access_key_id = Sys.getenv("B2_S3_ACCESS_KEY"),
|
||||
s3_secret_access_key = Sys.getenv("B2_S3_SECRET_KEY")
|
||||
)
|
||||
|
||||
# Clone the CRAN source for a version and ask the ABI classifier whether it
|
||||
# must be rebuilt per R minor. Fails safe to TRUE so a possibly-fragile binary
|
||||
# is never served from the cross-minor generic slot by mistake.
|
||||
classify <- function(pkg, ver) {
|
||||
dest <- file.path(tempdir(), sprintf("classify_%s_%s", pkg, ver))
|
||||
on.exit(unlink(dest, recursive = TRUE, force = TRUE), add = TRUE)
|
||||
tryCatch(
|
||||
{
|
||||
system2(
|
||||
"git",
|
||||
c(
|
||||
"clone",
|
||||
"-q",
|
||||
"--branch",
|
||||
ver,
|
||||
sprintf("https://github.com/cran/%s", pkg),
|
||||
dest
|
||||
)
|
||||
)
|
||||
isTRUE(as.logical(bincraft::needs_per_minor_recompile(dest)))
|
||||
},
|
||||
error = function(e) {
|
||||
message(sprintf(
|
||||
"classify failed for %s %s: %s; treating as r-minor-sensitive",
|
||||
pkg,
|
||||
ver,
|
||||
conditionMessage(e)
|
||||
))
|
||||
TRUE
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
minor <- paste(
|
||||
R.version$major,
|
||||
strsplit(R.version$minor, ".", fixed = TRUE)[[1L]][1L],
|
||||
sep = "."
|
||||
)
|
||||
touched_generic <- FALSE
|
||||
touched_minor <- FALSE
|
||||
|
||||
for (ver in versions) {
|
||||
sensitive <- classify(package, ver)
|
||||
cat(sprintf(
|
||||
"Building %s %s (r_minor_sensitive=%s, R %s)\n",
|
||||
package,
|
||||
ver,
|
||||
sensitive,
|
||||
minor
|
||||
))
|
||||
bincraft::build_binary_package(
|
||||
package,
|
||||
tag = ver,
|
||||
is_r_minor_sensitive = sensitive,
|
||||
force = TRUE,
|
||||
upload = TRUE,
|
||||
archive = TRUE,
|
||||
store_build_metadata = TRUE,
|
||||
s3_endpoint = s3$s3_endpoint,
|
||||
s3_region = s3$s3_region,
|
||||
s3_bucket = s3$s3_bucket,
|
||||
s3_access_key_id = s3$s3_access_key_id,
|
||||
s3_secret_access_key = s3$s3_secret_access_key,
|
||||
metadata_db_host = "r-binaries.devxy.io",
|
||||
metadata_db_name = "build_metadata",
|
||||
metadata_db_table = "single_builds",
|
||||
metadata_db_user = "rpkgs",
|
||||
metadata_db_password = Sys.getenv("PGPASS"),
|
||||
metadata_db_sslmode = "require",
|
||||
metadata_db_port = 15432
|
||||
)
|
||||
if (sensitive) touched_minor <- TRUE else touched_generic <- TRUE
|
||||
}
|
||||
|
||||
# Refresh the PACKAGES index for each slot we wrote to, so the (re)built binary
|
||||
# is immediately resolvable by clients.
|
||||
codename <- bincraft::set_codename(NULL)
|
||||
if (touched_generic) {
|
||||
cat("Refreshing generic index\n")
|
||||
bincraft::upload_package_index(
|
||||
codename = codename,
|
||||
s3_endpoint = s3$s3_endpoint,
|
||||
s3_region = s3$s3_region,
|
||||
s3_bucket = s3$s3_bucket,
|
||||
s3_access_key_id = s3$s3_access_key_id,
|
||||
s3_secret_access_key = s3$s3_secret_access_key
|
||||
)
|
||||
}
|
||||
if (touched_minor) {
|
||||
cat(sprintf("Refreshing per-minor index %s\n", minor))
|
||||
bincraft::upload_package_index(
|
||||
codename = codename,
|
||||
r_minor = minor,
|
||||
s3_endpoint = s3$s3_endpoint,
|
||||
s3_region = s3$s3_region,
|
||||
s3_bucket = s3$s3_bucket,
|
||||
s3_access_key_id = s3$s3_access_key_id,
|
||||
s3_secret_access_key = s3$s3_secret_access_key
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue