| 95 | # checkers — each returns a list of {signature, file, line, message} findings |
| 96 | # --------------------------------------------------------------------------- # |
| 97 | def run_vale(text, rel, repo_root): |
| 98 | if not text.strip(): |
| 99 | return [] |
| 100 | with tempfile.NamedTemporaryFile( |
| 101 | "w", suffix=".md", delete=False, encoding="utf-8" |
| 102 | ) as tf: |
| 103 | tf.write(text) |
| 104 | tmp = tf.name |
| 105 | try: |
| 106 | r = subprocess.run( |
| 107 | ["vale", "--config", VALE_CONFIG, "--minAlertLevel=suggestion", |
| 108 | "--output=JSON", tmp], |
| 109 | cwd=repo_root, capture_output=True, text=True, |
| 110 | ) |
| 111 | out = r.stdout.strip() |
| 112 | if not out: |
| 113 | return [] |
| 114 | data = json.loads(out) |
| 115 | except (json.JSONDecodeError, OSError) as exc: |
| 116 | sys.stderr.write(f"vale failed on {rel}: {exc}\n") |
| 117 | return [] |
| 118 | finally: |
| 119 | os.unlink(tmp) |
| 120 | findings = [] |
| 121 | for items in data.values(): |
| 122 | for it in items: |
| 123 | findings.append({ |
| 124 | "signature": ("vale", it.get("Check", ""), it.get("Match", "")), |
| 125 | "file": rel, |
| 126 | "line": it.get("Line", 0), |
| 127 | "message": f"{it.get('Check','')}: {it.get('Message','')}", |
| 128 | }) |
| 129 | return findings |
| 130 | |
| 131 | |
| 132 | def run_capcheck(text, rel): |