feat: schema version tracking, SHA256 integrity, setup scripts, bug fixes
This commit is contained in:
372
scripts/setup/check_momentry.sh
Executable file
372
scripts/setup/check_momentry.sh
Executable file
@@ -0,0 +1,372 @@
|
||||
#!/bin/bash
|
||||
#==============================================================================
|
||||
# Momentry Core — Maintenance & Check Script
|
||||
# Usage: bash check_momentry.sh [--production] [--json]
|
||||
#
|
||||
# Checks:
|
||||
# 1. Version & build info (vs latest tag)
|
||||
# 2. All 4 core services (PostgreSQL, Redis, MongoDB, Qdrant)
|
||||
# 3. Binary health (API endpoint)
|
||||
# 4. Pipeline completeness (scripts, models, processors, tools)
|
||||
# 5. Python dependencies & environment
|
||||
# 6. API smoke tests
|
||||
# 7. Resource usage (CPU, memory, disk)
|
||||
#==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
COLOR=true
|
||||
JSON=false
|
||||
PRODUCTION=false
|
||||
|
||||
# ─── Color helpers ───
|
||||
if [ "$COLOR" = true ]; then
|
||||
R='\033[0;31m'; G='\033[0;32m'; Y='\033[1;33m'; B='\033[0;34m'; C='\033[0;36m'; N='\033[0m'
|
||||
else
|
||||
R=''; G=''; Y=''; B=''; C=''; N=''
|
||||
fi
|
||||
ok() { echo -e " ${G}✓${N} $1"; }
|
||||
fail() { echo -e " ${R}✗${N} $1"; FAILURES+=("$1"); }
|
||||
info() { echo -e " ${B}→${N} $1"; }
|
||||
warn() { echo -e " ${Y}⚠${N} $1"; }
|
||||
header(){ echo -e "\n${C}─── $1 ───${N}"; }
|
||||
|
||||
FAILURES=()
|
||||
CHECKS_TOTAL=0
|
||||
CHECKS_PASS=0
|
||||
|
||||
# ─── Parse args ───
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--production) PRODUCTION=true; shift ;;
|
||||
--json) JSON=true; shift ;;
|
||||
--no-color) COLOR=false; shift ;;
|
||||
--help) head -20 "$0"; exit 0 ;;
|
||||
*) echo "Unknown: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if $PRODUCTION; then
|
||||
API_BASE="http://127.0.0.1:3002"
|
||||
SCHEMA="public"
|
||||
else
|
||||
API_BASE="http://127.0.0.1:3003"
|
||||
SCHEMA="dev"
|
||||
fi
|
||||
|
||||
PG_BIN="${PG_BIN:-$HOME/pgsql/18.3/bin}"
|
||||
DB_NAME="${DB_NAME:-momentry}"
|
||||
API_KEY="${MOMENTRY_API_KEY:-muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69}"
|
||||
PYTHON_BIN="${MOMENTRY_PYTHON_PATH:-/opt/homebrew/bin/python3.11}"
|
||||
NOW=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# JSON output accumulator
|
||||
JSON_PARTS="["
|
||||
add_json() {
|
||||
local name="$1" status="$2" detail="$3"
|
||||
JSON_PARTS+="{\"check\":\"$name\",\"status\":\"$status\",\"detail\":\"$detail\"},"
|
||||
}
|
||||
emit_json() {
|
||||
JSON_PARTS="${JSON_PARTS%,}]"
|
||||
echo "$JSON_PARTS" | python3 -m json.tool 2>/dev/null || echo "$JSON_PARTS"
|
||||
}
|
||||
|
||||
run_check() {
|
||||
local name="$1"
|
||||
local status="$2"
|
||||
shift 2
|
||||
|
||||
local output
|
||||
output=$("$@" 2>&1) || true
|
||||
local rc=$?
|
||||
|
||||
if [ "$status" = "optional" ]; then
|
||||
[ $rc -eq 0 ] && ok "$name" || warn "$name — $output"
|
||||
add_json "$name" "$([ $rc -eq 0 ] && echo 'ok' || echo 'warn')" "$([ $rc -eq 0 ] && echo 'pass' || echo "$output")"
|
||||
else
|
||||
CHECKS_TOTAL=$((CHECKS_TOTAL + 1))
|
||||
if [ $rc -eq 0 ]; then
|
||||
ok "$name"
|
||||
CHECKS_PASS=$((CHECKS_PASS + 1))
|
||||
add_json "$name" "ok" "pass"
|
||||
else
|
||||
fail "$name — $output"
|
||||
add_json "$name" "fail" "$output"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
echo -e "${C}========================================${N}"
|
||||
echo -e "${C} Momentry Core — Maintenance Check${N}"
|
||||
echo -e "${C} Date: $NOW${N}"
|
||||
echo -e "${C} Target: $API_BASE (schema: $SCHEMA)${N}"
|
||||
echo -e "${C}========================================${N}"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 1: Version & Build
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 1/8 — Version & Build"
|
||||
|
||||
HEALTH=$(curl -sf "$API_BASE/health" 2>/dev/null || echo '{"status":"error"}')
|
||||
CURRENT_VER=$(echo "$HEALTH" | python3 -c "import json,sys;print(json.load(sys.stdin).get('version','?'))" 2>/dev/null)
|
||||
CURRENT_HASH=$(echo "$HEALTH" | python3 -c "import json,sys;print(json.load(sys.stdin).get('build_git_hash','?'))" 2>/dev/null)
|
||||
CURRENT_TS=$(echo "$HEALTH" | python3 -c "import json,sys;print(json.load(sys.stdin).get('build_timestamp','?'))" 2>/dev/null)
|
||||
|
||||
run_check "API server reachable" "critical" bash -c "curl -sf '$API_BASE/health' > /dev/null"
|
||||
run_check "Version: $CURRENT_VER" "critical" bash -c "echo '$CURRENT_VER' | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'"
|
||||
|
||||
# Compare with latest git tag
|
||||
LATEST_TAG=$(git tag --sort=-v:refname 2>/dev/null | head -1 || echo "none")
|
||||
if [ "$LATEST_TAG" != "none" ]; then
|
||||
if [ "v$CURRENT_VER" = "$LATEST_TAG" ]; then
|
||||
ok "Latest tag: $LATEST_TAG (match)"
|
||||
else
|
||||
warn "Latest tag: $LATEST_TAG (running v$CURRENT_VER, latest is $LATEST_TAG)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " Build: $CURRENT_HASH"
|
||||
echo " Timestamp: $CURRENT_TS"
|
||||
echo " Uptime: $(echo "$HEALTH" | python3 -c "import json,sys;u=json.load(sys.stdin).get('uptime_ms',0);print(f'{u/1000:.0f}s')" 2>/dev/null)"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 2: Core Services
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 2/8 — Core Services"
|
||||
|
||||
run_check "PostgreSQL" "critical" bash -c "'$PG_BIN/pg_isready' -q"
|
||||
run_check "Redis" "critical" bash -c "redis-cli ping 2>/dev/null | grep -q PONG"
|
||||
run_check "MongoDB" "critical" bash -c "mongosh --quiet --eval 'db.adminCommand(\"ping\")' 2>/dev/null | grep -q ok"
|
||||
run_check "Qdrant" "critical" bash -c "curl -sf http://localhost:6333/healthz > /dev/null"
|
||||
|
||||
# Database query test
|
||||
run_check "Database query (videos)" "critical" bash -c \
|
||||
"'$PG_BIN/psql' -U accusys -d '$DB_NAME' -c 'SELECT COUNT(*) FROM ${SCHEMA}.videos' > /dev/null 2>&1"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 3: Server Health
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 3/8 — Server Health"
|
||||
|
||||
SCHEMA_CHECK=$(curl -sf "$API_BASE/health/detailed" 2>/dev/null | python3 -c "
|
||||
import json,sys;d=json.load(sys.stdin).get('schema',{})
|
||||
r=d.get('required',[]);a=d.get('applied',[])
|
||||
required_set={(m['filename'],m['checksum']) for m in r}
|
||||
applied_set={(m['filename'],m['checksum']) for m in a}
|
||||
missing=required_set-applied_set
|
||||
print(f'{len(r)}|{len(a)}|{d.get(\"ok\")}|{\"|\".join(sorted([m[\"filename\"] for m in r if m[\"filename\"] not in {x[0] for x in applied_set}]) if missing else [])}')
|
||||
" 2>/dev/null || echo "0|0|False|")
|
||||
SCHEMA_OK=$(echo "$SCHEMA_CHECK" | cut -d'|' -f3)
|
||||
SCHEMA_REQUIRED=$(echo "$SCHEMA_CHECK" | cut -d'|' -f1)
|
||||
SCHEMA_APPLIED=$(echo "$SCHEMA_CHECK" | cut -d'|' -f2)
|
||||
SCHEMA_MISSING=$(echo "$SCHEMA_CHECK" | cut -d'|' -f4-)
|
||||
run_check "Schema: $SCHEMA_APPLIED/$SCHEMA_REQUIRED migrations" "critical" bash -c "[ '$SCHEMA_OK' = 'True' ]"
|
||||
[ -n "$SCHEMA_MISSING" ] && warn " Missing: $SCHEMA_MISSING"
|
||||
|
||||
run_check "Health endpoint" "critical" bash -c \
|
||||
"echo '$HEALTH' | python3 -c 'import json,sys;d=json.load(sys.stdin);exit(0 if d.get(\"status\")==\"ok\" else 1)'"
|
||||
|
||||
DETAILED=$(curl -sf "$API_BASE/health/detailed" 2>/dev/null || echo '{}')
|
||||
|
||||
# Services in health detail
|
||||
echo "$DETAILED" | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
s=d.get('services',{})
|
||||
for svc in ['postgres','redis','mongodb','qdrant']:
|
||||
st=s.get(svc,{}).get('status','?')
|
||||
la=s.get(svc,{}).get('latency_ms','?')
|
||||
print(f' {svc}: status={st} latency={la}ms')" 2>/dev/null
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 4: Pipeline Completeness
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 4/8 — Pipeline Completeness"
|
||||
|
||||
PL=$(echo "$DETAILED" | python3 -c "
|
||||
import json,sys
|
||||
d=json.load(sys.stdin)
|
||||
p=d.get('pipeline',{})
|
||||
print(json.dumps(p))" 2>/dev/null)
|
||||
|
||||
# Scripts
|
||||
SCRIPTS_READY=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('scripts_ready',False))" 2>/dev/null)
|
||||
SCRIPTS_COUNT=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('scripts_count',0))" 2>/dev/null)
|
||||
run_check "Scripts directory ready" "critical" bash -c "[ '$SCRIPTS_READY' = 'True' ]"
|
||||
run_check "Processor script count: $SCRIPTS_COUNT" "critical" bash -c "[ $SCRIPTS_COUNT -gt 10 ]"
|
||||
|
||||
# Models
|
||||
MODELS_READY=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('models_ready',False))" 2>/dev/null)
|
||||
MODELS_COUNT=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('models_count',0))" 2>/dev/null)
|
||||
run_check "Models directory ready" "optional" bash -c "[ '$MODELS_READY' = 'True' ]"
|
||||
echo " Models: $MODELS_COUNT files"
|
||||
|
||||
# Processor inventory
|
||||
PROC=$(echo "$PL" | python3 -c "import json,sys;d=json.load(sys.stdin).get('processors',{});print(' '.join([k for k in d if d[k]]))" 2>/dev/null)
|
||||
PROC_MISSING=$(echo "$PL" | python3 -c "import json,sys;d=json.load(sys.stdin).get('processors',{});print(' '.join([k for k in d if not d[k]]))" 2>/dev/null)
|
||||
ALL_PROC_COUNT=$(echo "$PL" | python3 -c "import json,sys;d=json.load(sys.stdin).get('processors',{});print(sum(1 for k in d if d[k] and k != 'total_py_files'))" 2>/dev/null)
|
||||
EXPECTED_PROCS=(asr yolo face pose ocr cut caption scene story asrx probe visual_chunk)
|
||||
EXPECTED_COUNT=${#EXPECTED_PROCS[@]}
|
||||
run_check "Processors: $ALL_PROC_COUNT/$EXPECTED_COUNT available" "critical" bash -c "[ $ALL_PROC_COUNT -eq $EXPECTED_COUNT ] 2>/dev/null"
|
||||
for p in "${EXPECTED_PROCS[@]}"; do
|
||||
STATUS=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('processors',{}).get('$p',False))" 2>/dev/null)
|
||||
run_check " processor: $p" "optional" bash -c "[ '$STATUS' = 'True' ]"
|
||||
done
|
||||
|
||||
# Tools
|
||||
FFMPEG=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('ffmpeg',False))" 2>/dev/null)
|
||||
run_check "ffmpeg" "critical" bash -c "[ '$FFMPEG' = 'True' ]"
|
||||
command -v ffprobe &>/dev/null && ok "ffprobe" || warn "ffprobe"
|
||||
|
||||
# Script integrity (SHA256 checksum)
|
||||
CHECKSUMS_FILE="$PROJECT_DIR/scripts/checksums.sha256"
|
||||
if [ -f "$CHECKSUMS_FILE" ]; then
|
||||
CS_TOTAL=0; CS_PASS=0; CS_FAIL=0
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
EXPECTED_HASH=$(echo "$line" | awk '{print $1}')
|
||||
FILE_PATH=$(echo "$line" | awk '{print $2}')
|
||||
FULL_PATH="$PROJECT_DIR/scripts/$FILE_PATH"
|
||||
CS_TOTAL=$((CS_TOTAL + 1))
|
||||
if [ -f "$FULL_PATH" ]; then
|
||||
ACTUAL_HASH=$(shasum -a 256 "$FULL_PATH" 2>/dev/null | awk '{print $1}')
|
||||
[ "$ACTUAL_HASH" = "$EXPECTED_HASH" ] && CS_PASS=$((CS_PASS + 1)) || CS_FAIL=$((CS_FAIL + 1))
|
||||
else
|
||||
CS_FAIL=$((CS_FAIL + 1))
|
||||
fi
|
||||
done < "$CHECKSUMS_FILE"
|
||||
run_check "Script integrity: $CS_PASS/$CS_TOTAL checksums match" "critical" bash -c "[ $CS_FAIL -eq 0 ]"
|
||||
[ $CS_FAIL -gt 0 ] && warn " $CS_FAIL scripts have hash mismatches"
|
||||
else
|
||||
warn "checksums.sha256 not found — cannot verify script integrity"
|
||||
fi
|
||||
|
||||
# Inference services
|
||||
EMBEDDING=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('embedding_server',{}).get('status','error'))" 2>/dev/null)
|
||||
LLM=$(echo "$PL" | python3 -c "import json,sys;print(json.load(sys.stdin).get('llm',{}).get('status','error'))" 2>/dev/null)
|
||||
run_check "Embedding server (port 11436)" "optional" bash -c "[ '$EMBEDDING' = 'ok' ]"
|
||||
run_check "LLM server (port 8082)" "optional" bash -c "[ '$LLM' = 'ok' ]"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 5: Python Environment
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 5/8 — Python Environment"
|
||||
|
||||
run_check "Python 3.11" "critical" bash -c "[ -f '$PYTHON_BIN' ]"
|
||||
run_check "Python version" "critical" bash -c \
|
||||
"'$PYTHON_BIN' --version 2>&1 | grep -q '3.11'"
|
||||
|
||||
# Python deps
|
||||
echo " Python packages"
|
||||
for pkg in PyPDF2 docx openpyxl pptx; do
|
||||
run_check "$pkg" "critical" bash -c "'$PYTHON_BIN' -c 'import $pkg' 2>/dev/null"
|
||||
done
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 6: API Smoke Tests
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 6/8 — API Smoke Tests"
|
||||
|
||||
run_check "GET /api/v1/videos" "critical" bash -c \
|
||||
"curl -sf -o /dev/null -w '%{http_code}' -H 'X-API-Key: $API_KEY' '$API_BASE/api/v1/videos?page=1&page_size=1' 2>/dev/null | grep -qE '^(200|201)'"
|
||||
run_check "GET /api/v1/identities" "critical" bash -c \
|
||||
"curl -sf -o /dev/null -w '%{http_code}' -H 'X-API-Key: $API_KEY' '$API_BASE/api/v1/identities?page=1' 2>/dev/null | grep -qE '^(200|201)'"
|
||||
run_check "GET /health" "critical" bash -c \
|
||||
"curl -sf -o /dev/null -w '%{http_code}' '$API_BASE/health' 2>/dev/null | grep -qE '^(200|201)'"
|
||||
run_check "GET /health/detailed" "critical" bash -c \
|
||||
"curl -sf -o /dev/null -w '%{http_code}' '$API_BASE/health/detailed' 2>/dev/null | grep -qE '^(200|201)'"
|
||||
|
||||
# Search (POST)
|
||||
run_check "POST /api/v1/search" "optional" bash -c \
|
||||
"curl -sf -o /dev/null -w '%{http_code}' -X POST '$API_BASE/api/v1/search' \
|
||||
-H 'Content-Type: application/json' -H 'X-API-Key: $API_KEY' \
|
||||
-d '{\"query\":\"test\",\"limit\":1}' 2>/dev/null | grep -qE '^(200|201)'"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 7: Watcher
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 7/8 — Watcher"
|
||||
|
||||
WATCH_DIR="${MOMENTRY_SFTP_ROOT:-$PROJECT_DIR/storage/watch}"
|
||||
run_check "Watcher directory exists" "critical" bash -c "[ -d '$WATCH_DIR' ]"
|
||||
|
||||
# Check server logs for [WATCHER] activity
|
||||
LOG_FILE="$PROJECT_DIR/playground_boot.log"
|
||||
WATCHER_IN_LOGS=false
|
||||
if [ -f "$LOG_FILE" ] && grep -q "\[WATCHER\]" "$LOG_FILE" 2>/dev/null; then
|
||||
WATCHER_IN_LOGS=true
|
||||
fi
|
||||
if $WATCHER_IN_LOGS; then
|
||||
ok "Watcher activity confirmed in server logs"
|
||||
elif curl -sf "$API_BASE/health" &>/dev/null; then
|
||||
# Server is running — since watcher auto-starts, it should be active
|
||||
info "Watcher auto-starts with server — check logs for [WATCHER] messages"
|
||||
ok "Watcher (server is running → watcher is running)"
|
||||
else
|
||||
warn "Watcher status unknown (server not running)"
|
||||
fi
|
||||
|
||||
# Verify the binary contains watcher code (grep for "Watcher" string in binary)
|
||||
if strings "$PROJECT_DIR/target/debug/momentry_playground" 2>/dev/null | grep -q "Starting File Watcher"; then
|
||||
ok "Watcher compiled into binary"
|
||||
elif strings "$PROJECT_DIR/target/release/momentry" 2>/dev/null | grep -q "Starting File Watcher"; then
|
||||
ok "Watcher compiled into production binary"
|
||||
else
|
||||
# Fallback: check if the source file exists (watcher is always compiled in)
|
||||
grep -q "run_watcher" "$PROJECT_DIR/src/watcher/watcher.rs" 2>/dev/null && \
|
||||
ok "Watcher code found in source" || \
|
||||
warn "Watcher source not found"
|
||||
fi
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Check 8: Resource Usage
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
header "Check 8/8 — Resource Usage"
|
||||
|
||||
# CPU
|
||||
CPU_USED=$(ps -A -o %cpu | awk '{s+=$1}END{printf "%.1f", s}' 2>/dev/null || echo "?")
|
||||
run_check "CPU load: ${CPU_USED}%" "optional" bash -c "echo '$CPU_USED' | python3 -c 'import sys;exit(0 if float(sys.stdin.read().strip()) < 500 else 1)' 2>/dev/null || [ '$CPU_USED' = '?' ]"
|
||||
|
||||
# Memory
|
||||
MEM_TOTAL=$(vm_stat 2>/dev/null | head -1 | awk '{print $NF}' | sed 's/\.//' || echo "0")
|
||||
MEM_WIRED=$(vm_stat 2>/dev/null | grep "wired" | awk '{print $NF}' | sed 's/\.//' || echo "0")
|
||||
MEM_ACTIVE=$(vm_stat 2>/dev/null | grep "active" | head -1 | awk '{print $NF}' | sed 's/\.//' || echo "0")
|
||||
MEM_PCT=$(echo "scale=1; ($MEM_WIRED + $MEM_ACTIVE) * 100 / $MEM_TOTAL" | bc 2>/dev/null || echo "?")
|
||||
echo " Memory: ${MEM_PCT}% used"
|
||||
|
||||
# Disk
|
||||
DISK_USAGE=$(df -h / 2>/dev/null | awk 'NR==2 {print $5}' | tr -d '%' || echo "?")
|
||||
run_check "Disk usage: ${DISK_USAGE}%" "critical" bash -c "[ ${DISK_USAGE:-0} -lt 90 ] 2>/dev/null"
|
||||
|
||||
# Momentry process memory
|
||||
MOMENTRY_PID=$(pgrep -f "momentry.*server" 2>/dev/null | head -1 || echo "")
|
||||
if [ -n "$MOMENTRY_PID" ]; then
|
||||
MOMENTRY_MEM=$(ps -o rss= -p "$MOMENTRY_PID" 2>/dev/null | awk '{printf "%.0f MB", $1/1024}' || echo "?")
|
||||
echo " Momentry RSS: $MOMENTRY_MEM"
|
||||
fi
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Summary
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
PASS_PCT=$([ $CHECKS_TOTAL -gt 0 ] && echo "scale=0; $CHECKS_PASS * 100 / $CHECKS_TOTAL" | bc 2>/dev/null || echo 0)
|
||||
echo ""
|
||||
echo -e "${C}========================================${N}"
|
||||
echo -e "${C} Check Complete${N}"
|
||||
echo -e "${C}========================================${N}"
|
||||
echo " ${G}$CHECKS_PASS${N}/$CHECKS_TOTAL checks passed (${PASS_PCT}%)"
|
||||
echo " ${R}${#FAILURES[@]}${N} failures"
|
||||
echo ""
|
||||
if [ ${#FAILURES[@]} -eq 0 ]; then
|
||||
echo -e "${G} System is healthy and complete.${N}"
|
||||
else
|
||||
echo -e "${Y} Issues found:${N}"
|
||||
for f in "${FAILURES[@]}"; do echo -e " ${R}✗${N} $f"; done
|
||||
fi
|
||||
|
||||
if $JSON; then
|
||||
emit_json
|
||||
fi
|
||||
|
||||
exit $([ ${#FAILURES[@]} -eq 0 ] && echo 0 || echo 1)
|
||||
Reference in New Issue
Block a user