96 lines
4.5 KiB
Bash
Executable File
96 lines
4.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ci-test-review :: deterministic sweep (NO AI)
|
|
# -------------------------------------------------------------------------
|
|
# Runs the real cc-ci harness (runner/run_recipe_ci.py) across all enrolled
|
|
# recipes on the cc-ci VM, full suite per recipe, and emits:
|
|
# - a per-recipe status table to stdout
|
|
# - a machine-readable JSON summary to $OUT (default /srv/cc-ci/.cc-ci-logs/)
|
|
# Full per-recipe logs are kept ON cc-ci under /root/cc-ci-review-logs/<runid>/
|
|
# so the AI diagnosis step can read them via `ssh cc-ci`.
|
|
#
|
|
# This script makes NO pass/fail judgement of its own — it records the
|
|
# harness's exit code (0=green, 1=fail) and its RUN SUMMARY block verbatim.
|
|
# AI diagnosis/classification happens AFTER, per SKILL.md.
|
|
#
|
|
# Env knobs (all optional):
|
|
# RECIPES="ghost n8n" limit to these recipes (default: all enrolled)
|
|
# STAGES="install" pass through to harness (default: full suite)
|
|
# QUICK=1 add --quick fast lane (default: cold/authoritative)
|
|
# SSH=cc-ci ssh host alias (default: cc-ci)
|
|
# OUT=/path/dir local dir for the JSON summary (default .cc-ci-logs)
|
|
set -o errexit -o nounset -o pipefail
|
|
|
|
SSH="${SSH:-cc-ci}"
|
|
OUT="${OUT:-/srv/cc-ci/.cc-ci-logs}"
|
|
RUNID="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
REMOTE_LOGDIR="/root/cc-ci-review-logs/${RUNID}"
|
|
SUMMARY="${OUT}/ci-test-review-${RUNID}.json"
|
|
mkdir -p "$OUT"
|
|
|
|
resolve_remote_root() {
|
|
ssh "$SSH" "for d in '${REMOTE_ROOT:-/root/builder-clone}' /root/cc-ci; do [ -d \"\$d/tests\" ] && [ -f \"\$d/runner/run_recipe_ci.py\" ] && { printf '%s' \"\$d\"; exit 0; }; done; exit 1"
|
|
}
|
|
|
|
quick_flag=""; [ "${QUICK:-0}" = "1" ] && quick_flag="--quick"
|
|
stages_env=""; [ -n "${STAGES:-}" ] && stages_env="STAGES='${STAGES}'"
|
|
REMOTE_WORKTREE="$(resolve_remote_root)" || {
|
|
echo "ERROR: could not locate remote cc-ci checkout (tried ${REMOTE_ROOT:-/root/builder-clone} and /root/cc-ci)" >&2
|
|
exit 1
|
|
}
|
|
|
|
echo "ci-test-review sweep ${RUNID} (host=${SSH} root=${REMOTE_WORKTREE} quick=${QUICK:-0} stages=${STAGES:-all})" >&2
|
|
|
|
# 1. Enumerate enrolled recipes (tests/<recipe>/ dirs, minus harness scaffolding).
|
|
if [ -n "${RECIPES:-}" ]; then
|
|
recipes="$RECIPES"
|
|
else
|
|
recipes="$(ssh "$SSH" "cd '${REMOTE_WORKTREE}/tests' 2>/dev/null && ls -d */ 2>/dev/null" \
|
|
| sed 's#/##' | grep -vE '^(_generic|unit|__pycache__)$' || true)"
|
|
fi
|
|
[ -n "$recipes" ] || { echo "no enrolled recipes found" >&2; exit 1; }
|
|
echo "recipes: $(echo $recipes | tr '\n' ' ')" >&2
|
|
|
|
ssh "$SSH" "mkdir -p '$REMOTE_LOGDIR'"
|
|
|
|
# 2. Run the harness per recipe (deterministic). Capture exit code + RUN SUMMARY + version.
|
|
printf '%-18s %-7s %s\n' "RECIPE" "VERDICT" "TIERS (from harness RUN SUMMARY)"
|
|
printf '%-18s %-7s %s\n' "------" "-------" "--------------------------------"
|
|
|
|
json_items=""
|
|
for r in $recipes; do
|
|
log="${REMOTE_LOGDIR}/${r}.log"
|
|
# Real harness invocation (real abra inside). Tee full log on cc-ci; capture rc.
|
|
rc=0
|
|
ssh "$SSH" "cd '${REMOTE_WORKTREE}' && ${stages_env} RECIPE='${r}' cc-ci-run runner/run_recipe_ci.py ${quick_flag} >'${log}' 2>&1" || rc=$?
|
|
# Pull back the RUN SUMMARY block + the abra-checked-out recipe version, if recorded.
|
|
summary_block="$(ssh "$SSH" "awk '/===== RUN SUMMARY =====/{f=1} f{print}' '${log}' 2>/dev/null | sed 's/^/ /'" )"
|
|
tiers="$(ssh "$SSH" "awk '/===== RUN SUMMARY =====/{f=1} f&&/^ (install|upgrade|backup|restore|custom)/{gsub(/^ /,\"\");print}' '${log}' 2>/dev/null" | tr '\n' ';')"
|
|
version="$(ssh "$SSH" "grep -oE 'checked out [^ ]+ at [^ ]+|recipe version[: ]+[^ ]+|VERSION=[^ ]+' '${log}' 2>/dev/null | head -1" || true)"
|
|
verdict=GREEN; [ "$rc" = "0" ] || verdict=FAIL
|
|
printf '%-18s %-7s %s\n' "$r" "$verdict" "${tiers:-<no summary — see log>}"
|
|
# accumulate JSON (escape quotes/newlines minimally)
|
|
esc_tiers="$(printf '%s' "$tiers" | sed 's/"/\\"/g')"
|
|
esc_ver="$(printf '%s' "$version" | sed 's/"/\\"/g')"
|
|
json_items="${json_items}${json_items:+,}
|
|
{\"recipe\":\"${r}\",\"rc\":${rc},\"verdict\":\"${verdict}\",\"tiers\":\"${esc_tiers}\",\"version\":\"${esc_ver}\",\"log\":\"${SSH}:${log}\"}"
|
|
done
|
|
|
|
# 3. JSON summary for the AI diagnosis step.
|
|
cat > "$SUMMARY" <<JSON
|
|
{
|
|
"runid": "${RUNID}",
|
|
"host": "${SSH}",
|
|
"quick": ${QUICK:-0},
|
|
"stages": "${STAGES:-all}",
|
|
"remote_logdir": "${SSH}:${REMOTE_LOGDIR}",
|
|
"recipes": [${json_items}
|
|
]
|
|
}
|
|
JSON
|
|
|
|
echo >&2
|
|
echo "JSON summary : ${SUMMARY}" >&2
|
|
echo "full logs : ${SSH}:${REMOTE_LOGDIR}/<recipe>.log (read via ssh for diagnosis)" >&2
|
|
# Exit non-zero if any recipe failed, so a caller can branch; AI still diagnoses either way.
|
|
grep -q '"verdict":"FAIL"' "$SUMMARY" && exit 2 || exit 0
|