Improve Jeannie explain readability
This commit is contained in:
parent
86e061dd72
commit
d0004d99f8
|
|
@ -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.
|
||||
|
|
|
|||
|
|
|
@ -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"^(?P<status>ok|warn|fail|skip)\s+(?P<check>.+?)(?:\s{2,}(?P<summary>.+))?$", 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<label>.{1,28}) (?P<status>pass|warn|fail)(?: - (?P<summary>.*))?$", line)
|
||||
if scorecard_match:
|
||||
return (
|
||||
scorecard_match.group("label").strip(),
|
||||
scorecard_match.group("status").strip(),
|
||||
(scorecard_match.group("summary") or "").strip(),
|
||||
)
|
||||
return (line[:72], "", "")
|
||||
|
||||
print(f"Jeannie Explain: {topic}")
|
||||
print("=" * (17 + len(topic)))
|
||||
print("mode: read-only")
|
||||
|
|
@ -204,13 +234,13 @@ if topic == "scorecard":
|
|||
rows = parse_scorecard()
|
||||
if not rows:
|
||||
print("\nNo scorecard warnings or failures found.")
|
||||
for label, status, summary in rows:
|
||||
for index, (label, status, summary) in enumerate(rows, start=1):
|
||||
info = scorecard_info.get(label.lower(), {})
|
||||
print(f"\n{label}")
|
||||
print(f" status: {status}")
|
||||
print(f"\n{index}. {label}")
|
||||
print(f" Status: {status}")
|
||||
if summary:
|
||||
print(f" observed: {summary}")
|
||||
print_info(info)
|
||||
print(f" Observed: {summary}")
|
||||
print_info(info, " ")
|
||||
raise SystemExit(0)
|
||||
|
||||
found_lines = interesting_lines()
|
||||
|
|
@ -218,17 +248,26 @@ seen_patterns = set()
|
|||
matched_any = False
|
||||
|
||||
for line in found_lines:
|
||||
matches = match_findings(line)
|
||||
if not matches:
|
||||
title, status, summary = parse_report_line(line)
|
||||
match = best_finding(summary) if summary else None
|
||||
if not match:
|
||||
match = best_finding(line)
|
||||
if not match:
|
||||
continue
|
||||
print(f"\nFinding: {line}")
|
||||
for pattern, info in matches:
|
||||
pattern, info = match
|
||||
if pattern in seen_patterns:
|
||||
continue
|
||||
seen_patterns.add(pattern)
|
||||
matched_any = True
|
||||
print(f" matched: {pattern}")
|
||||
print_info(info)
|
||||
print(f"\n{len(seen_patterns)}. {title}")
|
||||
if status:
|
||||
print(f" Status: {status}")
|
||||
if summary:
|
||||
print(f" Observed: {summary}")
|
||||
else:
|
||||
print(f" Observed: {line}")
|
||||
print(f" Matched rule: {pattern}")
|
||||
print_info(info, " ")
|
||||
|
||||
if not matched_any:
|
||||
if found_lines:
|
||||
|
|
|
|||
Loading…
Reference in New Issue