Merge remote-tracking branch 'origin/main' into t3code/smarter-disk-pruning-macmini

# Conflicts:
#	.crow/build-all-versions-install-deps.yaml
#	.crow/build-all-versions.yaml
This commit is contained in:
Patrick Schratz 2026-07-13 09:30:21 +00:00
commit 6afa4e2405
No known key found for this signature in database
GPG key ID: 62050D5BC68AB6DC
25 changed files with 2119 additions and 69 deletions

View file

@ -62,9 +62,9 @@ steps:
GIT_USER: pat-s GIT_USER: pat-s
R_VERSION: 4.5.3 R_VERSION: 4.5.3
commands: commands:
- /opt/R/$R_VERSION/bin/R -q -e 'if (!requireNamespace("bincraft", quietly = TRUE) || packageVersion("bincraft") != "4.2.2") pak::pak("git::https://codefloe.com/rpkgs/bincraft.git@v4.2.2", dependencies = TRUE)' - /opt/R/$R_VERSION/bin/Rscript local/install-bincraft.R
- /opt/R/$R_VERSION/bin/R -q -e 'packageVersion("bincraft")' - /opt/R/$R_VERSION/bin/R -q -e 'packageVersion("bincraft")'
- /opt/R/$R_VERSION/bin/R -q -e 'bincraft::process_unarchived_pkgs(Sys.getenv("CODENAME"), Sys.getenv("ARCH"), workers = 2L)' - /opt/R/$R_VERSION/bin/R -q -e 'bincraft::process_unarchived_pkgs(Sys.getenv("CODENAME"), Sys.getenv("ARCH"), 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"), workers = 2L)'
backend_options: backend_options:
kubernetes: kubernetes:
resources: resources:

View file

@ -10,11 +10,22 @@ variables:
- arm64 - arm64
default: amd64 default: amd64
OS: OS:
description: 'Base OS image name (e.g. alpine, redhat, ubuntu).' description: "Base OS image name."
options:
- alpine
- redhat
- ubuntu
default: alpine default: alpine
OS_VERSION: OS_VERSION:
description: 'OS version / image tag (e.g. 3.24, 9, jammy, noble).' description: "OS image tag. Must match OS (alpine: 3.24; redhat: 8/9/10; ubuntu: jammy/noble)."
default: '3.24' options:
- "3.24"
- "8"
- "9"
- "10"
- "jammy"
- "noble"
default: "3.24"
R_VERSION: R_VERSION:
description: 'Primary R version under /opt/R.' description: 'Primary R version under /opt/R.'
options: options:
@ -42,6 +53,9 @@ steps:
image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}' image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}'
pull: true pull: true
environment: environment:
OTEL_R_TRACES_EXPORTER: none
OTEL_R_LOGS_EXPORTER: none
OTEL_R_METRICS_EXPORTER: none
REPO_RO_TOKEN: REPO_RO_TOKEN:
from_secret: REPO_RO_TOKEN from_secret: REPO_RO_TOKEN
GITHUB_PAT: GITHUB_PAT:
@ -66,8 +80,9 @@ steps:
- rm -rf /mnt/cache/pkgcache/R/pkgcache/_metadata/patched-* /mnt/cache/pkgcache/R/pkgcache/_metadata/pkgs-*.rds || true - 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 - 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 . - git clone -q https://pat-s:$$REPO_RO_TOKEN@git.devxy.io/devxy/build-cran-binaries.git .
- git clone -q https://codefloe.com/rpkgs/bincraft.git /tmp/bincraft # Pin the same bincraft version the build steps use, so the precomputed
- /opt/R/$R_VERSION/bin/R -q -e 'pak::sysreqs_db_update(); pak::local_install("/tmp/bincraft"); pak::pak(c("RPostgres", "s3fs", "data.table", "future", "jsonlite")); packageVersion("bincraft")' # snapshot and the per-agent library stay consistent across the pipeline.
- /opt/R/$R_VERSION/bin/R -q -e 'pak::sysreqs_db_update(); source("local/install-bincraft.R"); pak::pak(c("RPostgres", "s3fs", "data.table", "future", "jsonlite")); packageVersion("bincraft")'
- /opt/R/$R_VERSION/bin/R -q -e "source('local/packages-to-build.R'); saveRDS(pkgs, '/mnt/cache/packages/pkgs_to_build.rds'); saveRDS(pkgs[r_minor_sensitive == TRUE], '/mnt/cache/packages/r_minor_sensitive_pkgs.rds'); sprintf('Precomputed %s package versions (%s r-minor-sensitive)', nrow(pkgs), nrow(pkgs[r_minor_sensitive == TRUE]))" - /opt/R/$R_VERSION/bin/R -q -e "source('local/packages-to-build.R'); saveRDS(pkgs, '/mnt/cache/packages/pkgs_to_build.rds'); saveRDS(pkgs[r_minor_sensitive == TRUE], '/mnt/cache/packages/r_minor_sensitive_pkgs.rds'); sprintf('Precomputed %s package versions (%s r-minor-sensitive)', nrow(pkgs), nrow(pkgs[r_minor_sensitive == TRUE]))"
backend_options: backend_options:
docker: docker:

View file

@ -11,11 +11,25 @@ variables:
- arm64 - arm64
default: amd64 default: amd64
OS: OS:
description: 'Base OS image name (e.g. alpine, redhat, ubuntu).' description: "Base OS image name."
options:
- alpine
- redhat
- ubuntu
default: alpine default: alpine
OS_VERSION: OS_VERSION:
description: 'OS version / image tag (e.g. 3.24, 9, jammy, noble).' description: "OS image tag. Must match OS (alpine: 3.24; redhat: 8/9/10; ubuntu: jammy/noble)."
default: '3.24' options:
- "3.22"
- "3.23"
- "3.24"
- "8"
- "9"
- "10"
- "jammy"
- "noble"
- "resolute"
default: "3.24"
R_VERSION: R_VERSION:
description: 'Primary R version under /opt/R.' description: 'Primary R version under /opt/R.'
options: options:
@ -76,6 +90,9 @@ steps:
image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}' image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}'
pull: true pull: true
environment: environment:
OTEL_R_TRACES_EXPORTER: none
OTEL_R_LOGS_EXPORTER: none
OTEL_R_METRICS_EXPORTER: none
RED_HAT_DEV_PW: RED_HAT_DEV_PW:
from_secret: RED_HAT_DEV_PW from_secret: RED_HAT_DEV_PW
B2_S3_ACCESS_KEY: B2_S3_ACCESS_KEY:
@ -105,6 +122,14 @@ steps:
# growth is bounded separately by trim_pkgcache_metadata() in build-all.R. # 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 - 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 - 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
# volume is per-agent, so a job landing on an agent where install-deps did
# not run would otherwise use a stale bincraft (which resolves `platform`
# to a zero-length value and breaks every metadata query and the sysdeps
# install). Pin bincraft here, exactly like the R-minor pass below.
- rm -rf /mnt/cache/R-pkgs/00LOCK-*
- /opt/R/$R_VERSION/bin/Rscript local/install-bincraft.R
- XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run); XVFB_ARGS=""; if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi - XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run); XVFB_ARGS=""; if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi
- $XVFB $XVFB_ARGS -n $SPLIT_INDEX -- /opt/R/$R_VERSION/bin/Rscript local/build-all.R $SPLIT_INTO $SPLIT_INDEX $NCPUS 2>&1 - $XVFB $XVFB_ARGS -n $SPLIT_INDEX -- /opt/R/$R_VERSION/bin/Rscript local/build-all.R $SPLIT_INTO $SPLIT_INDEX $NCPUS 2>&1
- | - |
@ -116,11 +141,12 @@ steps:
echo "=== R-minor-sensitive pass under R $RV ===" echo "=== R-minor-sensitive pass under R $RV ==="
LIB="/mnt/cache/R-pkgs-$RMINOR" LIB="/mnt/cache/R-pkgs-$RMINOR"
mkdir -p "$LIB" mkdir -p "$LIB"
R_LIBS_USER="$LIB" "$(dirname "$RBIN")/R" -q -e 'if (!requireNamespace("bincraft", quietly = TRUE) || packageVersion("bincraft") != "4.2.2") pak::pak("git::https://codefloe.com/rpkgs/bincraft.git@v4.2.2")' || true R_LIBS_USER="$LIB" "$(dirname "$RBIN")/Rscript" local/install-bincraft.R || true
R_LIBS_USER="$LIB" $XVFB $XVFB_ARGS -n $SPLIT_INDEX -- "$(dirname "$RBIN")/Rscript" local/build-all.R --sensitive-only $SPLIT_INTO $SPLIT_INDEX $NCPUS 2>&1 || true R_LIBS_USER="$LIB" $XVFB $XVFB_ARGS -n $SPLIT_INDEX -- "$(dirname "$RBIN")/Rscript" local/build-all.R --sensitive-only $SPLIT_INTO $SPLIT_INDEX $NCPUS 2>&1 || true
done done
# archive missed packages # archive missed packages; first arg is the codename (e.g. "alpine324"),
- /opt/R/$R_VERSION/bin/R -q -e "bincraft::process_unarchived_pkgs(paste(Sys.getenv('OS'), Sys.getenv('OS_VERSION')), Sys.getenv('ARCH'), workers = $NCPUS)" # derived via bincraft like the upload step, not paste(OS, OS_VERSION).
- /opt/R/$R_VERSION/bin/R -q -e "bincraft::process_unarchived_pkgs(bincraft::set_codename(NULL), Sys.getenv('ARCH'), 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'), workers = $NCPUS)"
backend_options: backend_options:
docker: docker:
resources: resources:
@ -134,6 +160,9 @@ steps:
image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}' image: 'reg.devxy.io/rpkgs/build-env-${OS}:${OS_VERSION}'
pull: true pull: true
environment: environment:
OTEL_R_TRACES_EXPORTER: none
OTEL_R_LOGS_EXPORTER: none
OTEL_R_METRICS_EXPORTER: none
B2_S3_ACCESS_KEY: B2_S3_ACCESS_KEY:
from_secret: B2_S3_ACCESS_KEY from_secret: B2_S3_ACCESS_KEY
B2_S3_SECRET_KEY: B2_S3_SECRET_KEY:

View file

@ -15,6 +15,8 @@ variables:
- alpine-322-arm64 - alpine-322-arm64
- alpine-323-amd64 - alpine-323-amd64
- alpine-323-arm64 - alpine-323-arm64
- alpine-324-amd64
- alpine-324-arm64
- redhat-8-amd64 - redhat-8-amd64
- redhat-8-arm64 - redhat-8-arm64
- redhat-9-amd64 - redhat-9-amd64
@ -25,6 +27,8 @@ variables:
- ubuntu-2204-arm64 - ubuntu-2204-arm64
- ubuntu-2404-amd64 - ubuntu-2404-amd64
- ubuntu-2404-arm64 - ubuntu-2404-arm64
- ubuntu-2604-amd64
- ubuntu-2604-arm64
default: all default: all
when: when:
@ -64,6 +68,18 @@ matrix:
IMG: alpine:3.24 IMG: alpine:3.24
OS_ID: alpine323 OS_ID: alpine323
PROCESS_NEW: "FALSE" PROCESS_NEW: "FALSE"
- OS: alpine-324
ARCH: amd64
R_VERSION: 4.5.3
IMG: alpine:3.24
OS_ID: alpine324
PROCESS_NEW: "FALSE"
- OS: alpine-324
ARCH: arm64
R_VERSION: 4.5.3
IMG: alpine:3.24
OS_ID: alpine324
PROCESS_NEW: "FALSE"
- OS: redhat-8 - OS: redhat-8
ARCH: amd64 ARCH: amd64
R_VERSION: 4.4.3 R_VERSION: 4.4.3
@ -124,12 +140,27 @@ matrix:
IMG: ubuntu:noble IMG: ubuntu:noble
OS_ID: noble OS_ID: noble
PROCESS_NEW: "TRUE" PROCESS_NEW: "TRUE"
- OS: ubuntu-2604
ARCH: amd64
R_VERSION: 4.5.3
IMG: ubuntu:resolute
OS_ID: resolute
PROCESS_NEW: "TRUE"
- OS: ubuntu-2604
ARCH: arm64
R_VERSION: 4.5.3
IMG: ubuntu:resolute
OS_ID: resolute
PROCESS_NEW: "TRUE"
steps: steps:
- name: 'Processing Updates' - name: 'Processing Updates'
image: reg.devxy.io/rpkgs/build-env-${IMG} image: reg.devxy.io/rpkgs/build-env-${IMG}
pull: true pull: true
environment: environment:
OTEL_R_TRACES_EXPORTER: none
OTEL_R_LOGS_EXPORTER: none
OTEL_R_METRICS_EXPORTER: none
RED_HAT_DEV_PW: RED_HAT_DEV_PW:
from_secret: RED_HAT_DEV_PW from_secret: RED_HAT_DEV_PW
B2_S3_ACCESS_KEY: B2_S3_ACCESS_KEY:
@ -164,13 +195,13 @@ steps:
commands: commands:
- git clone -q https://pat-s:$$REPO_RO_TOKEN@git.devxy.io/devxy/build-cran-binaries.git . - git clone -q https://pat-s:$$REPO_RO_TOKEN@git.devxy.io/devxy/build-cran-binaries.git .
- rm -rf /mnt/cache/R-pkgs/00LOCK-* /mnt/cache/R-pkgs/bincraft /mnt/cache/R-pkgs/pkgcache /mnt/cache/pkgcache/R/pkgcache - rm -rf /mnt/cache/R-pkgs/00LOCK-* /mnt/cache/R-pkgs/bincraft /mnt/cache/R-pkgs/pkgcache /mnt/cache/pkgcache/R/pkgcache
- /opt/R/$R_VERSION/bin/R -q -e 'if (!requireNamespace("bincraft", quietly = TRUE) || packageVersion("bincraft") != "4.2.2") pak::pak("git::https://codefloe.com/rpkgs/bincraft.git@v4.2.2")' - /opt/R/$R_VERSION/bin/Rscript local/install-bincraft.R
- /opt/R/$R_VERSION/bin/R -q -e 'packageVersion("bincraft")' - /opt/R/$R_VERSION/bin/R -q -e 'packageVersion("bincraft")'
- mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages - mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages
# rhel-10 ships xwfb-run (Xwayland) instead of xvfb-run; prefer it and start weston, else fall back to xvfb-run # rhel-10 ships xwfb-run (Xwayland) instead of xvfb-run; prefer it and start weston, else fall back to xvfb-run
- XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run); XVFB_ARGS=""; if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi - XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run); XVFB_ARGS=""; if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi
# options(future.globals.onReference = NULL): for some reason s3fs::file_delete() throws 'Error: Detected a non-exportable reference ('externalptr') in one of the globals ('FUN' of class 'function') used in the future expression' otherwise # options(future.globals.onReference = NULL): for some reason s3fs::file_delete() throws 'Error: Detected a non-exportable reference ('externalptr') in one of the globals ('FUN' of class 'function') used in the future expression' otherwise
- $XVFB $XVFB_ARGS -- /opt/R/$R_VERSION/bin/R -q -e "options(crayon.enabled = TRUE, Ncpus = 4, future.globals.onReference = 'error', repos = structure(c(getOption('repos'),INLA='https://inla.r-inla-download.org/R/stable'))); progressr::handlers('cli'); progressr::handlers(global = TRUE); options(future.globals.onReference = NULL); bincraft::process_cran_updates(interval = $INTERVAL, platform = '${OS}', process_updated = TRUE, process_new = ${PROCESS_NEW}, process_removed = TRUE, r_minor_detection = 'classifier', 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'), 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, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)" - $XVFB $XVFB_ARGS -- /opt/R/$R_VERSION/bin/R -q -e "options(crayon.enabled = TRUE, Ncpus = 4, future.globals.onReference = 'error', repos = structure(c(getOption('repos'),INLA='https://inla.r-inla-download.org/R/stable'))); progressr::handlers('cli'); progressr::handlers(global = TRUE); options(future.globals.onReference = NULL); bincraft::process_cran_updates(interval = $INTERVAL, platform = '${OS}', process_updated = TRUE, process_new = ${PROCESS_NEW}, process_removed = TRUE, patches = 'local/patches', r_minor_detection = 'classifier',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'), 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, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)"
- | - |
PRIMARY_MINOR=$(echo "$R_VERSION" | cut -d. -f1-2) PRIMARY_MINOR=$(echo "$R_VERSION" | cut -d. -f1-2)
for RBIN in /opt/R/[0-9]*/bin/R; do for RBIN in /opt/R/[0-9]*/bin/R; do
@ -180,8 +211,8 @@ steps:
echo "=== R-minor-sensitive update pass under R $RV ===" echo "=== R-minor-sensitive update pass under R $RV ==="
LIB="/mnt/cache/R-pkgs-$RMINOR" LIB="/mnt/cache/R-pkgs-$RMINOR"
mkdir -p "$LIB" mkdir -p "$LIB"
R_LIBS_USER="$LIB" "$(dirname "$RBIN")/R" -q -e 'if (!requireNamespace("bincraft", quietly = TRUE) || packageVersion("bincraft") != "4.2.2") pak::pak("git::https://codefloe.com/rpkgs/bincraft.git@v4.2.2")' || true R_LIBS_USER="$LIB" "$(dirname "$RBIN")/Rscript" local/install-bincraft.R || true
R_LIBS_USER="$LIB" $XVFB $XVFB_ARGS -- "$(dirname "$RBIN")/R" -q -e "options(crayon.enabled = TRUE, Ncpus = 4, future.globals.onReference = NULL); bincraft::process_cran_updates(interval = $INTERVAL, platform = '${OS}', process_updated = TRUE, process_new = FALSE, process_removed = FALSE, r_minor_detection = 'classifier', r_minor_sensitive_only = TRUE, 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'), 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, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)" || true R_LIBS_USER="$LIB" $XVFB $XVFB_ARGS -- "$(dirname "$RBIN")/R" -q -e "options(crayon.enabled = TRUE, Ncpus = 4, future.globals.onReference = NULL); bincraft::process_cran_updates(interval = $INTERVAL, platform = '${OS}', process_updated = TRUE, process_new = FALSE, process_removed = FALSE, patches = 'local/patches', r_minor_detection = 'classifier',r_minor_sensitive_only = TRUE, 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'), 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, archive = TRUE, upload = TRUE, store_build_metadata = TRUE)" || true
done done
- /opt/R/$R_VERSION/bin/R -q -e 'library(bincraft); upload_package_index(codename = "${OS_ID}", 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"))' - /opt/R/$R_VERSION/bin/R -q -e 'library(bincraft); upload_package_index(codename = "${OS_ID}", 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"))'
- | - |
@ -202,6 +233,9 @@ steps:
- name: Purge CDN cache - name: Purge CDN cache
image: reg.devxy.io/docker.io/library/alpine:3.24 image: reg.devxy.io/docker.io/library/alpine:3.24
environment: environment:
OTEL_R_TRACES_EXPORTER: none
OTEL_R_LOGS_EXPORTER: none
OTEL_R_METRICS_EXPORTER: none
BUNNYNET_API_KEY: BUNNYNET_API_KEY:
from_secret: BUNNYNET_API_KEY from_secret: BUNNYNET_API_KEY
SUBDOMAIN1: 'cran.devxy.io' SUBDOMAIN1: 'cran.devxy.io'

View file

@ -103,6 +103,9 @@ steps:
image: reg.devxy.io/rpkgs/build-env-${IMG} image: reg.devxy.io/rpkgs/build-env-${IMG}
pull: true pull: true
environment: environment:
OTEL_R_TRACES_EXPORTER: none
OTEL_R_LOGS_EXPORTER: none
OTEL_R_METRICS_EXPORTER: none
RED_HAT_DEV_PW: RED_HAT_DEV_PW:
from_secret: RED_HAT_DEV_PW from_secret: RED_HAT_DEV_PW
B2_S3_ACCESS_KEY: B2_S3_ACCESS_KEY:
@ -129,12 +132,12 @@ steps:
- git clone -q https://pat-s:$$REPO_RO_TOKEN@git.devxy.io/devxy/build-cran-binaries.git . - git clone -q https://pat-s:$$REPO_RO_TOKEN@git.devxy.io/devxy/build-cran-binaries.git .
- mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages - mkdir -p /mnt/cache/pkgcache /mnt/cache/R-pkgs /mnt/cache/ccache /mnt/cache/packages
- rm -rf /mnt/cache/R-pkgs/00LOCK-* - rm -rf /mnt/cache/R-pkgs/00LOCK-*
- /opt/R/$R_VERSION/bin/R -q -e 'if (!requireNamespace("bincraft", quietly = TRUE) || packageVersion("bincraft") != "4.2.2") pak::pak("git::https://codefloe.com/rpkgs/bincraft.git@v4.2.2")' - /opt/R/$R_VERSION/bin/Rscript local/install-bincraft.R
- /opt/R/$R_VERSION/bin/R -q -e 'packageVersion("bincraft")' - /opt/R/$R_VERSION/bin/R -q -e 'packageVersion("bincraft")'
- XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run); XVFB_ARGS=""; if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi - XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run); XVFB_ARGS=""; if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi
- /opt/R/$R_VERSION/bin/R -q -e 'pak::pak("httr2")' - /opt/R/$R_VERSION/bin/R -q -e 'pak::pak("httr2")'
- /opt/R/$R_VERSION/bin/R -q -e 'source("local/fetch-rebuild-packages-from-issue.R")' - /opt/R/$R_VERSION/bin/R -q -e 'source("local/fetch-rebuild-packages-from-issue.R")'
- $XVFB $XVFB_ARGS -- /opt/R/$R_VERSION/bin/R -q -e "sink(stdout(), type = 'message'); options(crayon.enabled = TRUE, Ncpus = $NCPUS, future.globals.onReference = NULL); pkgs <- readLines('/tmp/rebuild_pkgs.txt'); if (length(pkgs) == 0) { cat('Nothing to rebuild\n'); q('no') }; excluded <- jsonlite::fromJSON('local/excluded-packages.json')[['package']]; pkgs <- setdiff(pkgs, excluded); cat(sprintf('Rebuilding %d packages\n', length(pkgs))); n <- length(pkgs); for (i in seq_along(pkgs)) { x <- pkgs[i]; cat(sprintf('[%d/%d] %s\n', i, n, x)); tryCatch(bincraft::build_binary_package(x, tag_limit = 1L, 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'), 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, archive = TRUE, upload = TRUE, store_build_metadata = TRUE), error = function(e) cat(sprintf('ERROR building %s - %s\n', x, conditionMessage(e)))) }" 2>&1 - $XVFB $XVFB_ARGS -- /opt/R/$R_VERSION/bin/R -q -e "sink(stdout(), type = 'message'); options(crayon.enabled = TRUE, Ncpus = $NCPUS, future.globals.onReference = NULL); pkgs <- readLines('/tmp/rebuild_pkgs.txt'); if (length(pkgs) == 0) { cat('Nothing to rebuild\n'); q('no') }; excluded <- jsonlite::fromJSON('local/excluded-packages.json')[['package']]; pkgs <- setdiff(pkgs, excluded); cat(sprintf('Rebuilding %d packages\n', length(pkgs))); n <- length(pkgs); for (i in seq_along(pkgs)) { x <- pkgs[i]; cat(sprintf('[%d/%d] %s\n', i, n, x)); tryCatch(bincraft::build_binary_package(x, tag_limit = 1L, patches = 'local/patches', 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'), 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, archive = TRUE, upload = TRUE, store_build_metadata = TRUE), error = function(e) cat(sprintf('ERROR building %s - %s\n', x, conditionMessage(e)))) }" 2>&1
backend_options: backend_options:
docker: docker:
resources: resources:

3
.gitignore vendored
View file

@ -99,3 +99,6 @@ docs/
local/test.R local/test.R
.DS_Store .DS_Store
docs/ docs/
# Superpowers SDD scratch (briefs, reports, ledger)
.superpowers/

View file

@ -2,3 +2,6 @@
ignores: ignores:
- LICENSE.md - LICENSE.md
- docs/superpowers/** - docs/superpowers/**
# Internal design docs (specs/plans) are not user-facing reference material.
- specs/**
- plans/**

View file

@ -16,19 +16,19 @@ repos:
args: args:
- --markdown-linebreak-ext=md - --markdown-linebreak-ext=md
- repo: https://github.com/DavidAnson/markdownlint-cli2 - repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.22.1 rev: v0.23.0
hooks: hooks:
- id: markdownlint-cli2 - id: markdownlint-cli2
- repo: https://github.com/rbubley/mirrors-prettier - repo: https://github.com/rbubley/mirrors-prettier
rev: v3.8.4 rev: v3.9.4
hooks: hooks:
- id: prettier - id: prettier
- repo: https://github.com/posit-dev/air-pre-commit - repo: https://github.com/posit-dev/air-pre-commit
rev: 0.9.0 rev: 0.10.0
hooks: hooks:
- id: air-format - id: air-format
- repo: https://github.com/editorconfig-checker/editorconfig-checker - repo: https://github.com/editorconfig-checker/editorconfig-checker
rev: v3.7.0 rev: v3.8.0
hooks: hooks:
- id: editorconfig-checker - id: editorconfig-checker
- repo: https://github.com/adrienverge/yamllint.git - repo: https://github.com/adrienverge/yamllint.git
@ -43,3 +43,9 @@ repos:
entry: YAML filenames must have .yaml extension. entry: YAML filenames must have .yaml extension.
language: fail language: fail
files: .yml$ files: .yml$
- id: validate-patches
name: validate patch registry
entry: Rscript local/validate-patches.R
language: system
files: ^local/(patches/|validate-patches\.R$)
pass_filenames: false

72
.terraform.lock.hcl generated
View file

@ -38,43 +38,43 @@ provider "registry.opentofu.org/hashicorp/http" {
} }
provider "registry.terraform.io/bunnyway/bunnynet" { provider "registry.terraform.io/bunnyway/bunnynet" {
version = "0.14.3" version = "0.15.1"
constraints = "~> 0.14" constraints = "~> 0.15"
hashes = [ hashes = [
"h1:2VmbbvV/3fVb/dHZ5m3CxSPoptpn/PuhvSgV/RA9Ae8=", "h1:/2NUpbtjkc+w6n5V3kGP0rSzGjN0K2Wdfe2K+CZdmhU=",
"h1:4MANuttWPyJGZlfbx5P4i2Jpbmvda1tfWgd3olK2nhY=", "h1:1TOrpmCR0aT5xX1sjt70zqlYkMJnVe6r0nx2B0DS/mE=",
"h1:AsSfDvm6flYQgjFnFVaRPv7v1/P5pj36dQ4N103Nw+A=", "h1:4uC8r8ILr+vZJ230uGqIUaWQ7uORCrIzoEuYrAq2JuE=",
"h1:D2AK5psWQmZqrOHsbeV70OP/zcG92i++AmGKBz/HVMA=", "h1:6b+2osJUfwcaYZ6mathLPf/58sr/4XkLXQrcSufnkMk=",
"h1:DpJWi/bhHoZa/ccy2YB/c7HJjTlR6CuZBxGFCxU2uJk=", "h1:CPldfjf79QS8mIP+GWoS0FVhrFlyjvN2+39zfyP76Ik=",
"h1:Iz47FCzHBOZC1RrTxKwJwKsH6bdEvdYtO0+RBpbmz8I=", "h1:EvXICHyGIKpoYlDeKHOPzfmpvdRdhgsOMcR2nb6+tKY=",
"h1:RJQm4qEfljAmcJaFf45uXwUhtbRTwfDuiUWZ6Q7X+Cw=", "h1:GZLq+nDxS1CyBH0ELGTSQj9X7ozemJ1jpPA4KwbtR+c=",
"h1:YQBi0MRW+dOfkxQYbjKkzDm7UIcc2FeFm8Cck+W3kgs=", "h1:ISNFqL745IQgZ6yMLy8ofV8ixbYqZYa9JKdi2W3pmNk=",
"h1:bWq/zJ2Chr5TNuz9y3eaGmzrmXsX30pl5HkGpxE1kVM=", "h1:IrNrEuvFd0nYDGQefwmT8d1CSJb9e8LN5w9vw1ODp7E=",
"h1:hwzEskDnFHaJFvKNAGv/wIxRT2SHcXLfwmBb8XNspq4=", "h1:Ms79slY9bZ94+n4cwIHvI9+/cvbucwo6S1+z5KAiznw=",
"h1:jGggry0lZmG0S0inM7Odae3275na9Gp3O97ZlvBiA2s=", "h1:NWA9XSEBcpSkgwwIvl6tHrxGQY3uYhqNS4Vnb9RLyLQ=",
"h1:mJdpVc9qOpqXcPpptWfAET8qloo6qFMb+NzHesazybk=", "h1:UjvxxxggicLtiE3yTe1Gx0oLUTeZpWmgIfXuHzWHn1c=",
"h1:nbpkaopSGlmxkKLtNe9sKx9IqojoicD1+hNRdaYd5ok=", "h1:VgJjo14DGkU4Jwo4D3GT4/5sq1tdjiZscKS5l3cb890=",
"h1:pwuoUqikuVOut3RtPTnu6pNoDnyeXlEX7Rfb2Hr1Ch0=", "h1:dBu3AW5YNLIvbBIMNk3wUHKw4TW+BDbj34a+mCqYhWE=",
"h1:u0naFiWGrBVhI3AsUb/ZfS5N/GeiRk0ZkxjHtA10lF0=", "h1:i5oGD06nQ3JRsBIa2u4wCej+ETgp970CFl75dOKkHno=",
"h1:uozoSPzZ7MJIHj5O62W8n86NIfH4JtF7GvMNI6rKlrM=", "h1:mTqR+vD1AWPx+mu7S0/pzBy71z7WOrjH7arOP77PXh4=",
"h1:vDS308OeXVVgjojMCAAEQ1VshmOLjwra7obliKB5fv8=", "h1:nmTM61G8vYjpofeEqspMORpsNvTGCNZySGfjdXardL0=",
"zh:23200ad919df4c44a720c667c536d3a0ae0c235e1f7b53b9e56f88005bf01891", "zh:0f9bf5aaa47164a4d6ae4433d5e285a9456a5053401b2bad4ed68622f574ddee",
"zh:2fe5445f578c6a244995538fde6dd7ef0dd5f3969686cba3d91fcf28ea31278d", "zh:3039bee421fb8855a919f449fc731d145254371f5f0c39cc4660f3aeed6a8b10",
"zh:3a5a8139ed177ccbee74feb4d19c881e18723e4bf3a64b6d35931420402a2a1a", "zh:36664b08186e0c194747b18dee24ed97327c6e704133d4cf0df27abef1652f86",
"zh:5189bd6c03ec3c428185992f519f3bf520d4e9eba8771c240814adbecdf60140", "zh:3c7eae99d8c5ff65dfb99c8b9c1980147282d68971e1ef1ff0f126a6bff59d8f",
"zh:532ac8eb94a84ccf046d4cb4eded825269effdbb7597f392f848fbfad5dcae1d", "zh:5293cc21abf54f4e5745437ca2d40d206aae323b2e1d41cf45dcc63a8868cbb3",
"zh:5f100279d73ce998c7bf4ffa2bbcd617ee307bf51c4a94146f210aeec268c4d0", "zh:5994e5145e616e7e881010717e4c7def2945eb6d933f62db4ec3167732ccac84",
"zh:80cb58abd2e431386ab1b6ad186a6e6e5347448993bacc831c0251b9016445b9", "zh:7994db9ed3fdb6cbf21f2154ea962cf82f04e425988bedc6657de2497d6cb6c3",
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
"zh:9ac734c6aae19ea0832269f1f1dc3c3e100855c09b6168eecd8693958515b228", "zh:93aa863e536ba9376ccf9e614e9edc9b214a2ce8c4316d416a8e249b436f52d2",
"zh:a0e3b75176c71e5f41a6552cef57ad67d11778cb59fd2eb792e69f662b67ae01", "zh:ab5cb4baeda57559686a0ccf0e09158aa64624ee6ba0ef32b769f13b11a43068",
"zh:b4d0e1dd9a80d1ac9a3f1beaefb58d4b944cb0a30bdc672b9037a69b4dc20ec3", "zh:ae9388b62eede8fd9272407bf75f8241a965bd489d45ec9dd3f9fac696d500e1",
"zh:b8309337e966528991cc2dd61ff0247864e4c3c7bef7dcf225f066edb6171050", "zh:caa5befd16960e2f69c7ec483e228e5ff43ab0979c17f1b874c9ffaa1c7c0e43",
"zh:c34ce5fcdd481214697505bceb616a7dfcda7702f1afefa7dc5459965b8e01ca", "zh:cddd3e1067defa06a4e4ad5cb3940c7943e29c42417de57236aa7d3e2aeaae13",
"zh:cb3f662ad06bb86aa763a5644ba318bc3db0ed30b04eb810c478e81f4b012d55", "zh:cdfa44d591d0805116159556947904d70f534c6816188c45cf3a7544d2722ac9",
"zh:cf0f0a459e2b6b1e014af9f53f32b5f555a249b61a0dcaccd3e4fe89178d8aa5", "zh:d607e9f1f3e09f13404f219e1893e3b3c77aece4afb54e999f934c021f41f576",
"zh:d4dfb653a0295bfbbebcb4bb022b490f2c24ca31287ebf77fe9dcaee2e5ea658", "zh:d8a397aca95125c6a0c0c78d2ded5843b9204effa9f5cf7419f017b500ad9228",
"zh:e49ba31aa092a30ea8a1091f7b26dbe41c3998f924f5b0d82810f27fc2f63501", "zh:f5499eaff0d221725ad209d27d87c5b46d5c554caad7dc42947c760377abe3b0",
"zh:ea4d7d5688caf2f9cda3a1721779b61a051db6b43ca7267ae2712bd1262cfcfd", "zh:fc5f5cf433abc83e5169fa222992ec521c0c802075970251e3dd2c1d50c4f5c1",
] ]
} }

14
CLAUDE.md Normal file
View file

@ -0,0 +1,14 @@
# CLAUDE.md
Crow CI pipelines (`.crow/`) and local tooling (`local/`) that build CRAN binary packages (incl. Alpine/musl) and upload them to Backblaze B2.
## Conventions
- **PRs:** the remote is Forgejo on `codefloe.com`; use `fj -H codefloe.com` (not `gh`).
- **Storage:** Backblaze B2 bucket `devxy-r-builds` (endpoints configured in the `.crow/` pipelines).
## Gotchas
- **B2 requires authentication for the list-bucket API.** Anonymous GET only works for individual public-read objects — an empty listing means missing credentials, not an empty bucket.
- Weekly-rebuild pipelines run for hours; watch them as background tasks and fetch Crow logs yourself instead of having the user paste progress.
- musl builds of packages with bundled native deps (e.g. RcppParallel/TBB) recur as failures; check for an existing patch before re-deriving a fix.

View file

@ -53,6 +53,28 @@ For every package+tag combination:
1. Archive old package versions and keep the latest one in the root 1. Archive old package versions and keep the latest one in the root
1. Delete local binaries after successful upload 1. Delete local binaries after successful upload
## Patching packages
Some CRAN packages fail to compile on specific platforms due to compiler- or OS-specific issues unrelated to the package itself.
The canonical example is `RcppParallel`, whose bundled TBB sources fail on musl (Alpine) and newer compiler/OS combinations.
Because such packages are often transitive dependencies of many others, a single failure cascades: all dependents fail even though nothing is wrong with the dependent itself.
To address this, frequently-failing packages can be "patched" before they are installed — whether as a direct build target or a transitive dependency pulled in by `pak`.
The patch registry lives in `local/patches/registry.json`.
Each entry specifies a package and the platforms/versions it applies to, along with either lightweight build-time overrides (environment variables, configure arguments, Makevars) or a source diff (for deeper fixes).
See `local/patches/README.md` for the complete schema.
Patching uses a two-tier approach:
1. **Lightweight overrides:** environment variables, configure arguments, or Makevars settings applied during build — typically version-independent and fast.
2. **Source diffs:** unified diff patches applied to the unpacked source before building — more powerful but version-pinned.
The system is implemented in `bincraft`: when a package needs patching, `bincraft` pre-builds it with the patch and serves the patched binary to `pak`, ensuring transitive dependents receive the fixed package.
This way, the fix cascades to all packages that depend on it.
For the design rationale and architecture, see `specs/2026-06-30-package-patching-design.md`.
## Build Environment ## Build Environment
Binaries are built on a mixed-architecture Kubernetes cluster using CI. Binaries are built on a mixed-architecture Kubernetes cluster using CI.
@ -199,7 +221,7 @@ Tag does not have a NAMESPACE file and hence cannot be built.
Dependency not available: Either because the dependency was not declared or errored itself during installation. Dependency not available: Either because the dependency was not declared or errored itself during installation.
```text ```text
In function '\033[01m\033[KRcpp::List solveRRBLUP(const mat&, const mat&, const mat&)\033[m\033[K':\n\033[01m\033[KMME.cpp:162:61:\033[m\033[K \033[01;31m\033[Kerror: \033[m\033[K'\033[01m\033[KPI\033[m\033[K' was not declared in this scope\n 162 | double ll = -0.5*(double(optRes[\"objective\"])+df+df*log(2*\033[01;31m\033[KPI\033[m\033[K/df));\n | \033[01;31m\033[K^~\033[m\033[K\n\033[01m\033[KMME.cpp:\033[m\033[K In function '\033[01m\033[KRcpp::List solveRRBLUPMV(const mat&, const mat&, const mat&, int, double)\033[m\033[K':\n\033[01m\033[KMME.cpp:277:31:\033[m\033[K \033[01;31m\033[Kerror: \033[m\033[K'\033[01m\033[KPI\033[m\033[K' was not declared in this scope; did you mean '\033[01m\033[KHI\033[m\033[K'?\n 277 | ll -= double(n*m)/2.0*log(2*\033[01;31m\033[KPI\033[m\033[K);\n | \033[01;31m\033[K^~\033[m\033[K\n | \033[32m\033[KHI\033[m\033[K\nmake: *** [/opt/R/4.4.1/lib/R/etc/Makeconf:204: MME.o] Error 1\nERROR: compilation failed for package 'AlphaSimR'\n* removing '/tmp/RtmpclI5CE/temp_libpath11135d215d5/AlphaSimR'\n In function '\033[01m\033[KRcpp::List solveRRBLUP(const mat&, const mat&, const mat&)\033[m\033[K':\n\033[01m\033[KMME.cpp:162:61:\033[m\033[K \033[01;31m\033[Kerror: \033[m\033[K'\033[01m\033[KPI\033[m\033[K' was not declared in this scope\n 162 | double ll = -0.5*(double(optRes[\"objective\"])+df+df*log(2*\033[01;31m\033[KPI\033[m\033[K/df));\n | \033[01;31m\033[K^~\033[m\033[K\n\033[01m\033[KMME.cpp:\033[m\033[K In function '\033[01m\033[KRcpp::List solveRRBLUPMV(const mat&, const mat&, const mat&, int, double)\033[m\033[K':\n\033[01m\033[KMME.cpp:277:31:\033[m\033[K \033[01;31m\033[Kerror: \033[m\033[K'\033[01m\033[KPI\033[m\033[K' was not declared in this scope; did you mean '\033[01m\033[KHI\033[m\033[K'?\n 277 | ll -= double(n*m)/2.0*log(2*\033[01;31m\033[KPI\033[m\033[K);\n | \033[01;31m\033[K^~\033[m\033[K\n | \033[32m\033[KHI\033[m\033[K\nmake: *** [/opt/R/4.4.2/lib/R/etc/Makeconf:204: MME.o] Error 1\nERROR: compilation failed for package 'AlphaSimR'\n* removing '/tmp/RtmpclI5CE/temp_libpath11135d215d5/AlphaSimR'\n
``` ```
Compiler error: Possible reasons: too old CXX code which cannot be compiled anymore with CXX14 or CXX17. Compiler error: Possible reasons: too old CXX code which cannot be compiled anymore with CXX14 or CXX17.

View file

@ -16,6 +16,11 @@ ARG CACHEBUST
WORKDIR /work WORKDIR /work
COPY build-one.R /work/build-one.R COPY build-one.R /work/build-one.R
# Resolve and install the latest bincraft release dynamically (no hardcoded pin).
COPY install-bincraft.R /work/install-bincraft.R
# Ship the patch registry so build-one.R's `patches = "local/patches"` resolves
# (build context is `local/`, CWD is /work).
COPY patches /work/local/patches
RUN --mount=type=secret,id=b2_access,required=true \ RUN --mount=type=secret,id=b2_access,required=true \
--mount=type=secret,id=b2_secret,required=true \ --mount=type=secret,id=b2_secret,required=true \
@ -27,6 +32,9 @@ RUN --mount=type=secret,id=b2_access,required=true \
export GITHUB_PAT="$(cat /run/secrets/github_pat 2>/dev/null || true)" && \ export GITHUB_PAT="$(cat /run/secrets/github_pat 2>/dev/null || true)" && \
export GIT_TERMINAL_PROMPT=0 && \ export GIT_TERMINAL_PROMPT=0 && \
export OTEL_SDK_DISABLED=true && \ export OTEL_SDK_DISABLED=true && \
export OTEL_R_TRACES_EXPORTER=none && \
export OTEL_R_LOGS_EXPORTER=none && \
export OTEL_R_METRICS_EXPORTER=none && \
XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run || true); \ XVFB=$(command -v xwfb-run 2>/dev/null || command -v xvfb-run || true); \
XVFB_ARGS=""; \ XVFB_ARGS=""; \
if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi; \ if command -v xwfb-run >/dev/null 2>&1; then dnf install -y -q weston 2>/dev/null; XVFB_ARGS="-c weston"; fi; \
@ -37,7 +45,7 @@ RUN --mount=type=secret,id=b2_access,required=true \
echo "No working virtual display; building without xvfb" >&2; \ echo "No working virtual display; building without xvfb" >&2; \
fi; \ fi; \
run_build() { if [ "$USE_XVFB" = 1 ]; then $XVFB -a $XVFB_ARGS -- "$@"; else "$@"; fi; }; \ run_build() { if [ "$USE_XVFB" = 1 ]; then $XVFB -a $XVFB_ARGS -- "$@"; else "$@"; fi; }; \
ensure_bincraft() { "$1" -q -e 'if (!requireNamespace("bincraft", quietly = TRUE) || packageVersion("bincraft") != "4.2.1") pak::pak("git::https://codefloe.com/rpkgs/bincraft.git@v4.2.1")'; }; \ ensure_bincraft() { "$1" -q -e 'source("/work/install-bincraft.R")'; }; \
PRIMARY_MINOR=$(echo "$R_VERSION" | cut -d. -f1-2); \ PRIMARY_MINOR=$(echo "$R_VERSION" | cut -d. -f1-2); \
seen=" $PRIMARY_MINOR "; \ seen=" $PRIMARY_MINOR "; \
prc=0; failed=""; \ prc=0; failed=""; \

41
docker/buildkitd.toml Normal file
View file

@ -0,0 +1,41 @@
# 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"

View file

@ -10,8 +10,10 @@
# - buildx builders named `artemis` (amd64) and `gaia` (arm64), created with the # - buildx builders named `artemis` (amd64) and `gaia` (arm64), created with the
# docker-container driver (runs BuildKit on the remote host's docker daemon over # 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. # SSH). The default `remote` driver does NOT work with an ssh:// docker host.
# docker buildx create --name artemis --driver docker-container ssh://<user@host-amd64> # Pass --config docker/buildkitd.toml so BuildKit's GC keeps a usable cache
# docker buildx create --name gaia --driver docker-container ssh://<user@host-arm64> # (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>
# - exported secrets: B2_S3_ACCESS_KEY, B2_S3_SECRET_KEY, PGPASS (GITHUB_PAT optional) # - exported secrets: B2_S3_ACCESS_KEY, B2_S3_SECRET_KEY, PGPASS (GITHUB_PAT optional)
# #
# Overridable (env or `just VAR=… rebuild …`): # Overridable (env or `just VAR=… rebuild …`):

View file

@ -64,12 +64,38 @@ sprintf("# of package versions for this job: %s", nrow(chunk))
exclude <- jsonlite::fromJSON("local/excluded-packages.json")[["package"]] exclude <- jsonlite::fromJSON("local/excluded-packages.json")[["package"]]
chunk <- chunk[!chunk$Package %in% exclude, ] chunk <- chunk[!chunk$Package %in% exclude, ]
# Skip package versions already built in a previous run. # Skip package versions already attempted in a previous run (built or errored).
# pkgs_to_build.rds is a static snapshot from the install-deps step, so on a # 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 # restart it still lists everything an interrupted run already produced. The
# metadata DB reflects that progress, so we re-derive the remaining set here. # metadata DB reflects that progress, so we re-derive the remaining set here.
platform <- paste(Sys.getenv("OS"), gsub("[.]", "", Sys.getenv("OS_VERSION")), sep = "-") # We exclude *all* attempted versions, not just successful ones: a previously
arch <- Sys.getenv("ARCH") # 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
# environment, so Sys.getenv() would return "" and this pre-filter would query
# platform "-" and skip nothing.
codename <- bincraft::set_codename(NULL)
platform <- switch(
codename,
jammy = "ubuntu-2204",
noble = "ubuntu-2404",
resolute = "ubuntu-2604",
rhel10 = "redhat-10",
rhel9 = "redhat-9",
rhel8 = "redhat-8",
alpine320 = "alpine-320",
alpine321 = "alpine-321",
alpine322 = "alpine-322",
alpine323 = "alpine-323",
alpine324 = "alpine-324",
alpine325 = "alpine-325",
alpine326 = "alpine-326",
NA_character_
)
local_machine <- Sys.info()[["machine"]]
arch <- if (grepl("arm64|aarch64", local_machine)) "arm64" else "amd64"
con <- DBI::dbConnect( con <- DBI::dbConnect(
RPostgres::Postgres(), RPostgres::Postgres(),
dbname = "build_metadata", dbname = "build_metadata",
@ -81,13 +107,13 @@ con <- DBI::dbConnect(
) )
built <- DBI::dbGetQuery( built <- DBI::dbGetQuery(
con, con,
"SELECT name, tag FROM single_builds WHERE platform = $1 AND arch = $2 AND error_occurred = FALSE", "SELECT name, tag FROM single_builds WHERE platform = $1 AND arch = $2",
params = list(platform, arch) params = list(platform, arch)
) )
DBI::dbDisconnect(con) DBI::dbDisconnect(con)
before <- nrow(chunk) before <- nrow(chunk)
chunk <- chunk[!paste(chunk$Package, chunk$Version) %in% paste(built$name, built$tag), ] chunk <- chunk[!paste(chunk$Package, chunk$Version) %in% paste(built$name, built$tag), ]
sprintf("Skipped %d already-built package versions; %d remaining for this job", before - nrow(chunk), nrow(chunk)) sprintf("Skipped %d already-attempted package versions; %d remaining for this job", before - nrow(chunk), nrow(chunk))
# Read pre-computed S3 listing from install-deps step # Read pre-computed S3 listing from install-deps step
# This avoids loading s3fs/reticulate/Python in the build container, # This avoids loading s3fs/reticulate/Python in the build container,
@ -121,6 +147,7 @@ mapply(
metadata_db_sslmode = "require", metadata_db_sslmode = "require",
metadata_db_port = 15432, metadata_db_port = 15432,
archive = TRUE, archive = TRUE,
patches = "local/patches",
upload = TRUE, upload = TRUE,
store_build_metadata = TRUE store_build_metadata = TRUE
) )

View file

@ -103,6 +103,7 @@ for (ver in versions) {
force = TRUE, force = TRUE,
upload = TRUE, upload = TRUE,
archive = TRUE, archive = TRUE,
patches = "local/patches",
store_build_metadata = TRUE, store_build_metadata = TRUE,
s3_endpoint = s3$s3_endpoint, s3_endpoint = s3$s3_endpoint,
s3_region = s3$s3_region, s3_region = s3$s3_region,

52
local/install-bincraft.R Normal file
View file

@ -0,0 +1,52 @@
#!/usr/bin/env Rscript
# Install the latest tagged bincraft release, resolved dynamically, so the CI
# workflows and the build-one image never pin a hardcoded version (no more
# editing `@vX.Y.Z` in many places on every release).
#
# Run with the R whose library should receive bincraft:
# Rscript local/install-bincraft.R
# or, to target a specific R from a shell loop:
# "$RBIN" -q -e 'source("local/install-bincraft.R")'
#
# How it works: list the remote tags with `git ls-remote` (no token needed for
# the public repo), keep the `vX.Y.Z` release tags, pick the highest version,
# and install it with pak. pak is idempotent on the git ref, so re-running keeps
# the package when it is already current and only updates when a newer tag ships.
# Filtering/sorting is done in R (not via git's `--sort`/refspec) so behaviour is
# identical across git versions and `system2()` argument handling.
repo_url <- Sys.getenv(
"BINCRAFT_GIT_URL",
unset = "https://codefloe.com/rpkgs/bincraft.git"
)
# GIT_TERMINAL_PROMPT=0 keeps a non-interactive run from hanging on auth.
refs <- system2(
"git",
c("ls-remote", "--tags", repo_url),
stdout = TRUE,
stderr = FALSE,
env = "GIT_TERMINAL_PROMPT=0"
)
tags <- sub(".*refs/tags/", "", refs)
tags <- tags[!grepl("\\^\\{\\}$", tags)] # drop dereferenced "...^{}" lines
tags <- grep("^v[0-9]", tags, value = TRUE) # only vX.Y.Z release tags
if (length(tags) == 0L) {
stop(
"Could not resolve any bincraft release tag from ",
repo_url,
call. = FALSE
)
}
latest <- tags[order(package_version(sub("^v", "", tags)), decreasing = TRUE)][
1L
]
message(sprintf("Installing latest bincraft release: %s", latest))
pak::pak(sprintf("git::%s@%s", repo_url, latest))
message(sprintf(
"bincraft %s installed (%s)",
as.character(utils::packageVersion("bincraft")),
latest
))

43
local/patches/README.md Normal file
View file

@ -0,0 +1,43 @@
# Patch Registry
This directory contains the curated registry of per-package build-time patches consumed by bincraft's `patches` argument.
## Schema
The registry is defined in `registry.json` as an array of patch entries. Each entry specifies lightweight build-time overrides (environment variables, configure arguments, Makevars) and optionally a source diff to apply before building.
### Field semantics
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `package` | string | yes | CRAN package name. |
| `versions` | string | yes | `"*"` for any, a constraint such as `">=5.1.0"`, or an exact version `"5.1.11-2"`. Env-tier fixes are typically `"*"`; source diffs are normally exact or lower-bounded because a diff is pinned to the source it was generated against. |
| `platforms` | array of strings | yes | Matched against the running build's platform tokens — distro family (`alpine`, `ubuntu`, `redhat`), codename (`ubuntu-2604`, `alpine-324`), and arch (`amd64`, `arm64`). An entry matches if any listed token matches any build token. `["*"]` matches all platforms. |
| `env` | object | no | Environment variables exported only for this package's isolated build. |
| `configure_args` | array | no | Arguments passed as `--configure-args` to the isolated build. |
| `makevars` | object | no | Key/value pairs written into a package-local Makevars for the isolated build. |
| `patch` | string or null | no | Path (relative to `local/patches/`) to a unified diff applied to the unpacked CRAN source before building. |
| `reason` | string | yes | Human explanation, surfaced in logs and metadata. |
## Adding an entry
To add a new patch entry:
1. Add an object to the array in `registry.json` with the fields documented above.
Start with lightweight overrides (environment variables, configure arguments, Makevars) before resorting to source diffs.
2. If a source diff is needed, place it in `local/patches/<package>/<file>.patch` and reference its path in the `patch` field.
For example, a diff for `RcppParallel` would go in `local/patches/RcppParallel/fix.patch` and be referenced as `"patch": "RcppParallel/fix.patch"`.
3. The `reason` field should clearly explain why the patch is needed and what problem it solves.
## Validation
The registry is validated and applied by bincraft during the build process.
For manual validation, run the validator from the repo root:
```bash
Rscript local/validate-patches.R
```
This validates the schema, referenced patch-file existence, and checks for duplicate entries across platforms and versions.

View file

@ -0,0 +1,16 @@
diff --git a/src/Makevars.in b/src/Makevars.in
index be8445f..faee771 100644
--- a/src/Makevars.in
+++ b/src/Makevars.in
@@ -60,7 +60,10 @@ else
endif
ifeq ($(UNAME), Linux)
- USE_TBB=Linux
+ # bincraft patch: the bundled Intel TBB build hangs/fails on musl (Alpine)
+ # and newer toolchains (g++ 15). Skip it (leave USE_TBB unset) and force the
+ # TinyThread backend so RcppParallel still builds.
+ PKG_CXXFLAGS += -DRCPP_PARALLEL_USE_TBB=0
endif
ifeq ($(UNAME), SunOS)

View file

@ -0,0 +1,19 @@
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}`

Some files were not shown because too many files have changed in this diff Show more