From d0004d99f834f46dd9da905d65d4271849d97f07 Mon Sep 17 00:00:00 2001 From: juvdiaz Date: Mon, 29 Jun 2026 20:37:03 -0600 Subject: [PATCH] Improve Jeannie explain readability --- infra/jeannie-explain/findings.tsv | 1 + scripts/explain | 99 +++++++++++++++++++++--------- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/infra/jeannie-explain/findings.tsv b/infra/jeannie-explain/findings.tsv index 46c9a33..514eab4 100644 --- a/infra/jeannie-explain/findings.tsv +++ b/infra/jeannie-explain/findings.tsv @@ -2,6 +2,7 @@ pattern meaning diagnose_commands jeannie_fix_commands other_fix_commands risk a unsupported template variable Generated docs found a template placeholder that render-docs does not support. ./jeannie validate none Edit README.md.tmpl or scripts/render-docs low no Usually a docs/template mismatch rather than an infrastructure issue. generated README.md is stale README.md does not match README.md.tmpl rendering. ./jeannie validate none scripts/render-docs low candidate A future docs-render Jeannie command could safely wrap this. missing_resource_policy Workloads are missing resource requests or limits, so capacity planning is unreliable. ./jeannie resource-budget;./jeannie capacity none Edit app manifests or Helm values medium no Add requests first, then limits for workloads you own. +pods missing requests/limits Some Kubernetes pods are missing resource requests or limits, so the scheduler and capacity reports cannot fully trust workload sizing. ./jeannie resource-budget --details;./jeannie capacity none Add requests/limits in the app manifests, Helm values, or platform chart values medium no Yes, this is fixed from code/config by adding resources to workloads you own; avoid auto-generating limits blindly. missing requests One or more containers do not declare CPU or memory requests. ./jeannie resource-budget --details;./jeannie capacity none Edit app manifests or Helm values medium no Start with application namespaces before system namespaces. missing limits One or more containers do not declare CPU or memory limits. ./jeannie resource-budget --details;./jeannie capacity none Edit app manifests or Helm values medium no Limits are useful but can cause throttling; set them deliberately. resource policy Capacity or resource policy needs attention. ./jeannie resource-budget;./jeannie capacity none Edit infra/resource-budgets.yml or app manifests medium no Do not hide pressure by only changing the budget file. diff --git a/scripts/explain b/scripts/explain index 6ecab24..6569639 100755 --- a/scripts/explain +++ b/scripts/explain @@ -105,8 +105,12 @@ def read_tsv(path, key): with open(path, encoding="utf-8", newline="") as handle: return {row[key].lower(): row for row in csv.DictReader(handle, delimiter="\t")} +def read_tsv_rows(path): + with open(path, encoding="utf-8", newline="") as handle: + return list(csv.DictReader(handle, delimiter="\t")) + scorecard_info = read_tsv(scorecard_map, "label") -finding_info = read_tsv(findings_map, "pattern") +finding_rows = read_tsv_rows(findings_map) with open(input_file, encoding="utf-8") as handle: text = handle.read() @@ -116,30 +120,32 @@ lines = text.splitlines() def split_commands(value): return [item.strip() for item in (value or "").split(";") if item.strip() and item.strip() != "none"] -def print_info(info): +def print_info(info, indent=" "): diagnose = split_commands(info.get("diagnose_commands")) jeannie_fix = split_commands(info.get("jeannie_fix_commands")) other_fix = split_commands(info.get("other_fix_commands")) - print(f" means: {info.get('meaning') or 'No explain entry exists yet.'}") + print(f"{indent}Why: {info.get('meaning') or 'No explain entry exists yet.'}") if diagnose: - print(" diagnose:") - for command in diagnose: - print(f" {command}") + print(f"{indent}Next command: {diagnose[0]}") + if len(diagnose) > 1: + print(f"{indent}More checks:") + for command in diagnose[1:]: + print(f"{indent} {command}") if jeannie_fix: - print(" Jeannie fix command exists: yes") + print(f"{indent}Jeannie fix command:") for command in jeannie_fix: - print(f" {command}") + print(f"{indent} {command}") else: - print(" Jeannie fix command exists: no") + print(f"{indent}Jeannie fix command: none yet") if other_fix: - print(" non-Jeannie fix:") + print(f"{indent}Code/config fix:") for command in other_fix: - print(f" {command}") - print(f" risk: {info.get('risk') or 'unknown'}") - print(f" auto-fix safe: {info.get('auto_fix') or 'no'}") + print(f"{indent} {command}") + print(f"{indent}Risk: {info.get('risk') or 'unknown'}") + print(f"{indent}Auto-fix safe: {info.get('auto_fix') or 'no'}") if info.get("notes"): - print(f" note: {info['notes']}") + print(f"{indent}Note: {info['notes']}") def parse_scorecard(): rows = [] @@ -191,11 +197,35 @@ def interesting_lines(): def match_findings(line): lowered = line.lower() matches = [] - for pattern, info in finding_info.items(): + for info in finding_rows: + pattern = (info.get("pattern") or "").lower() if pattern and pattern in lowered: matches.append((pattern, info)) return matches +def best_finding(line): + matches = match_findings(line) + if not matches: + return None + return sorted(matches, key=lambda item: (len(item[0]), item[0]), reverse=True)[0] + +def parse_report_line(line): + report_match = re.match(r"^(?Pok|warn|fail|skip)\s+(?P.+?)(?:\s{2,}(?P.+))?$", line) + if report_match: + return ( + report_match.group("check").strip(), + report_match.group("status").strip(), + (report_match.group("summary") or "").strip(), + ) + scorecard_match = re.match(r"^(?P