All checks were successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/54 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/52 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/53 Pipeline was successful
ci/crow/manual/weekly-rebuild-reindex/18 Pipeline was successful
## Motivation Weekly rebuild shards can all hit a transient CRAN DNS/index outage at once, and the dependent CDN purge always fails because it tries to clone over the checkout preserved from the re-index step. ## Changes - Retry `uvr add` resolution up to four times with bounded backoff. - Reuse the existing Crow workspace checkout in the CDN purge step. - Remove the purge step's unused Git package and repository token. ## Validation - `crow lint .crow/` - `shellcheck local/uvr-install.sh scripts/purge_cdn_zone.sh` - `git diff --check` Reviewed-on: #164
120 lines
5 KiB
Shell
Executable file
120 lines
5 KiB
Shell
Executable file
#!/bin/sh
|
|
# Install R packages into the CI library with uvr (https://github.com/nbafrank/uvr).
|
|
#
|
|
# Usage:
|
|
# local/uvr-install.sh httr2 jsonlite
|
|
# local/uvr-install.sh forgejo::codefloe.com/rpkgs/bincraft@v4.4.3
|
|
#
|
|
# Replaces `pak::pak(...)`. uvr is project-scoped: `uvr add` refuses to run
|
|
# outside a project and always writes to `.uvr/library/`, and only
|
|
# `uvr sync --library` can target an existing library. The project is therefore
|
|
# minted in a scratch directory under TMPDIR and thrown away afterwards; that
|
|
# also keeps `uvr init`'s `.Rprofile` out of the repo checkout, where it would
|
|
# hijack `.libPaths()` for every other R call in the pipeline.
|
|
#
|
|
# Pruning is a no-op here: uvr disables it whenever `--library` is passed,
|
|
# precisely because such a target may be shared (`/mnt/cache/R-pkgs` holds
|
|
# bincraft and its dependencies alongside whatever this script installs).
|
|
#
|
|
# System dependencies come from uvr's vendored r-system-requirements rules, so
|
|
# `pak::sysreqs_db_update()` and `PKG_SYSREQS_PLATFORM` are no longer needed.
|
|
#
|
|
# Environment:
|
|
# UVR_R_BIN R interpreter to install for; set by install-bincraft.R so
|
|
# the per-R-minor passes target their own R, not the primary
|
|
# R_VERSION fallback interpreter selector (/opt/R/<version>/bin/R)
|
|
# UVR_TARGET_LIB target library; defaults to R_LIBS_USER, then to the
|
|
# active R's .libPaths()[1] (which is where pak wrote)
|
|
# UVR_INSTALL_DIR where the uvr binary lands (default /usr/local/bin)
|
|
|
|
set -eu
|
|
|
|
# renovate: datasource=github-releases depName=nbafrank/uvr
|
|
UVR_PIN="v0.4.6"
|
|
|
|
if [ "$#" -eq 0 ]; then
|
|
echo "usage: $0 <pkg-spec>..." >&2
|
|
exit 2
|
|
fi
|
|
|
|
# The build images keep R under /opt/R/<version> and off PATH. uvr resolves the
|
|
# interpreter via PATH and never downloads one unless `uvr r install` is run, so
|
|
# put the requested R first.
|
|
r_bin="${UVR_R_BIN:-}"
|
|
if [ -z "$r_bin" ] && [ -n "${R_VERSION:-}" ] && [ -x "/opt/R/${R_VERSION}/bin/R" ]; then
|
|
r_bin="/opt/R/${R_VERSION}/bin/R"
|
|
fi
|
|
if [ -n "$r_bin" ]; then
|
|
PATH="$(dirname "$r_bin"):$PATH"
|
|
export PATH
|
|
else
|
|
r_bin="$(command -v R)"
|
|
fi
|
|
|
|
target_lib="${UVR_TARGET_LIB:-${R_LIBS_USER:-}}"
|
|
if [ -z "$target_lib" ]; then
|
|
target_lib="$("$r_bin" --no-echo --no-save -e 'cat(.libPaths()[1])')"
|
|
fi
|
|
if [ -z "$target_lib" ]; then
|
|
echo "error: could not determine a target library; set UVR_TARGET_LIB" >&2
|
|
exit 2
|
|
fi
|
|
mkdir -p "$target_lib"
|
|
|
|
# Pin the manifest to the active R so the lockfile's R stays in step with the
|
|
# library's R sentinel. Without that, uvr can decide the library is ABI-stale
|
|
# and wipe it -- and this target is shared with bincraft. uvr only discovers R
|
|
# via PATH/R_HOME (it does not scan /opt/R), so the R put on PATH above is the
|
|
# only candidate this constraint can resolve to.
|
|
# shellcheck disable=SC2016 # $major/$minor are R expressions, not shell vars
|
|
r_full="$("$r_bin" --no-echo --no-save -e 'cat(paste(R.version$major, R.version$minor, sep = "."))')"
|
|
|
|
install_dir="${UVR_INSTALL_DIR:-/usr/local/bin}"
|
|
uvr_bin="${install_dir}/uvr"
|
|
if [ ! -x "$uvr_bin" ]; then
|
|
echo "Bootstrapping uvr ${UVR_PIN} into ${install_dir}"
|
|
UVR_INSTALL_DIR="$install_dir" UVR_VERSION="$UVR_PIN" \
|
|
sh -c 'curl -fsSL https://raw.githubusercontent.com/nbafrank/uvr/main/install.sh | sh'
|
|
fi
|
|
|
|
project_dir="${TMPDIR:-/tmp}/uvr-ci-$$"
|
|
rm -rf "$project_dir"
|
|
mkdir -p "$project_dir"
|
|
trap 'rm -rf "$project_dir"' EXIT
|
|
cd "$project_dir"
|
|
|
|
"$uvr_bin" init --here --r-version "$r_full"
|
|
# --no-install resolves and locks only; retry because concurrent shards can
|
|
# expose short-lived DNS or CRAN-index failures and uvr rolls the manifest back
|
|
# cleanly after an unsuccessful resolution.
|
|
add_attempt=1
|
|
while ! "$uvr_bin" add --no-install "$@"; do
|
|
if [ "$add_attempt" -ge 4 ]; then
|
|
echo "error: uvr add failed after ${add_attempt} attempts" >&2
|
|
exit 1
|
|
fi
|
|
add_delay=$((add_attempt * 10))
|
|
echo "warning: uvr add attempt ${add_attempt} failed; retrying in ${add_delay}s" >&2
|
|
sleep "$add_delay"
|
|
add_attempt=$((add_attempt + 1))
|
|
done
|
|
|
|
# TEMPORARY (drop once the images ship a uvr above v0.4.5): the sync below runs
|
|
# `apt-get install` for every resolved system dependency without refreshing the
|
|
# index first, and the ubuntu build images end their apt layers with
|
|
# `rm -rf /var/lib/apt/lists/*`. With no index apt cannot resolve a package that
|
|
# exists and is enabled, so `igraph needs: libglpk-dev` fails the whole build
|
|
# with `E: Unable to locate package libglpk-dev` on ubuntu 26.04.
|
|
#
|
|
# Fixed upstream in `e491b2e`, tagged one day after v0.4.5 (nbafrank/uvr#250),
|
|
# and the images pick it up via build-env-images#23 — but only after an image
|
|
# rebuild is triggered, which is why this runs here too.
|
|
#
|
|
# apt only: `apk add` fetches its index implicitly and dnf refreshes expired
|
|
# metadata on its own. Non-fatal, since a refresh failure still leaves whatever
|
|
# index is already there, and the install's own error is the more actionable one.
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
apt-get update -qq || echo "warning: apt-get update failed; continuing" >&2
|
|
fi
|
|
|
|
"$uvr_bin" sync --library "$target_lib" --install-system-deps
|