Files
computer-playbook/scripts/tests/deploy/local/utils/cache-retry.sh
T
kevinveenbirkenbachandClaude Opus 4.7 eb4bcd23a8 refactor(env/spot): forbid .get() defaults + cache namespace rename
Three new lint tests close gaps in the INFINITO_* SPOT contract:
* tests/lint/filesystem/env/test_default_env_keys_handled.py
  Every default.env key must be wired through a handler.
* tests/lint/filesystem/env/test_used_keys_declared.py
  Every INFINITO_* in .sh/.py must live in env/{default,ci}.env or
  a handler; per-line `# nocheck:` suppresses runtime-only refs.
* tests/lint/filesystem/python/test_no_infinito_get_defaults.py
  Forbid ANY default in `.get("INFINITO_*", ...)` -- including the
  empty-string fallback (which silently masks an unset key). The
  nocheck check walks every physical line the call spans so an
  autoformat-split call still honours the marker.

Cache namespace flattens to a single prefix:
  INFINITO_PACKAGE_CACHE_*  -> INFINITO_CACHE_PACKAGE_*
  INFINITO_REGISTRY_CACHE_* -> INFINITO_CACHE_REGISTRY_*
The five previously driver-injected paths (APT_LIST, NPMRC,
PIP_CONF, REGISTRY_PROXY_CONF, FRONTEND_CA_FILE) move into
env/default.env and passthrough.STATIC_KEYS; the
`cache_env_overrides()` helper and its two call sites are gone
along with the test-fixture override for FRONTEND_CA_DIR.

SPOT-strict rewrites:
  - 19 sites: `something.get("INFINITO_X", "")` -> subscript
    `something["INFINITO_X"]` (loud KeyError when env loader did
    not run; all keys live in env/default.env).
  - 2 handler-internal bootstrap reads suppressed with
    `# nocheck: handler-bootstrap-read`.
  - 30 runtime-only / driver-injected / test-fixture references
    annotated with `# nocheck: <reason>` per-line.
  - Unit tests that previously removed SPOT-guaranteed keys via
    `{k: v ... if k \!= KEY}` or `clear=True` switch to setting
    the key to "" explicitly, matching the new contract.

Side fixes:
  - INFINITO_DNS_DOMAIN + INFINITO_DNS_HOSTS_FILE move from
    shell-side defaults in common.sh to env/default.env and
    passthrough.STATIC_KEYS.
  - dns_systemd_is_operational() drops the dead
    INFINITO_DNS_FORCE_HOSTS_FALLBACK override branch (the
    variable was never set anywhere).
  - INFINITO_WORKER_FETCH switches from `os.environ.get(KEY, "1")`
    to `os.environ[KEY]`.
  - scripts/tests/code/wrapper.sh exec args move into a bash
    array so `# nocheck:` on INFINITO_TEST_TYPE can live on its
    own element line (SC2215 fix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 22:26:16 +02:00

123 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# shellcheck shell=bash
#
# Wraps a command and, on apt "Release expired" / "Valid-Until expired"
# failure, invalidates the Nexus apt-proxy caches via REST and re-runs once.
# Usage: deploy_with_cache_retry "<label>" -- <cmd> [args...]
_CACHE_RETRY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
_CACHE_RETRY_REPO_ROOT="$(cd "${_CACHE_RETRY_DIR}/../../../../.." && pwd)"
_cache_retry_stale_pattern='Release file .*is expired|Valid-Until.*expired'
# Used when the Nexus API discovery call fails; mirrors scripts/docker/cache/package.sh.
_CACHE_RETRY_FALLBACK_REPOS=(apt-debian apt-debian-security apt-ubuntu apt-ubuntu-security)
_cache_retry_nexus_invalidate() {
local repo="$1"
local rest="http://127.0.0.1:8081/service/rest/v1/repositories/${repo}/invalidate-cache"
local code
code="$(docker exec infinito-package-cache curl -sS -o /dev/null -w '%{http_code}' \
-u "admin:${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}" \
-X POST "${rest}" 2>/dev/null || echo 000)"
if [[ "${code}" =~ ^2[0-9][0-9]$ ]]; then
echo "[cache-retry] invalidate-cache OK (${code}): ${repo}"
else
echo "[cache-retry] WARN: invalidate-cache failed (${code}) for ${repo} — continuing"
fi
}
_cache_retry_list_apt_proxy_repos() {
local json
json="$(docker exec infinito-package-cache curl -sS \
-u "admin:${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}" \
"http://127.0.0.1:8081/service/rest/v1/repositories" 2>/dev/null)" || return 1
[[ -z "${json}" ]] && return 1
printf '%s' "${json}" |
"${PYTHON:-python3}" "${_CACHE_RETRY_REPO_ROOT}/utils/nexus/list_apt_proxy_repos.py" \
2>/dev/null ||
return 1
return 0
}
cache_retry_recover() {
# Defensive re-source: helper may be invoked outside the wrapping deploy script.
# shellcheck disable=SC1091 # path is runtime-resolved through REPO_ROOT
source "${REPO_ROOT:-${PWD}}/scripts/meta/env/load.sh"
local repos=()
local discovered
if discovered="$(_cache_retry_list_apt_proxy_repos)"; then
while IFS= read -r _line; do
[[ -n "${_line}" ]] && repos+=("${_line}")
done <<<"${discovered}"
echo "[cache-retry] discovered apt-proxy repos from Nexus: ${repos[*]}"
else
repos=("${_CACHE_RETRY_FALLBACK_REPOS[@]}")
echo "[cache-retry] WARN: could not query Nexus — using fallback list: ${repos[*]}"
fi
local repo
for repo in "${repos[@]}"; do
echo "[cache-retry] Nexus invalidate-cache: ${repo}"
_cache_retry_nexus_invalidate "${repo}"
done
echo "[cache-retry] docker builder prune -af..."
docker builder prune -af >/dev/null 2>&1 || true
echo "[cache-retry] docker image prune -af..."
docker image prune -af >/dev/null 2>&1 || true
}
# Toggle errexit around the pipeline so a wrapped non-zero exit reaches the caller
# instead of killing the script before the stale-cache match is evaluated.
_cache_retry_run_capture() {
local log="$1"
shift
__cache_retry_rc=0
local _prev_e=0
[[ $- == *e* ]] && _prev_e=1
set +e
"$@" 2>&1 | tee "${log}"
__cache_retry_rc=${PIPESTATUS[0]}
[[ "${_prev_e}" -eq 1 ]] && set -e
return 0
}
deploy_with_cache_retry() {
local label="${1:-deploy}"
shift
if [[ "${1:-}" == "--" ]]; then shift; fi
local log sanitized
sanitized="${label//[^A-Za-z0-9._-]/-}"
log="$(mktemp -t "deploy-${sanitized:0:80}.XXXXXX.log")"
_cache_retry_run_capture "${log}" "$@"
local rc=${__cache_retry_rc}
if [[ "${rc}" -eq 0 ]]; then
rm -f "${log}"
return 0
fi
if ! grep -qE "${_cache_retry_stale_pattern}" "${log}" 2>/dev/null; then
rm -f "${log}"
return "${rc}"
fi
echo
echo "[cache-retry] Stale apt Release file detected in '${label}' output."
echo "[cache-retry] Invalidating Nexus apt-proxy caches, pruning Docker caches, re-running once."
cache_retry_recover
echo
echo "[cache-retry] Re-running: ${label}"
: >"${log}"
_cache_retry_run_capture "${log}" "$@"
rc=${__cache_retry_rc}
rm -f "${log}"
return "${rc}"
}