#!/bin/sh # Host-safe entry point for the pinned RVBox build toolchain. set -eu repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) compose_file=$repo_root/deploy/compose.yaml toolchain_image=rvbox-dev-toolchain:latest cache_volumes='rvbox-dev_rvbox-go-build rvbox-dev_rvbox-go-mod' usage() { cat <<'EOF' usage: scripts/build build|verify|doctor|recover|clean [options] build build supported binaries in the pinned toolchain verify run the normal pinned verification gate doctor read-only toolchain/artifact/cache availability report recover rebuild a missing/broken toolchain image, then build clean print owned cleanup targets; no deletion without --execute clean options: --artifacts remove only this repository's ignored bin/ directory --toolchain remove only rvbox-dev-toolchain:latest --caches remove only the two named RVBox Go cache volumes --all select all three owned target classes --execute perform the selected deletion (otherwise dry run) --yes skip the interactive confirmation; requires --execute Run artifacts below .test-runs/ are deliberately not a build-clean target. Use scripts/test-env purge --run-id ID (or its explicit --all workflow) so a resumable test environment is never removed by a build cleanup operation. EOF } fail() { printf '%s\n' "build: $*" >&2; exit 2; } docker_ready() { docker version >/dev/null 2>&1 || fail "Docker is unavailable; start or repair Docker, then retry" docker compose -f "$compose_file" version >/dev/null 2>&1 || fail "Docker Compose is unavailable" } ensure_toolchain() { docker_ready if ! docker image inspect "$toolchain_image" >/dev/null 2>&1; then printf '%s\n' "build: toolchain image is absent; rebuilding from $compose_file" >&2 docker compose -f "$compose_file" build toolchain fi } run_make() { target=$1 ensure_toolchain cd "$repo_root" exec docker compose -f "$compose_file" run --rm toolchain make "$target" } size_of() { path=$1 if [ -e "$path" ]; then du -sh "$path" 2>/dev/null | awk '{print $1}' || printf '%s' '?'; else printf '%s' '0'; fi } doctor() { docker_ready printf 'repository=%s\n' "$repo_root" if docker image inspect "$toolchain_image" >/dev/null 2>&1; then printf 'toolchain_image=%s present\n' "$toolchain_image" else printf 'toolchain_image=%s absent (scripts/build build will rebuild it)\n' "$toolchain_image" fi for volume in $cache_volumes; do if docker volume inspect "$volume" >/dev/null 2>&1; then printf 'cache_volume=%s present\n' "$volume" else printf 'cache_volume=%s absent (Go will repopulate it)\n' "$volume" fi done printf 'build_artifacts=%s size=%s\n' "$repo_root/bin" "$(size_of "$repo_root/bin")" printf '%s\n' 'test artifacts are managed separately by scripts/test-env' } confirm() { [ "$yes" = yes ] && return 0 [ -t 0 ] || fail "--execute without a terminal requires --yes" printf '%s' 'Proceed with exactly the printed RVBox cleanup targets? [y/N] ' >&2 read -r answer [ "$answer" = y ] || [ "$answer" = Y ] || fail "cleanup cancelled" } clean() { artifacts=no toolchain=no caches=no execute=no yes=no while [ "$#" -gt 0 ]; do case $1 in --artifacts) artifacts=yes ;; --toolchain) toolchain=yes ;; --caches) caches=yes ;; --all) artifacts=yes; toolchain=yes; caches=yes ;; --execute) execute=yes ;; --yes) yes=yes ;; --help|-h) usage; return 0 ;; *) fail "unknown clean option $1" ;; esac shift done [ "$artifacts" = yes ] || [ "$toolchain" = yes ] || [ "$caches" = yes ] || fail "select at least one clean target" docker_ready printf '%s\n' 'RVBox cleanup targets:' if [ "$artifacts" = yes ]; then if [ -L "$repo_root/bin" ]; then fail "refusing symlink build-artifact target $repo_root/bin"; fi printf ' artifact_dir=%s size=%s\n' "$repo_root/bin" "$(size_of "$repo_root/bin")" fi if [ "$toolchain" = yes ]; then if docker image inspect "$toolchain_image" >/dev/null 2>&1; then users=$(docker ps -a --filter "ancestor=$toolchain_image" --format '{{.ID}}' | wc -l | tr -d ' ') printf ' toolchain_image=%s containers=%s\n' "$toolchain_image" "$users" [ "$users" = 0 ] || fail "refusing to remove an image still referenced by containers" else printf ' toolchain_image=%s absent\n' "$toolchain_image" fi fi if [ "$caches" = yes ]; then for volume in $cache_volumes; do if docker volume inspect "$volume" >/dev/null 2>&1; then printf ' cache_volume=%s\n' "$volume" else printf ' cache_volume=%s absent\n' "$volume" fi done fi if [ "$execute" != yes ]; then printf '%s\n' 'dry run only; rerun with --execute (and optionally --yes) to delete these exact targets' return 0 fi confirm if [ "$artifacts" = yes ] && [ -d "$repo_root/bin" ]; then rm -rf "$repo_root/bin"; fi if [ "$toolchain" = yes ] && docker image inspect "$toolchain_image" >/dev/null 2>&1; then docker image rm "$toolchain_image"; fi if [ "$caches" = yes ]; then for volume in $cache_volumes; do if docker volume inspect "$volume" >/dev/null 2>&1; then docker volume rm "$volume"; fi done fi printf '%s\n' 'RVBox build cleanup complete; scripts/build recover will recreate the toolchain and build artifacts.' } command=${1:-build} shift || true case $command in build|verify) [ "$#" -eq 0 ] || fail "$command does not accept additional arguments"; run_make "$command" ;; doctor) [ "$#" -eq 0 ] || fail "doctor does not accept additional arguments"; doctor ;; recover) [ "$#" -eq 0 ] || fail "recover does not accept additional arguments"; docker_ready; docker compose -f "$compose_file" build toolchain; run_make build ;; clean) clean "$@" ;; --help|-h) usage ;; *) usage >&2; fail "unknown command $command" ;; esac