diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..d25b7f7 --- /dev/null +++ b/run.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# +# run.sh — End-to-end IEC 62443-3-3 SL2 compliance test runner +# +# Orchestrates three phases: +# 1. Run ansible-playbook against the inventory (all FR suites) +# 2. Find the latest JSON report in reports/ +# 3. Render it with Python (or Go, or fallback Python one-liner) +# +# Usage: +# ./run.sh # runs against all hosts +# ./run.sh --limit plc-rack01 -K # single host, ask sudo password +# ./run.sh --limit localhost -K # test locally +# +# Environment: +# INVENTORY — path to inventory file (default: ./inventory.ini) +# LIMIT — ansible --limit pattern (default: all) +# +# All extra arguments are forwarded to ansible-playbook: +# ./run.sh -vvv --limit localhost +# +# Output: +# reports/-.json — raw JSON test data +# stdout — formatted report (terminal or md) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INVENTORY="${INVENTORY:-$SCRIPT_DIR/inventory.ini}" +LIMIT="${LIMIT:-all}" + +echo "=== IEC 62443-3-3 SL2 Compliance Validation ===" +echo "" + +# ── Phase 1: Run Ansible tests ────────────────────────────────── +echo "[1/3] Running compliance tests..." +ansible-playbook -i "$INVENTORY" "$SCRIPT_DIR/playbooks/site.yml" \ + --limit "$LIMIT" \ + "$@" + +# ── Phase 2: Find latest report ───────────────────────────────── +REPORT_DIR="$SCRIPT_DIR/reports" +LATEST_JSON=$(ls -t "$REPORT_DIR"/*.json 2>/dev/null | head -1) + +if [ -z "$LATEST_JSON" ]; then + echo "" + echo "✗ No JSON report generated. Check Ansible output above." + exit 1 +fi + +echo "" +echo "[2/3] Latest report: $(basename "$LATEST_JSON")" + +# ── Phase 3: Render with Go template ──────────────────────────── +echo "[3/3] Rendering report with Go template..." +echo "" + +cd "$REPORT_DIR" +if ! go run render.go "$LATEST_JSON" report.gohtml 2>/dev/null; then + # Fallback: if Go isn't available, just cat the JSON + echo "---" + echo "(Go not available; showing raw JSON summary)" + python3 -c " +import json, sys +with open('$(basename "$LATEST_JSON")') as f: + r = json.load(f) +s = r['summary'] +print(f'Total: {s[\"total\"]} | Passed: {s[\"passed\"]} | Failed: {s[\"failed\"]} | Rate: {s[\"passed\"]/s[\"total\"]*100:.1f}%') +print() +for t in r['results']: + icon = '✅' if t['passed'] == True else ('❌' if t['passed'] == False else '🔍') + print(f' {icon} [{t[\"test_id\"]}] {t[\"description\"]}') +print() +print('Failures:') +for f in r['failures']: + print(f' ❌ {f[\"test_id\"]}: {f[\"description\"]} (Severity: {f[\"severity\"]})') + print(f' Expected: {f[\"expected\"]}') + print(f' Actual: {f[\"actual\"]}') + print(f' Fix: {f[\"remediation\"]}') +" 2>/dev/null || cat "$LATEST_JSON" +fi + +echo "" +echo "=== Done ==="