diff options
Diffstat (limited to '.extras/test-build')
| -rwxr-xr-x | .extras/test-build | 864 |
1 files changed, 0 insertions, 864 deletions
diff --git a/.extras/test-build b/.extras/test-build deleted file mode 100755 index f245aed..0000000 --- a/.extras/test-build +++ /dev/null @@ -1,864 +0,0 @@ -#!/bin/bash -# -# test-build - verify an already-published SBo package still builds on a target -# Slackware version inside a throwaway docker container. Resolves + builds its -# SBo deps from the local tree, caches built deps per image digest, reports -# per-package status and lints the target. -# -# Dependency-resolution, cache, and summary logic are adapted from sbo-batch-test -# (github: danixland). The overlay chroot is replaced by a docker container: the -# container IS the disposable environment, so no overlayfs. -# -# No em dashes in prose by author convention. - -# ============================================================================= -# CONFIG (do not edit here; real values live in the external config file) -# ============================================================================= -SBO_TREE_CURRENT="" -SBO_TREE_STABLE="" -IMAGE_CURRENT="" -IMAGE_STABLE="" -LOG_ROOT="/var/log/sbo-test-build" -PKG_CACHE="" - -TB_CONFIG="${TB_CONFIG:-$HOME/.config/sbo-testbuild/config}" -if [[ -f "$TB_CONFIG" ]]; then - # shellcheck disable=SC1090 - source "$TB_CONFIG" -fi - -TB_OVERRIDES="${TB_OVERRIDES:-$HOME/.config/sbo-testbuild/overrides}" - -# ============================================================================= -set -uo pipefail -# Not -e: a package build failing is a handled outcome, not a script crash. - -# ---- flags / globals -------------------------------------------------------- -USE_COLOR=1 # --no-color or non-TTY disables -DRY_RUN=0 # --dry-run: resolve + print order, do not build -ASSUME_YES=0 # --yes: skip the confirm prompt (still prints the order) -USE_CACHE=1 # --no-cache disables the dep cache for one run -KEEP_TARGET=0 # --keep: copy the built target package out to KEEP_DIR -VERSION_ID="current" # "current" | "15.0"; set by --stable -TARGET_ARG="" - -ACTIVE_TREE="" # selected SBo tree (by version) -ACTIVE_IMAGE="" # selected image tag (by version) -RUN_DIR="" # timestamped log dir for this run -DEPS_DIR="" # per-run host dir of built dep .txz, mounted into the container -BUILD_OUT="" # per-run host dir where built packages are copied out - -# Status tracking. Keyed by "category/prog". Parallel assoc arrays. -declare -A ST_STATUS=() -declare -A ST_REASON=() -declare -A ST_TIME=() -declare -A ST_README=() - -usage() { - cat <<'EOF' -test-build - verify an SBo package builds on a target Slackware in docker - -USAGE: - test-build [OPTIONS] <program-name> - -OPTIONS: - -h, --help This text. - --stable Target Slackware 15.0 (image + tree). Default is -current. - --dry-run Resolve, apply overrides, print the build order, do not build. - --yes Skip the confirm prompt (the order is still printed first). - --no-cache Rebuild all deps this run, ignore/refresh the cache. - --keep Copy the built target package out to a kept/ dir (path is - printed), so it can be installed on the host. The build is - otherwise throwaway. - --no-color Disable ANSI color (auto-disabled when stdout is not a TTY). -EOF -} - -parse_args() { - while [[ $# -gt 0 ]]; do - case "$1" in - -h|--help) usage; exit 0 ;; - --stable|15.0) VERSION_ID="15.0"; shift ;; - --dry-run) DRY_RUN=1; shift ;; - --yes) ASSUME_YES=1; shift ;; - --no-cache) USE_CACHE=0; shift ;; - --keep) KEEP_TARGET=1; shift ;; - --no-color) USE_COLOR=0; shift ;; - -*) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - *) - if [[ -n "$TARGET_ARG" ]]; then - echo "Only one target accepted (got '$TARGET_ARG' and '$1')." >&2 - exit 2 - fi - TARGET_ARG="$1"; shift ;; - esac - done - if [[ -z "$TARGET_ARG" ]]; then - echo "No target given." >&2; usage >&2; exit 2 - fi -} - -init_color() { - if [[ $USE_COLOR -eq 1 && -t 1 ]]; then - C_RED=$'\e[31m'; C_GRN=$'\e[32m'; C_YEL=$'\e[33m'; C_RST=$'\e[0m' - else - C_RED=""; C_GRN=""; C_YEL=""; C_RST="" - fi -} - -# Map VERSION_ID to the active tree + image. No I/O, unit-testable. -select_version_paths() { - if [[ "$VERSION_ID" == "15.0" ]]; then - ACTIVE_TREE="$SBO_TREE_STABLE"; ACTIVE_IMAGE="$IMAGE_STABLE" - else - ACTIVE_TREE="$SBO_TREE_CURRENT"; ACTIVE_IMAGE="$IMAGE_CURRENT" - fi -} - -# require_config: the external config must exist and set the version's paths. -require_config() { - if [[ ! -f "$TB_CONFIG" ]]; then - cat >&2 <<EOF -No config file: $TB_CONFIG -Copy the example and edit it: - - mkdir -p "\$(dirname "$TB_CONFIG")" - cp .extras/test-build-config.example "$TB_CONFIG" - \${EDITOR:-vi} "$TB_CONFIG" -EOF - exit 1 - fi - select_version_paths - if [[ -z "$ACTIVE_TREE" || -z "$ACTIVE_IMAGE" ]]; then - echo "Config $TB_CONFIG is missing the tree/image for version '$VERSION_ID'." >&2 - exit 1 - fi - if [[ ! -d "$ACTIVE_TREE" ]]; then - echo "SBo tree for '$VERSION_ID' does not exist: $ACTIVE_TREE" >&2 - exit 1 - fi -} - -# ============================================================================= -# SBo tree lookup -# ============================================================================= -# In this tool there is one active tree per run, but the resolver reads an array -# named SBO_TREE_ROOTS so the ported logic and its self-check match sbo-batch-test. -# main() sets SBO_TREE_ROOTS=("$ACTIVE_TREE") after require_config. -declare -a SBO_TREE_ROOTS=() - -find_slackbuild_dir() { - local prog="$1" root d - for root in "${SBO_TREE_ROOTS[@]}"; do - [[ -d "$root" ]] || continue - for d in "$root"/*/"$prog"; do - if [[ -d "$d" && -f "$d/$prog.info" ]]; then - echo "$d"; return 0 - fi - done - done - return 1 -} - -category_of() { basename "$(dirname "$1")"; } -pkg_key() { echo "$(category_of "$1")/$(basename "$1")"; } - -# Resolve the TARGET to test from CWD or a path (repo-agnostic). The target is -# the SlackBuild you are editing, wherever it lives; its deps come from the -# configured SBo tree (see find_slackbuild_dir), NOT the other way around. -# - a path (absolute, or containing '/', or '.'/'./x'): use that dir directly -# - a bare name: ./<name>/ under CWD, else CWD itself if it IS <name>/ -# Prints the resolved absolute dir on success; returns 1 (with a message) if the -# dir has no matching <name>.info. -resolve_target_dir() { - local arg="$1" dir prog - if [[ "$arg" == /* || "$arg" == .* || "$arg" == */* ]]; then - dir="${arg%/}"; prog="$(basename "$dir")" - elif [[ -f "./$arg/$arg.info" ]]; then - dir="./$arg"; prog="$arg" - elif [[ "$(basename "$PWD")" == "$arg" && -f "./$arg.info" ]]; then - dir="."; prog="$arg" - else - echo "Target '$arg' not found: no ./$arg/$arg.info, and CWD is not $arg/." >&2 - return 1 - fi - if [[ ! -f "$dir/$prog.info" ]]; then - echo "Target dir '$dir' has no $prog.info (expected a SlackBuild package dir)." >&2 - return 1 - fi - ( cd "$dir" && pwd ) # emit absolute path -} - -read_requires() { - local info="$1" - # shellcheck disable=SC1090 - ( set +u; source "$info"; echo "${REQUIRES:-}" ) -} - -version_of() { - local dir="$1" - local info="$dir/$(basename "$dir").info" - [[ -f "$info" ]] || return - local v; v="$(grep -m1 '^VERSION=' "$info" | cut -d'"' -f2)" - echo "$v" -} - -# ============================================================================= -# DEPENDENCY RESOLUTION (topo sort + cycle detection, LOCAL tree only) -# ============================================================================= -declare -a RESOLVED_ORDER=() -declare -A UNMET=() -declare -a CYCLES=() -declare -A HAS_README=() -declare -A _vstate=() -declare -A FETCH_DEPS=() # prog -> 1: resolve via sbopkg in-container - -# Is a prog already present in the container base? Kept as a callback so the -# pure topo logic stays testable. -# -# Populated lazily from the active image's package db on first call: each entry -# in /var/log/packages is named <name>-<ver>-<arch>-<build>, so strip the last -# three dash-fields to get the bare package name and match the SBo token -# against that. TB_BASE_PKGS can be pre-seeded by tests to bypass docker. -declare -A TB_BASE_PKGS=() -TB_BASE_LOADED="${TB_BASE_LOADED:-0}" - -load_base_pkgs() { - [[ "$TB_BASE_LOADED" == "1" ]] && return 0 - TB_BASE_LOADED=1 - [[ -n "$ACTIVE_IMAGE" ]] || return 0 - local entry name - while IFS= read -r entry; do - [[ -n "$entry" ]] || continue - # strip trailing -<ver>-<arch>-<build> - name="${entry%-*-*-*}" - TB_BASE_PKGS["$name"]=1 - done < <(docker run --rm "$ACTIVE_IMAGE" \ - /bin/sh -c 'ls /var/log/packages' 2>/dev/null) -} - -installed_in_base() { - load_base_pkgs - [[ -n "${TB_BASE_PKGS[$1]:-}" ]] -} - -_resolve_visit() { - local dir="$1" parent="$2" - local key; key="$(basename "$dir")" - - if [[ "${_vstate[$dir]:-}" == "1" ]]; then return 0; fi - if [[ "${_vstate[$dir]:-}" == "0" ]]; then - CYCLES+=("cycle involving $key (pulled in via $parent)") - return 1 - fi - _vstate["$dir"]=0 - - local info="$dir/$(basename "$dir").info" - local req tok depdir rc=0 - req="$(read_requires "$info")" - for tok in $req; do - if [[ "$tok" == "%README%" ]]; then - HAS_README["$dir"]=1 - continue - fi - tok="$(apply_rename "$tok")" - # drop: satisfied by the -current base (e.g. a 15.0-only dep like rust-opt - # whose payload ships in current's full-install image). Skip it entirely: - # no recurse, no UNMET, no order entry. Inert on 15.0 (overrides are). - if [[ "${OV_DROP[$tok]:-}" == "1" ]]; then - continue - fi - if [[ "${OV_FETCH[$tok]:-}" == "1" ]]; then - FETCH_DEPS["$tok"]=1 - continue - fi - if depdir="$(find_slackbuild_dir "$tok")"; then - _resolve_visit "$depdir" "$key" || rc=1 - elif installed_in_base "$tok"; then - : - else - UNMET["$tok"]="needed by $key" - rc=1 - fi - done - - _vstate["$dir"]=1 - RESOLVED_ORDER+=("$dir") - return $rc -} - -resolve_target() { - local dir="$1" - RESOLVED_ORDER=() - CYCLES=() - UNMET=() - FETCH_DEPS=() - _vstate=() - _resolve_visit "$dir" "(top)" -} - -# ============================================================================= -# current-vs-stable overrides. Parsed from $TB_OVERRIDES. Applied only when -# targeting -current (15.0 is the SBo baseline, no deltas). -# ============================================================================= -declare -A OV_DROP=() # prog -> 1 -declare -A OV_RENAME=() # old -> new -declare -A OV_FETCH=() # prog -> 1 - -load_overrides() { - OV_DROP=(); OV_RENAME=(); OV_FETCH=() - # 15.0 is the baseline: no overrides. - [[ "$VERSION_ID" == "15.0" ]] && return - [[ -f "$TB_OVERRIDES" ]] || return - local line kind rest - while IFS= read -r line; do - line="${line%%#*}" # strip comments - line="${line#"${line%%[![:space:]]*}"}" # ltrim - [[ -z "$line" ]] && continue - kind="${line%%:*}"; rest="${line#*:}" - kind="${kind//[[:space:]]/}" - rest="${rest#"${rest%%[![:space:]]*}"}" # ltrim value - case "$kind" in - drop) OV_DROP["${rest//[[:space:]]/}"]=1 ;; - fetch) OV_FETCH["${rest//[[:space:]]/}"]=1 ;; - rename) - # rest is "old -> new" - local old new - old="${rest%%->*}"; new="${rest##*->}" - old="${old//[[:space:]]/}"; new="${new//[[:space:]]/}" - [[ -n "$old" && -n "$new" ]] && OV_RENAME["$old"]="$new" ;; - *) echo "WARN: unknown override rule: $line" >&2 ;; - esac - done < "$TB_OVERRIDES" -} - -# Map a dep token through rename rules (identity if no rule). -apply_rename() { - local tok="$1" - echo "${OV_RENAME[$tok]:-$tok}" -} - -# Remove dropped packages from RESOLVED_ORDER in place. -apply_overrides_to_order() { - local d prog keep=() - for d in "${RESOLVED_ORDER[@]}"; do - prog="$(basename "$d")" - [[ "${OV_DROP[$prog]:-}" == "1" ]] && continue - keep+=("$d") - done - # Assign without "${keep[@]:-}": under set -u that fallback yields a - # one-element array holding an empty string when keep is empty (all deps - # dropped), which the build loop would then iterate over as d="". Guard it. - if [[ ${#keep[@]} -eq 0 ]]; then - RESOLVED_ORDER=() - else - RESOLVED_ORDER=("${keep[@]}") - fi -} - -# ============================================================================= -# Dependency cache. Layout: $CACHE_ROOT/<cat>/<prog>/<prog>-<ver>-...txz where -# CACHE_ROOT = $PKG_CACHE/<image-digest> (set per run by resolve_cache_root). -# Key is prog+version. --no-cache (USE_CACHE=0) or empty PKG_CACHE disables. -# ============================================================================= -CACHE_ROOT="" # set by resolve_cache_root once the image digest is known - -# True when the cache is usable this run. -_cache_on() { [[ $USE_CACHE -eq 1 && -n "$PKG_CACHE" && -n "$CACHE_ROOT" ]]; } - -_cache_ver_of() { - local prog="$1" base="$2" - base="${base#"$prog"-}" - echo "${base%%-*}" -} - -# cache_decision <cat> <prog> <version> -> cached | bump:OLD:NEW | new -cache_decision() { - local cat="$1" prog="$2" version="$3" - _cache_on || { echo new; return; } - local dir="$CACHE_ROOT/$cat/$prog" - local f newest="" - for f in "$dir/$prog"-*.t?z; do - [[ -e "$f" ]] || continue - [[ -z "$newest" || "$f" -nt "$newest" ]] && newest="$f" - done - [[ -z "$newest" ]] && { echo new; return; } - local have; have="$(_cache_ver_of "$prog" "$(basename "$newest")")" - if [[ "$have" == "$version" ]]; then echo cached; else echo "bump:$have:$version"; fi -} - -cache_path() { - local cat="$1" prog="$2" version="$3" - _cache_on || return - local dir="$CACHE_ROOT/$cat/$prog" - local f newest="" - for f in "$dir/$prog"-*.t?z; do - [[ -e "$f" ]] || continue - [[ -z "$newest" || "$f" -nt "$newest" ]] && newest="$f" - done - [[ -z "$newest" ]] && return - [[ "$(_cache_ver_of "$prog" "$(basename "$newest")")" == "$version" ]] && echo "$newest" -} - -cache_store() { - local cat="$1" prog="$2" src="$3" - _cache_on || return - local dir="$CACHE_ROOT/$cat/$prog" - mkdir -p "$dir" - rm -f "$dir"/*.t?z - cp -a "$src" "$dir/" -} - -cache_label() { - local dir="$1" is_target="$2" - local cat prog ver dec - cat="$(category_of "$dir")"; prog="$(basename "$dir")"; ver="$(version_of "$dir")" - dec="$(cache_decision "$cat" "$prog" "$ver")" - local label - case "$dec" in - cached) label="cached ($ver)" ;; - bump:*) label="rebuild: ${dec#bump:}"; label="${label/:/ -> }" ;; - *) label="build (new)" ;; - esac - if [[ "$is_target" == "1" ]]; then - case "$dec" in - cached) label="build (cached $ver, rebuilt as target)" ;; - esac - echo "target, $label" - else - echo "$label" - fi -} - -# Compute CACHE_ROOT from the image's digest, namespaced by variant. Falls back -# to the tag if the digest cannot be read (still isolates per image reference). -# The dir is "<variant>-<digest>", so an image rebuild changes the digest and -# self-invalidates the cache (deps built against the old base are never reused). -# -# Prune stale digests of THIS variant on the way in: a base update leaves the -# old <variant>-<olddigest> dir orphaned, so drop it. Scoped to the variant so a -# --current run does not wipe the 15.0 cache (both live caches survive, and -# testing both trees back-to-back still reuses built deps). -resolve_cache_root() { - [[ -z "$PKG_CACHE" ]] && { CACHE_ROOT=""; return; } - local digest - digest="$(docker image inspect --format '{{index .Id}}' "$ACTIVE_IMAGE" 2>/dev/null)" - [[ -z "$digest" ]] && digest="tag-${ACTIVE_IMAGE//[^a-zA-Z0-9._-]/_}" - digest="${digest//[^a-zA-Z0-9._-]/_}" - local name="${VERSION_ID}-${digest}" - CACHE_ROOT="$PKG_CACHE/$name" - mkdir -p "$CACHE_ROOT" - - # drop superseded caches for this variant only - local d - for d in "$PKG_CACHE/${VERSION_ID}-"*; do - [[ -d "$d" ]] || continue - [[ "$(basename "$d")" == "$name" ]] && continue - rm -rf "$d" - done -} - -# Does SlackBuild dir $1 directly require any prog in the dead list (nameref $2)? -# Direct-requires check only; transitive blocking works because run_target -# iterates in topo order, propagating a failure one hop per package. -depends_on_failed() { - local dir="$1"; local -n failed="$2" - local info="$dir/$(basename "$dir").info" - local req tok f - req="$(read_requires "$info")" - for tok in $req; do - [[ "$tok" == "%README%" ]] && continue - for f in "${failed[@]:-}"; do - [[ "$tok" == "$f" ]] && return 0 - done - done - return 1 -} - -# build_one <slackbuild-dir> <is_target 0|1> [container-name] -# Runs the build in a throwaway container. Sets ST_STATUS/ST_REASON/ST_TIME. -# Successful builds copy their package to a host workdir; deps are cached and -# the target is linted. Returns 0 on SUCCESS/CACHED, 1 otherwise. -build_one() { - local dir="$1" is_target="${2:-0}" - local prog cat key - prog="$(basename "$dir")"; cat="$(category_of "$dir")"; key="$cat/$prog" - local logf="$RUN_DIR/${cat}_${prog}.log" - local start; start=$(date +%s) - local version; version="$(version_of "$dir")" - - [[ "${HAS_README[$dir]:-}" == "1" ]] && ST_README["$key"]=1 - - # Dep with a version-matching cached package: installpkg it into the shared - # dep-package dir; no build. The target never takes this path. - if [[ "$is_target" != "1" ]]; then - local cached; cached="$(cache_path "$cat" "$prog" "$version")" - if [[ -n "$cached" ]]; then - cp -a "$cached" "$DEPS_DIR/" - { - echo "===== test-build: $prog (from cache) =====" - echo "cached package: $(basename "$cached")" - } >> "$logf" - ST_TIME["$key"]=$(( $(date +%s) - start )) - ST_STATUS["$key"]="CACHED" - return 0 - fi - fi - - # Build in a container. Mounts: - # $dir -> /sbo/pkg (ro, the SlackBuild) - # $DEPS_DIR -> /sbo/deps (rw, already-built dep .txz to installpkg first) - # $BUILD_OUT -> /sbo/out (rw, where the built package is copied out) - # The in-container script installs any deps present, then builds the target, - # writes a status token to /sbo/out/$prog.status, and copies the package out. - local statf="$BUILD_OUT/$prog.status" - rm -f "$statf" - - # fetch deps (removed from -current tree): let the container's sbopkg build - # them first. FETCH_DEPS is the set collected during resolution. - local fetch_list="" - local fp - for fp in "${!FETCH_DEPS[@]}"; do fetch_list+="$fp "; done - - # -i is required: without it docker does not attach stdin, so `bash -s` reads - # nothing and the heredoc script is silently discarded (exit 0, empty log). - docker run --rm -i \ - -v "$dir":/sbo/pkg:ro \ - -v "$DEPS_DIR":/sbo/deps \ - -v "$BUILD_OUT":/sbo/out \ - -e PROG="$prog" \ - -e FETCH_LIST="$fetch_list" \ - -e IS_TARGET="$is_target" \ - "$ACTIVE_IMAGE" /bin/bash -s >>"$logf" 2>&1 <<'CONTAINER_EOF' -set -uo pipefail -prog="$PROG" -statf="/sbo/out/$prog.status" - -# 0. install already-built dependency packages (order guaranteed by the host). -for d in /sbo/deps/*.t?z; do - [[ -e "$d" ]] || continue - installpkg --terse "$d" || { echo "INSTALL-FAILED (dep $d)"; echo INSTALL-FAILED > "$statf"; exit 1; } -done - -# 0b. fetch-from-SBo deps via sbopkg (removed from the -current tree). -for f in $FETCH_LIST; do - echo "sbopkg-building fetch dep: $f" - sbopkg -B -i "$f" || { echo "BUILD-FAILED (fetch dep $f)"; echo BUILD-FAILED > "$statf"; exit 1; } -done - -# copy the SlackBuild out of the read-only mount so it can write there. -cp -a /sbo/pkg /sbo/build -cd /sbo/build || { echo BUILD-FAILED > "$statf"; exit 1; } -. ./"$prog".info - -export OUTPUT=/sbo/out -mkdir -p "$OUTPUT" - -echo "===== test-build: $prog =====" -echo "PRGNAM=${PRGNAM:-$prog} VERSION=${VERSION:-?} BUILD=${BUILD:-?} TAG=${TAG:-?}" -echo "uname -m: $(uname -m) OUTPUT=$OUTPUT" -echo "REQUIRES=${REQUIRES:-}" -echo "=================================" - -if [ "$(uname -m)" = "x86_64" ] && [ -n "${DOWNLOAD_x86_64:-}" ] && [ "${DOWNLOAD_x86_64}" != "UNSUPPORTED" ] && [ "${DOWNLOAD_x86_64}" != "UNTESTED" ]; then - DL="$DOWNLOAD_x86_64"; MD="$MD5SUM_x86_64" -else - DL="$DOWNLOAD"; MD="$MD5SUM" -fi - -for u in $DL; do - wget -c --tries=3 "$u" || { echo DOWNLOAD-FAILED > "$statf"; exit 1; } -done - -set -- $MD -for u in $DL; do - f="$(basename "$u")" - want="$1"; shift - got="$(md5sum "$f" | cut -d' ' -f1)" - if [ "$got" != "$want" ]; then - echo "MD5 mismatch on $f: want $want got $got" - echo MD5-MISMATCH > "$statf"; exit 1 - fi -done - -# Source /etc/profile.d so dep-provided env is live (google-go-lang sets GOROOT -# + PATH to its go here, rust-opt sets cargo, etc.). This heredoc runs in a -# non-login shell, which does NOT read profile.d, so a bare `go build` would -# otherwise pick the system gccgo instead of the installed google-go-lang. -set +u # profile.d scripts routinely reference unset vars -for pf in /etc/profile.d/*.sh; do - [ -r "$pf" ] && . "$pf" -done -set -u - -chmod +x ./"$prog".SlackBuild -if ! ./"$prog".SlackBuild; then - echo BUILD-FAILED > "$statf"; exit 1 -fi - -pkg="$(ls -t "$OUTPUT"/"$prog"-*.t?z 2>/dev/null | head -n1)" -if [ -z "$pkg" ]; then - echo "No package produced in $OUTPUT" - echo BUILD-FAILED > "$statf"; exit 1 -fi -if ! installpkg --terse "$pkg"; then - echo INSTALL-FAILED > "$statf"; exit 1 -fi -echo "===== installed files: $(basename "$pkg") =====" -pkgname="$(basename "$pkg")"; pkgname="${pkgname%.t?z}" -cat "/var/log/packages/$pkgname" 2>/dev/null || echo "(package db entry not found)" -echo "=================================" - -# lint the target here in the container: sbopkglint is baked into the image and -# runs as root, so it needs no host sudo. Fail-soft: findings never fail the build. -if [ "${IS_TARGET:-0}" = "1" ] && command -v sbopkglint >/dev/null 2>&1; then - echo "===== sbopkglint: $(basename "$pkg") =====" - if sbopkglint "$pkg"; then - echo "LINT-CLEAN" - else - echo "LINT-FINDINGS" - fi - echo "=================================" -fi -echo SUCCESS > "$statf" -CONTAINER_EOF - - local status="BUILD-FAILED" - [[ -f "$statf" ]] && status="$(cat "$statf")" - ST_TIME["$key"]=$(( $(date +%s) - start )) - ST_STATUS["$key"]="$status" - - if [[ "$status" == "SUCCESS" ]]; then - # locate the built package copied to the host workdir - local built newest="" - for built in "$BUILD_OUT/${prog}"-*.t?z; do - [[ -e "$built" ]] || continue - [[ -z "$newest" || "$built" -nt "$newest" ]] && newest="$built" - done - if [[ -n "$newest" ]]; then - if [[ "$is_target" != "1" ]]; then - cache_store "$cat" "$prog" "$newest" - # make the dep available to later builds in this run - cp -a "$newest" "$DEPS_DIR/" - else - # lint ran in-container (see IS_TARGET block); surface its verdict from - # the log so the host summary can show clean/findings. - if grep -q '^LINT-FINDINGS$' "$logf" 2>/dev/null; then - echo " lint: ${C_RED}findings${C_RST} (see $(basename "$logf"))" - elif grep -q '^LINT-CLEAN$' "$logf" 2>/dev/null; then - echo " lint: ${C_GRN}clean${C_RST}" - fi - # --keep: copy the built target package to a durable dir (sibling of the - # logs tree, not inside a throwaway run dir) so it can be installed on - # the host, e.g. to regenerate post-install artifacts. - if [[ $KEEP_TARGET -eq 1 ]]; then - local keepdir; keepdir="$(dirname "$LOG_ROOT")/kept" - mkdir -p "$keepdir" - cp -a "$newest" "$keepdir/" - echo " kept: $keepdir/$(basename "$newest")" - fi - fi - fi - return 0 - fi - ST_REASON["$key"]="see $(basename "$logf")" - return 1 -} - -# confirm_order: print the resolved order (overrides marked) and ask to proceed. -# --yes skips the prompt but the order is still printed. --dry-run never reaches -# here. Returns 0 to proceed, 1 to abort. -confirm_order() { - local target_dir="$1" - echo " build order (${VERSION_ID}):" - local d - for d in "${RESOLVED_ORDER[@]}"; do - local it=0; [[ "$d" == "$target_dir" ]] && it=1 - local rdm=""; [[ "${HAS_README[$d]:-}" == 1 ]] && rdm=" [%README%]" - printf " %-30s %s%s\n" "$(pkg_key "$d")" "$(cache_label "$d" "$it")" "$rdm" - echo "$(pkg_key "$d")" >> "$RUN_DIR/build-order.txt" - done - # note fetch deps (built via sbopkg in-container, not in the order list) - local fp - for fp in "${!FETCH_DEPS[@]}"; do - printf " %-30s %s\n" "$fp" "fetch (sbopkg in container)" - done - [[ $ASSUME_YES -eq 1 ]] && return 0 - local reply - read -rp " Proceed? [Y/n] " reply - [[ -z "$reply" || "$reply" =~ ^[Yy]$ ]] -} - -# run_target <target-slackbuild-dir> -run_target() { - local target_dir="$1" - local tkey; tkey="$(pkg_key "$target_dir")" - - echo - echo "=== Target: $tkey (${VERSION_ID}) ===" - resolve_target "$target_dir" - apply_overrides_to_order - - # Hard resolution failures: report and stop, do not build. - if [[ ${#CYCLES[@]} -gt 0 || ${#UNMET[@]} -gt 0 ]]; then - local why="" - if [[ ${#UNMET[@]} -gt 0 ]]; then - local u - for u in "${!UNMET[@]}"; do why+="unmet:$u(${UNMET[$u]}) "; done - fi - [[ ${#CYCLES[@]} -gt 0 ]] && why+="${CYCLES[*]}" - ST_STATUS["$tkey"]="UNMET-DEP" - ST_REASON["$tkey"]="$why" - echo " resolution failed: $why" - echo " add an override rule ($TB_OVERRIDES) and rerun, or fix the tree." >&2 - return - fi - - if [[ $DRY_RUN -eq 1 ]]; then - echo " build order (dry-run):" - local d - for d in "${RESOLVED_ORDER[@]}"; do - local it=0; [[ "$d" == "$target_dir" ]] && it=1 - local rdm=""; [[ "${HAS_README[$d]:-}" == 1 ]] && rdm=" [%README%]" - printf " %-30s %s%s\n" "$(pkg_key "$d")" "$(cache_label "$d" "$it")" "$rdm" - echo "$(pkg_key "$d")" >> "$RUN_DIR/build-order.txt" - done - local fp - for fp in "${!FETCH_DEPS[@]}"; do - printf " %-30s %s\n" "$fp" "fetch (sbopkg in container)" - done - return - fi - - if ! confirm_order "$target_dir"; then - echo " aborted." - ST_STATUS["$tkey"]="ABORTED" - return - fi - - # Per-run docker workdirs (host side, discarded after the run). - DEPS_DIR="$RUN_DIR/deps"; BUILD_OUT="$RUN_DIR/out" - mkdir -p "$DEPS_DIR" "$BUILD_OUT" - - local d failed_progs=() - for d in "${RESOLVED_ORDER[@]}"; do - local key; key="$(pkg_key "$d")" - local prog; prog="$(basename "$d")" - - if depends_on_failed "$d" failed_progs; then - ST_STATUS["$key"]="BLOCKED-BY-DEP" - ST_REASON["$key"]="blocked by failed dep" - [[ "${HAS_README[$d]:-}" == "1" ]] && ST_README["$key"]=1 - echo " $key: BLOCKED-BY-DEP" - failed_progs+=("$prog") - continue - fi - - local it=0; [[ "$d" == "$target_dir" ]] && it=1 - echo " building $key ..." - if build_one "$d" "$it"; then - echo " $key: ${ST_STATUS[$key]} (${ST_TIME[$key]}s)" - else - echo " $key: ${ST_STATUS[$key]} (${ST_TIME[$key]}s)" - failed_progs+=("$prog") - fi - done -} - -# ============================================================================= -# SUMMARY (ported) -# ============================================================================= -print_summary() { - local total=$SECONDS - local succ=0 fail=0 blocked=0 cached=0 - local summary="$RUN_DIR/summary.log" - - { - echo "test-build run summary" - echo "target: $TARGET_ARG version: $VERSION_ID" - echo - } > "$summary" - - echo - echo "================ SUMMARY ================" - local key - for key in "${!ST_STATUS[@]}"; do - local st="${ST_STATUS[$key]}" rsn="${ST_REASON[$key]:-}" t="${ST_TIME[$key]:-0}" - local rd=""; [[ "${ST_README[$key]:-}" == "1" ]] && rd=" [%README%]" - local col="$C_YEL" - case "$st" in - SUCCESS) col="$C_GRN"; ((succ++)) ;; - CACHED) col="$C_GRN"; ((cached++)) ;; - BLOCKED-BY-DEP|UNMET-DEP|ABORTED) col="$C_YEL"; ((blocked++)) ;; - *) col="$C_RED"; ((fail++)) ;; - esac - printf "%s%-30s %-16s%s %s%s (%ss)\n" "$col" "$key" "$st" "$C_RST" "$rsn" "$rd" "$t" - printf "%-30s %-16s %s%s (%ss)\n" "$key" "$st" "$rsn" "$rd" "$t" >> "$summary" - done - echo "----------------------------------------" - printf "%s%d succeeded%s, %s%d failed%s, %s%d blocked%s, %s%d cached%s, total %ss\n" \ - "$C_GRN" "$succ" "$C_RST" "$C_RED" "$fail" "$C_RST" "$C_YEL" "$blocked" "$C_RST" \ - "$C_GRN" "$cached" "$C_RST" "$total" - echo "logs: $RUN_DIR" - if [[ $fail -eq 0 && $blocked -eq 0 ]]; then - echo "${C_GRN}All green.${C_RST} Safe to build the SBo submission tarball on the host." - fi - { - echo - echo "$succ succeeded, $fail failed, $blocked blocked, $cached cached, total ${total}s" - echo "logs: $RUN_DIR" - } >> "$summary" -} - - -# Ensure the selected image is available locally. It is built by a separate job -# (the image-builder) and pushed to the LAN registry; this script only consumes -# it. If it is not already local, pull it once (ACTIVE_IMAGE is a fully-qualified -# registry ref). A stale local tag is not refreshed here. -# ponytail: pull-if-missing only. If the registry's :current is rebuilt, the -# local copy goes stale silently. Add a --pull force-flag if that bites. -require_image() { - docker image inspect "$ACTIVE_IMAGE" >/dev/null 2>&1 && return 0 - echo "Image not present locally, pulling: $ACTIVE_IMAGE" >&2 - docker pull "$ACTIVE_IMAGE" >&2 && return 0 - cat >&2 <<EOF -Image not found and pull failed: $ACTIVE_IMAGE -It is produced by the image-builder job (full Slackware $VERSION_ID + -sbo-maintainer-tools + sbopkg) and pushed to the LAN registry. Check the -registry is reachable and the tag exists, then rerun. This script does not -build images. -EOF - exit 1 -} - -main() { - parse_args "$@" - init_color - require_config - SBO_TREE_ROOTS=("$ACTIVE_TREE") - load_overrides - - # image + cache namespace (skip the image check on a pure dry-run so the order - # can be inspected without the image present). - if [[ $DRY_RUN -eq 0 ]]; then - command -v docker >/dev/null 2>&1 || { echo "docker not found in PATH." >&2; exit 1; } - require_image - fi - resolve_cache_root - - RUN_DIR="$LOG_ROOT/$(date +%Y-%m-%d_%H-%M-%S)" - mkdir -p "$RUN_DIR" - : > "$RUN_DIR/build-order.txt" - - # The target is the local SlackBuild under test (CWD or a path); its deps are - # resolved from the configured SBo tree during _resolve_visit. - local tdir - if ! tdir="$(resolve_target_dir "$TARGET_ARG")"; then - exit 1 - fi - - run_target "$tdir" - print_summary -} - -main "$@" |
