Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
2561bed5e7 |
9 changed files with 9 additions and 210 deletions
|
|
@ -4,7 +4,7 @@
|
|||
# they are merged with build-all-versions' identical declarations.
|
||||
variables:
|
||||
target_arch:
|
||||
description: 'Architecture to build.'
|
||||
description: "Architecture to build."
|
||||
options:
|
||||
- amd64
|
||||
- arm64
|
||||
|
|
@ -27,7 +27,7 @@ variables:
|
|||
- "noble"
|
||||
default: "3.24"
|
||||
R_VERSION:
|
||||
description: 'Primary R version under /opt/R.'
|
||||
description: "Primary R version under /opt/R."
|
||||
options:
|
||||
- 4.5.3
|
||||
- 4.4.3
|
||||
|
|
@ -74,10 +74,6 @@ steps:
|
|||
commands:
|
||||
# one-time full wipe to fix corrupted .so files from previous failed builds
|
||||
# - rm -rf /mnt/cache/R-pkgs
|
||||
# Clear churny pkgcache metadata left by a prior crashed run (the "patched"
|
||||
# repo mints a new hash per PACKAGES change -> unbounded pkgs-*.rds/patched-*).
|
||||
# Keep pkg/ downloads and the stable CRAN/BioC/INLA repo dirs.
|
||||
- rm -rf /mnt/cache/pkgcache/R/pkgcache/_metadata/patched-* /mnt/cache/pkgcache/R/pkgcache/_metadata/pkgs-*.rds || true
|
||||
- mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages
|
||||
- git clone -q https://pat-s:$$REPO_RO_TOKEN@git.devxy.io/devxy/build-cran-binaries.git .
|
||||
# Pin the same bincraft version the build steps use, so the precomputed
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# Skip list lives in local/excluded-packages.json (read by local/build-all.R).
|
||||
variables:
|
||||
target_arch:
|
||||
description: 'Architecture to build.'
|
||||
description: "Architecture to build."
|
||||
options:
|
||||
- amd64
|
||||
- arm64
|
||||
|
|
@ -31,7 +31,7 @@ variables:
|
|||
- "resolute"
|
||||
default: "3.24"
|
||||
R_VERSION:
|
||||
description: 'Primary R version under /opt/R.'
|
||||
description: "Primary R version under /opt/R."
|
||||
options:
|
||||
- 4.5.3
|
||||
- 4.4.3
|
||||
|
|
@ -116,11 +116,6 @@ steps:
|
|||
- ${ARCH}-binaries-r-dep-cache-${OS}-${OS_VERSION//./}:/mnt/cache
|
||||
commands:
|
||||
- git clone -q https://pat-s:$$REPO_RO_TOKEN@git.devxy.io/devxy/build-cran-binaries.git .
|
||||
# Clear churny pkgcache metadata left by a prior crashed run (the "patched"
|
||||
# repo mints a new hash per PACKAGES change -> unbounded pkgs-*.rds/patched-*).
|
||||
# Keep pkg/ downloads and the stable CRAN/BioC/INLA repo dirs. Within-run
|
||||
# growth is bounded separately by trim_pkgcache_metadata() in build-all.R.
|
||||
- rm -rf /mnt/cache/pkgcache/R/pkgcache/_metadata/patched-* /mnt/cache/pkgcache/R/pkgcache/_metadata/pkgs-*.rds || true
|
||||
- mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages
|
||||
# The primary pass must not rely on build-all-versions-install-deps having
|
||||
# run on *this* agent: depends_on only orders the steps, but the cache
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
# BuildKit GC config for the remote buildx builders (artemis/amd64, gaia/arm64).
|
||||
#
|
||||
# Apply when creating the docker-container builders:
|
||||
# docker buildx create --name artemis --driver docker-container \
|
||||
# --config docker/buildkitd.toml ssh://<user@host-amd64>
|
||||
# docker buildx create --name gaia --driver docker-container \
|
||||
# --config docker/buildkitd.toml ssh://<user@host-arm64>
|
||||
#
|
||||
# Why: BuildKit's default GC caps the ephemeral cache tier — RUN
|
||||
# --mount=type=cache mounts, local build context, git checkouts — at a
|
||||
# hardcoded 512 MB (shown as "488.3 MiB" in `buildx inspect`). Across our
|
||||
# 7-distro build matrix that fills instantly and forces re-downloads of
|
||||
# system + R packages every rebuild. The first rule below raises that tier.
|
||||
#
|
||||
# Limits are absolute (not %) on purpose: artemis and gaia have very
|
||||
# different free space (Hetzner ~42 GiB free vs Mac mini ~279 GiB), so a
|
||||
# percentage would mean wildly different real budgets. minFreeSpace = 20 GB
|
||||
# keeps the tight Hetzner host safe while staying modest on the Mac mini.
|
||||
|
||||
[worker.oci]
|
||||
gc = true
|
||||
|
||||
# Tier 1 — ephemeral caches (cache mounts, local context, git checkouts).
|
||||
# Raised from the 512 MB default to 8 GB, retained for 7 days so weekly
|
||||
# rebuilds reuse downloaded packages instead of re-fetching them.
|
||||
[[worker.oci.gcpolicy]]
|
||||
filters = [
|
||||
"type==source.local",
|
||||
"type==exec.cachemount",
|
||||
"type==source.git.checkout",
|
||||
]
|
||||
keepDuration = "168h"
|
||||
maxUsedSpace = "8GB"
|
||||
|
||||
# Tier 2 — everything else (image layers, RUN exec results). Bounds the
|
||||
# whole buildkit cache and always leaves 20 GB free on the host disk.
|
||||
[[worker.oci.gcpolicy]]
|
||||
all = true
|
||||
reservedSpace = "2GB"
|
||||
maxUsedSpace = "40GB"
|
||||
minFreeSpace = "20GB"
|
||||
6
justfile
6
justfile
|
|
@ -10,10 +10,8 @@
|
|||
# - buildx builders named `artemis` (amd64) and `gaia` (arm64), created with the
|
||||
# docker-container driver (runs BuildKit on the remote host's docker daemon over
|
||||
# SSH). The default `remote` driver does NOT work with an ssh:// docker host.
|
||||
# Pass --config docker/buildkitd.toml so BuildKit's GC keeps a usable cache
|
||||
# (the default caps the cache-mount tier at 512 MB, forcing re-downloads).
|
||||
# docker buildx create --name artemis --driver docker-container --config docker/buildkitd.toml ssh://<user@host-amd64>
|
||||
# docker buildx create --name gaia --driver docker-container --config docker/buildkitd.toml ssh://<user@host-arm64>
|
||||
# docker buildx create --name artemis --driver docker-container ssh://<user@host-amd64>
|
||||
# docker buildx create --name gaia --driver docker-container ssh://<user@host-arm64>
|
||||
# - exported secrets: B2_S3_ACCESS_KEY, B2_S3_SECRET_KEY, PGPASS (GITHUB_PAT optional)
|
||||
#
|
||||
# Overridable (env or `just VAR=… rebuild …`):
|
||||
|
|
|
|||
|
|
@ -64,13 +64,10 @@ sprintf("# of package versions for this job: %s", nrow(chunk))
|
|||
exclude <- jsonlite::fromJSON("local/excluded-packages.json")[["package"]]
|
||||
chunk <- chunk[!chunk$Package %in% exclude, ]
|
||||
|
||||
# Skip package versions already attempted in a previous run (built or errored).
|
||||
# Skip package versions already built in a previous run.
|
||||
# pkgs_to_build.rds is a static snapshot from the install-deps step, so on a
|
||||
# restart it still lists everything an interrupted run already produced. The
|
||||
# metadata DB reflects that progress, so we re-derive the remaining set here.
|
||||
# We exclude *all* attempted versions, not just successful ones: a previously
|
||||
# errored version is skipped by build_binary_package() anyway, so leaving it in
|
||||
# the chunk only makes the job cycle through it one-by-one for no benefit.
|
||||
# Derive platform + arch from the running container, mirroring the codename ->
|
||||
# platform mapping bincraft uses internally. The OS/OS_VERSION selectors are
|
||||
# workflow-level CI variables that are not injected into the container
|
||||
|
|
@ -107,13 +104,13 @@ con <- DBI::dbConnect(
|
|||
)
|
||||
built <- DBI::dbGetQuery(
|
||||
con,
|
||||
"SELECT name, tag FROM single_builds WHERE platform = $1 AND arch = $2",
|
||||
"SELECT name, tag FROM single_builds WHERE platform = $1 AND arch = $2 AND error_occurred = FALSE",
|
||||
params = list(platform, arch)
|
||||
)
|
||||
DBI::dbDisconnect(con)
|
||||
before <- nrow(chunk)
|
||||
chunk <- chunk[!paste(chunk$Package, chunk$Version) %in% paste(built$name, built$tag), ]
|
||||
sprintf("Skipped %d already-attempted package versions; %d remaining for this job", before - nrow(chunk), nrow(chunk))
|
||||
sprintf("Skipped %d already-built package versions; %d remaining for this job", before - nrow(chunk), nrow(chunk))
|
||||
|
||||
# Read pre-computed S3 listing from install-deps step
|
||||
# This avoids loading s3fs/reticulate/Python in the build container,
|
||||
|
|
@ -122,10 +119,6 @@ s3_cache <- readRDS("/mnt/cache/packages/s3_cache.rds")
|
|||
sprintf("S3 cache: %s files", length(s3_cache))
|
||||
|
||||
n <- nrow(chunk)
|
||||
# Every `trim_every` packages, bound the pkgcache _metadata dir so a full-platform
|
||||
# run does not accumulate thousands of ~70 MB snapshots and fill the host disk.
|
||||
# No-op on amd64 (R_PKG_CACHE_DIR is empty / cache not persisted).
|
||||
trim_every <- 25L
|
||||
mapply(
|
||||
function(pkg, ver, sens, i) {
|
||||
cat(sprintf("[%d/%d] %s_%s (r_minor_sensitive=%s)\n", i, n, pkg, ver, sens))
|
||||
|
|
@ -151,12 +144,6 @@ mapply(
|
|||
upload = TRUE,
|
||||
store_build_metadata = TRUE
|
||||
)
|
||||
if (i %% trim_every == 0L) {
|
||||
removed <- trim_pkgcache_metadata()
|
||||
if (removed > 0L) {
|
||||
cat(sprintf(" [pkgcache trim] removed %d stale _metadata entries\n", removed))
|
||||
}
|
||||
}
|
||||
},
|
||||
chunk$Package,
|
||||
chunk$Version,
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
diff --git a/configure b/configure
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -11,6 +11,15 @@
|
||||
PKG_TEST_HEADER="<uv.h>"
|
||||
PKG_LIBS="-luv"
|
||||
|
||||
+# bincraft patch: force the vendored static libuv so the resulting binary
|
||||
+# is self-contained. fs configure otherwise links system libuv whenever
|
||||
+# pkg-config finds libuv-devel (installed as a build-time sysreq), yielding
|
||||
+# an fs.so with NEEDED libuv.so.1 that fails to dyn.load on machines lacking
|
||||
+# runtime libuv (install.packages/renv do not install SystemRequirements).
|
||||
+echo "Building static libuv (bincraft: forced vendored)" 1>&2
|
||||
+cp -f src/Makevars.vendor src/Makevars
|
||||
+exit 0
|
||||
+
|
||||
# Use pkg-config if available
|
||||
if [ `command -v pkg-config` ]; then
|
||||
PKGCONFIG_CFLAGS=`pkg-config --cflags --silence-errors ${PKG_CONFIG_NAME}`
|
||||
|
|
@ -9,16 +9,6 @@
|
|||
"patch": "RcppParallel/disable-tbb.patch",
|
||||
"reason": "bundled Intel TBB build hangs/fails on musl (Alpine) and newer toolchains (g++ 15 on ubuntu-2604); patch unsets USE_TBB and forces -DRCPP_PARALLEL_USE_TBB=0 so RcppParallel skips the bundled build and uses the TinyThread backend"
|
||||
},
|
||||
{
|
||||
"package": "fs",
|
||||
"versions": "*",
|
||||
"platforms": ["*"],
|
||||
"env": {},
|
||||
"configure_args": [],
|
||||
"makevars": {},
|
||||
"patch": "fs/force-vendored-libuv.patch",
|
||||
"reason": "fs 2.x configure links system libuv whenever pkg-config finds libuv-devel (installed as a build-time sysreq), producing an fs.so with NEEDED libuv.so.1. That binary fails to dyn.load on consumer machines lacking runtime libuv, because install.packages/renv do not install SystemRequirements (only pak does, and only in the build container). The patch short-circuits configure to copy src/Makevars.vendor and build the bundled static libuv (needs cmake) so the binary is self-contained on every platform. An env/pkg-config override was tried first but the rebuilt binary still linked libuv.so.1, so a source patch is used instead."
|
||||
},
|
||||
{
|
||||
"package": "rstan",
|
||||
"versions": "*",
|
||||
|
|
|
|||
|
|
@ -31,42 +31,3 @@ parse_build_args <- function(args) {
|
|||
ncpus = as.integer(pos[3L])
|
||||
)
|
||||
}
|
||||
|
||||
# Bound the {pkgcache} metadata dir, which otherwise grows without limit: the
|
||||
# "patched" repo mints a new content hash on every PACKAGES change, so each build
|
||||
# writes a fresh ~70 MB _metadata/pkgs-<hash>.rds (+ patched-<hash>/) that is
|
||||
# never reused. Keep the `keep` newest entries by mtime; only remove entries
|
||||
# older than `min_age_secs`, so a concurrent split-job's in-flight files are
|
||||
# never deleted (each build uses a unique hash, so aged entries are
|
||||
# unreferenced). Stable repo dirs (CRAN-*, BioC*, INLA-*) and pkg/ downloads are
|
||||
# not matched and thus preserved. Returns the number of entries removed.
|
||||
trim_pkgcache_metadata <- function(cache_dir = Sys.getenv("R_PKG_CACHE_DIR"),
|
||||
keep = 20L,
|
||||
min_age_secs = 600) {
|
||||
meta <- file.path(cache_dir, "R", "pkgcache", "_metadata")
|
||||
if (!nzchar(cache_dir) || !dir.exists(meta)) {
|
||||
return(0L)
|
||||
}
|
||||
entries <- c(
|
||||
Sys.glob(file.path(meta, "patched-*")),
|
||||
Sys.glob(file.path(meta, "pkgs-*.rds"))
|
||||
)
|
||||
if (length(entries) == 0L) {
|
||||
return(0L)
|
||||
}
|
||||
info <- file.info(entries)
|
||||
order_new_first <- order(info$mtime, decreasing = TRUE)
|
||||
ranked <- entries[order_new_first]
|
||||
ranked_mtime <- info$mtime[order_new_first]
|
||||
if (length(ranked) <= keep) {
|
||||
return(0L)
|
||||
}
|
||||
candidates <- ranked[(keep + 1L):length(ranked)]
|
||||
candidate_age <- as.numeric(Sys.time()) - as.numeric(ranked_mtime[(keep + 1L):length(ranked)])
|
||||
removable <- candidates[candidate_age >= min_age_secs]
|
||||
if (length(removable) == 0L) {
|
||||
return(0L)
|
||||
}
|
||||
unlink(removable, recursive = TRUE, force = TRUE)
|
||||
length(removable)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
source(file.path("..", "r-minor-helpers.R"))
|
||||
|
||||
# Build a fake _metadata dir under a temp R_PKG_CACHE_DIR. Each entry's mtime is
|
||||
# set to `age_secs` in the past so we can exercise the age gate deterministically.
|
||||
make_meta <- function(patched = 0L, pkgs = 0L, keep_repos = TRUE, age_secs = 3600) {
|
||||
root <- tempfile("pkgcache-")
|
||||
meta <- file.path(root, "R", "pkgcache", "_metadata")
|
||||
dir.create(meta, recursive = TRUE)
|
||||
old <- Sys.time() - age_secs
|
||||
mk_dir <- function(p) { dir.create(p); Sys.setFileTime(p, old); p }
|
||||
mk_file <- function(p) { writeLines("x", p); Sys.setFileTime(p, old); p }
|
||||
for (i in seq_len(patched)) mk_dir(file.path(meta, sprintf("patched-%03d", i)))
|
||||
for (i in seq_len(pkgs)) mk_file(file.path(meta, sprintf("pkgs-%03d.rds", i)))
|
||||
if (keep_repos) {
|
||||
mk_dir(file.path(meta, "CRAN-075c426938"))
|
||||
mk_dir(file.path(meta, "BioCsoft-1ac964ed6c"))
|
||||
mk_file(file.path(meta, "bioc-sysreqs.dcf.gz"))
|
||||
mk_dir(file.path(root, "R", "pkgcache", "pkg")) # downloads, must survive
|
||||
}
|
||||
root
|
||||
}
|
||||
|
||||
n_churn <- function(root) {
|
||||
meta <- file.path(root, "R", "pkgcache", "_metadata")
|
||||
length(Sys.glob(file.path(meta, "patched-*"))) +
|
||||
length(Sys.glob(file.path(meta, "pkgs-*.rds")))
|
||||
}
|
||||
|
||||
test_that("empty cache_dir is a no-op", {
|
||||
expect_identical(trim_pkgcache_metadata("", keep = 5L, min_age_secs = 0), 0L)
|
||||
})
|
||||
|
||||
test_that("missing _metadata dir is a no-op", {
|
||||
expect_identical(
|
||||
trim_pkgcache_metadata(tempfile("absent-"), keep = 5L, min_age_secs = 0),
|
||||
0L
|
||||
)
|
||||
})
|
||||
|
||||
test_that("fewer than keep entries removes nothing", {
|
||||
root <- make_meta(patched = 2L, pkgs = 2L)
|
||||
expect_identical(trim_pkgcache_metadata(root, keep = 20L, min_age_secs = 0), 0L)
|
||||
expect_identical(n_churn(root), 4L)
|
||||
})
|
||||
|
||||
test_that("trims down to keep newest, leaving churn == keep", {
|
||||
root <- make_meta(patched = 30L, pkgs = 30L) # 60 churn entries, all old
|
||||
removed <- trim_pkgcache_metadata(root, keep = 20L, min_age_secs = 0)
|
||||
expect_identical(removed, 40L)
|
||||
expect_identical(n_churn(root), 20L)
|
||||
})
|
||||
|
||||
test_that("entries younger than min_age_secs are protected", {
|
||||
root <- make_meta(patched = 30L, pkgs = 0L, keep_repos = FALSE, age_secs = 60)
|
||||
# keep=5 would drop 25, but all are 60s old < 600s gate -> nothing removed
|
||||
expect_identical(trim_pkgcache_metadata(root, keep = 5L, min_age_secs = 600), 0L)
|
||||
expect_identical(n_churn(root), 30L)
|
||||
})
|
||||
|
||||
test_that("stable repo dirs and pkg downloads are never touched", {
|
||||
root <- make_meta(patched = 30L, pkgs = 30L)
|
||||
trim_pkgcache_metadata(root, keep = 0L, min_age_secs = 0)
|
||||
meta <- file.path(root, "R", "pkgcache", "_metadata")
|
||||
expect_true(dir.exists(file.path(meta, "CRAN-075c426938")))
|
||||
expect_true(dir.exists(file.path(meta, "BioCsoft-1ac964ed6c")))
|
||||
expect_true(file.exists(file.path(meta, "bioc-sysreqs.dcf.gz")))
|
||||
expect_true(dir.exists(file.path(root, "R", "pkgcache", "pkg")))
|
||||
})
|
||||
Loading…
Reference in a new issue