From 6fdca86f1bfe8e31d51d54e667b717a9a8e88b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Sun, 19 Jul 2026 04:17:01 +0200 Subject: [PATCH] release 1.13.12: SUMMARY badge rows are not warn/error matches Classify SUMMARY chrome ([ WARN ] 2, warnings only, bare OK/ERROR counts without #NNN/tid) as meta so sticky counts and the warn/error filter only show real checks. Filter context skips blank lines. --- VERSION | 2 +- VERSIONS.md | 1 + site-gen/console_to_html.py | 77 +++++++++++++++++++++++++++++++++---- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/VERSION b/VERSION index 66fae2a..4428455 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.13.11 +1.13.12 diff --git a/VERSIONS.md b/VERSIONS.md index 893099a..f632efc 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -17,6 +17,7 @@ Git tags: `vMAJOR.FEATURE.FIX` (e.g. `v1.8.0`). File `VERSION` omits the `v` pre | Tag | Date (UTC) | Notes | |-----|------------|--------| +| **v1.13.12** | 2026-07-19 | **Bugfix:** SUMMARY chrome (`[ WARN ] 2`, `warnings only`, OK/ERROR counts without `#NNN`) is **meta**, not warn/error — sticky counts + warn filter no longer double SUMMARY rows; filter context skips blank lines. | | **v1.13.11** | 2026-07-19 | **Bugfix:** sticky error/warn filter keeps **±3 dimmed context lines** around each match (gray/grayscale) so phase/group context stays readable; dashed gap between distant clusters. | | **v1.13.10** | 2026-07-19 | **Bugfix:** multi-phase runs print **one global SUMMARY** at parent end (aggregated OK/ERROR/WARN/INFO/BLOCK via `TALER_MON_STATE`); no phase SUMMARY at all (not even last phase); mid-phase still lists ERRORS/BLOCKERS; solo `check_*.sh` unchanged. | | **v1.13.9** | 2026-07-19 | **Bugfix:** no mid-run SUMMARY / progress standings between phases (no more 34/52 + SUMMARY after urls while monpages still runs); full stand only on last phase or solo check script; `progress_finish` remains. | diff --git a/site-gen/console_to_html.py b/site-gen/console_to_html.py index bf4d312..0cccb98 100755 --- a/site-gen/console_to_html.py +++ b/site-gen/console_to_html.py @@ -261,31 +261,64 @@ def strip_ansi(s: str) -> str: return ANSI_RE.sub("", s) +def is_numbered_check_line(line: str) -> bool: + """True for real suite checks (global #NNN and/or tid like www.merchant-09). + + SUMMARY box rows look like `` [ WARN ] 2`` / `` [ WARN ] warnings only`` + and must NOT count as warn/error (v1.13.12). + """ + if re.search(r"#\d{3}\b", line): + return True + if TID_RE.search(line): + return True + return False + + def classify(line: str) -> str: u = line.upper() + low = line.lower() # section / summary headers before badge matching (they contain the word ERROR) if "SUMMARY" in u or "--- ERRORS" in u or "ERRORS ·" in u or "ERRORS (" in u: return "meta" - if "numbered checks" in line.lower() or "totals:" in line.lower(): + if "numbered checks" in low or "totals:" in low: + return "meta" + # SUMMARY verdict chrome (not a check) + if "warnings only" in low or "all clear" in low or "failed — see" in low or "failed - see" in low: return "meta" # host-agent tees converter "wrote … (errors=N …)" into the same log - if line.lstrip().lower().startswith("wrote ") and "errors=" in line.lower(): + if line.lstrip().lower().startswith("wrote ") and "errors=" in low: return "meta" # git pull / reset noise (commit subjects may contain the word ERROR) if re.match(r"\s*HEAD is now at\b", line): return "meta" if "BLOCKER" in u or "┌ BLOCKER" in u: + # SUMMARY may print " [ BLOCK ] 1" without a check id + if not is_numbered_check_line(line) and re.search( + r"[\[┌]\s*BLOCK(?:ER)?\s*[\]┐]", u + ): + return "meta" return "blocker" - # Only real check badges (not free-text "ERROR" inside commit messages) + # Only real check badges (not free-text "ERROR" inside commit messages, + # and not SUMMARY count rows like " [ ERROR ] 3") if re.search(r"┌\s*ERROR\b|\[\s*ERROR\b", u): + if not is_numbered_check_line(line): + return "meta" return "error" if re.search(r"\bERROR monpages\b", line, re.I): return "error" if re.search(r"┌\s*WARN\b|\[\s*WARN\b", u): + if not is_numbered_check_line(line): + return "meta" return "warn" if re.search(r"┌\s*INFO\b|\[\s*INFO\b", u): + if not is_numbered_check_line(line): + return "meta" return "info" - if " OK" in u or "[OK" in u or "┌ OK" in u or u.strip().startswith("OK"): + if re.search(r"┌\s*OK\b|\[\s*OK\b", u) or ( + (" OK" in u or u.strip().startswith("OK")) and is_numbered_check_line(line) + ): + if re.search(r"┌\s*OK\b|\[\s*OK\b", u) and not is_numbered_check_line(line): + return "meta" return "ok" if u.strip().startswith("-- ") or u.strip().startswith("=="): return "section" @@ -394,6 +427,13 @@ def is_summary_error_line(ln: str) -> bool: return True if "totals:" in low: return True + if "warnings only" in low or "all clear" in low: + return True + # SUMMARY count rows without #NNN / tid (e.g. " [ ERROR ] 3") + if re.search(r"[\[\u250c]\s*(ERROR|WARN|OK|INFO|BLOCK)", ln, re.I) and not is_numbered_check_line( + ln + ): + return True # console_to_html "wrote … (errors=N warnings=M level=…)" is teed into the # host-agent log; "errors=" uppercases to "ERRORS=" and must not count as ERROR. if low.lstrip().startswith("wrote ") and "errors=" in low: @@ -1419,20 +1459,41 @@ STICKY_JS = """ if (mode === "warn") return el.classList.contains("warn"); return false; } + function isBlankLine(el) { + var t = (el.textContent || "").replace(/\u00a0/g, " ").trim(); + return t.length === 0; + } // 0 = hide, 1 = match, 2 = context var keep = new Array(lines.length); - var i, d, j, k; + var i, j, k, n, idx; for (i = 0; i < lines.length; i++) keep[i] = 0; for (i = 0; i < lines.length; i++) { if (!isMatch(lines[i])) continue; keep[i] = 1; - for (d = 1; d <= FILTER_CTX; d++) { - if (i - d >= 0 && keep[i - d] !== 1) keep[i - d] = 2; - if (i + d < lines.length && keep[i + d] !== 1) keep[i + d] = 2; + // ±FILTER_CTX *non-empty* neighbours (skip blank lines so filter is not full of voids) + n = 0; + idx = i - 1; + while (idx >= 0 && n < FILTER_CTX) { + if (keep[idx] === 1) { idx--; continue; } + if (isBlankLine(lines[idx])) { idx--; continue; } + keep[idx] = 2; + n++; + idx--; + } + n = 0; + idx = i + 1; + while (idx < lines.length && n < FILTER_CTX) { + if (keep[idx] === 1) { idx++; continue; } + if (isBlankLine(lines[idx])) { idx++; continue; } + keep[idx] = 2; + n++; + idx++; } } for (j = 0; j < lines.length; j++) { if (keep[j] === 2) lines[j].classList.add("filter-ctx"); + // never show pure blank lines in filter mode + if (isBlankLine(lines[j]) && keep[j] !== 1) keep[j] = 0; } // dashed gap between non-contiguous visible clusters var prevVisible = -2;