build: add safe toolchain cleanup and recovery
This commit is contained in:
@@ -190,7 +190,7 @@ scripts/test-unit [--package PATTERN] [--run REGEXP] [--race]
|
|||||||
scripts/test-integration [--suite NAME|all] [--run-id ID] [--resume]
|
scripts/test-integration [--suite NAME|all] [--run-id ID] [--resume]
|
||||||
scripts/test-e2e [--scenario NAME|all] [--run-id ID] [--resume]
|
scripts/test-e2e [--scenario NAME|all] [--run-id ID] [--resume]
|
||||||
scripts/test-env doctor|coverage|status|logs|collect|recover|reuse|stop|reset|purge|gc ...
|
scripts/test-env doctor|coverage|status|logs|collect|recover|reuse|stop|reset|purge|gc ...
|
||||||
scripts/build build|verify (pinned toolchain; host-safe)
|
scripts/build build|verify|doctor|recover|clean (pinned toolchain; host-safe)
|
||||||
scripts/windows/build-test-bundle --run-id ID --config FILE [--ca FILE]
|
scripts/windows/build-test-bundle --run-id ID --config FILE [--ca FILE]
|
||||||
scripts/windows/test-host status|prepare|stage|run|collect|stop|reset|recover
|
scripts/windows/test-host status|prepare|stage|run|collect|stop|reset|recover
|
||||||
```
|
```
|
||||||
@@ -208,6 +208,13 @@ exact `.test-runs/<run-id>` directory, with source-commit and SHA-256 manifest;
|
|||||||
it refuses replacement rather than overwriting an earlier bundle. This is not a
|
it refuses replacement rather than overwriting an earlier bundle. This is not a
|
||||||
release publisher: signing, version resources, and public checksum publication
|
release publisher: signing, version resources, and public checksum publication
|
||||||
remain Phase 8 gates.
|
remain Phase 8 gates.
|
||||||
|
It must report the exact RVBox toolchain image/cache/artifact footprint and
|
||||||
|
provide a dry-run-first cleanup that can remove only the ignored `bin/` output,
|
||||||
|
the exact toolchain image, and its two named Go-cache volumes. Cleanup requires
|
||||||
|
`--execute` plus interactive confirmation (or CI `--yes`), refuses images used
|
||||||
|
by containers, never touches `.test-runs/`, and never invokes a global Docker
|
||||||
|
prune. `scripts/build recover` must rebuild the toolchain image and binaries
|
||||||
|
after any or all of those owned resources were cleared.
|
||||||
|
|
||||||
The first integration/E2E invocation creates a filesystem-safe random run ID,
|
The first integration/E2E invocation creates a filesystem-safe random run ID,
|
||||||
or validates an explicitly supplied one, and records
|
or validates an explicitly supplied one, and records
|
||||||
|
|||||||
@@ -21,6 +21,22 @@ Build all supported binaries without installing `make` or Go on the host:
|
|||||||
scripts/build build
|
scripts/build build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`scripts/build doctor` reports the exact owned builder image, Go cache volumes,
|
||||||
|
and ignored `bin/` artifact directory. To reclaim space, cleanup is dry-run by
|
||||||
|
default and never performs a global Docker prune:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/build clean --all
|
||||||
|
scripts/build clean --all --execute --yes
|
||||||
|
scripts/build recover
|
||||||
|
```
|
||||||
|
|
||||||
|
The second command removes only `bin/`, `rvbox-dev-toolchain:latest`, and the
|
||||||
|
two named RVBox Go-cache volumes. `recover` rebuilds the pinned toolchain and
|
||||||
|
all binaries from source. It intentionally does not remove `.test-runs/`, which
|
||||||
|
may contain resumable environments; use the exact-run `scripts/test-env purge`
|
||||||
|
workflow for those.
|
||||||
|
|
||||||
For a native Windows run, create the immutable per-run test bundle with the
|
For a native Windows run, create the immutable per-run test bundle with the
|
||||||
pinned toolchain. Supply a test-specific config whose endpoint and CA path are
|
pinned toolchain. Supply a test-specific config whose endpoint and CA path are
|
||||||
valid for that run; the command refuses to replace an existing bundle.
|
valid for that run; the command refuses to replace an existing bundle.
|
||||||
|
|||||||
+148
-14
@@ -3,20 +3,154 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
target=${1:-build}
|
compose_file=$repo_root/deploy/compose.yaml
|
||||||
shift || true
|
toolchain_image=rvbox-dev-toolchain:latest
|
||||||
|
cache_volumes='rvbox-dev_rvbox-go-build rvbox-dev_rvbox-go-mod'
|
||||||
|
|
||||||
case $target in
|
usage() {
|
||||||
build|verify) ;;
|
cat <<'EOF'
|
||||||
*)
|
usage: scripts/build build|verify|doctor|recover|clean [options]
|
||||||
printf '%s\n' 'usage: scripts/build [build|verify]' >&2
|
|
||||||
exit 2
|
build build supported binaries in the pinned toolchain
|
||||||
;;
|
verify run the normal pinned verification gate
|
||||||
esac
|
doctor read-only toolchain/artifact/cache availability report
|
||||||
[ "$#" -eq 0 ] || {
|
recover rebuild a missing/broken toolchain image, then build
|
||||||
printf '%s\n' 'scripts/build does not accept additional arguments' >&2
|
clean print owned cleanup targets; no deletion without --execute
|
||||||
exit 2
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
cd "$repo_root"
|
fail() { printf '%s\n' "build: $*" >&2; exit 2; }
|
||||||
exec docker compose -f deploy/compose.yaml run --rm toolchain make "$target"
|
|
||||||
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user