spawn/test/update-readme.py
Ahmed Abushagur 8b9f9a0e5a
QA-Bot setup (#335)
* feat: testing

* feat: auto-fix dead apis

* fix: mock works

* feat: new fixtures

* fix: more clouds tested

* fix: dry run fix

* fix: civo valid size

* fix: civo result wait

* feat: fixtures

* feat: per cloud agent
2026-02-10 19:51:07 -08:00

135 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""Update README.md matrix cells based on test results.
Usage:
python3 test/update-readme.py results.txt
Results file format (one per line):
cloud/agent:pass
cloud/agent:fail
Only touches cells that have test results; untested combinations stay unchanged.
"""
import json
import re
import sys
import os
def main():
if len(sys.argv) < 2:
print("Usage: python3 test/update-readme.py RESULTS_FILE", file=sys.stderr)
sys.exit(1)
results_file = sys.argv[1]
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
readme_path = os.path.join(repo_root, "README.md")
manifest_path = os.path.join(repo_root, "manifest.json")
# Parse results
results = {}
with open(results_file) as f:
for line in f:
line = line.strip()
if not line or ":" not in line:
continue
combo, status = line.rsplit(":", 1)
results[combo] = status # cloud/agent -> pass|fail
if not results:
print("No results to apply.")
return
# Load manifest to map agent keys to display names
with open(manifest_path) as f:
manifest = json.load(f)
# Build agent key -> name mapping for row matching
agent_names = {}
for key, info in manifest["agents"].items():
agent_names[info["name"]] = key # "Claude Code" -> "claude"
# Read README
with open(readme_path) as f:
lines = f.readlines()
# Find the matrix table: header row starts with "| |"
header_idx = None
for i, line in enumerate(lines):
if line.startswith("| |") or line.startswith("| | "):
header_idx = i
break
if header_idx is None:
print("Could not find matrix table header in README.md", file=sys.stderr)
sys.exit(1)
# Parse cloud columns from header
# Header: | | [Sprite](sprite/) | [Hetzner Cloud](hetzner/) | ...
header = lines[header_idx]
header_cells = [c.strip() for c in header.split("|")]
# header_cells[0] = "", header_cells[1] = "" (row label), header_cells[2:] = cloud cells
cloud_columns = {} # cloud_dir -> column index (0-based within cells)
for col_idx, cell in enumerate(header_cells):
# Extract dir from [Name](dir/)
m = re.search(r'\[.*?\]\(([^/)]+)/?[^)]*\)', cell)
if m:
cloud_columns[m.group(1)] = col_idx
# Process data rows (skip header and separator)
changed = False
for i in range(header_idx + 2, len(lines)):
line = lines[i]
if not line.startswith("|"):
break
cells = line.split("|")
if len(cells) < 3:
continue
# Extract agent key from first data cell
# e.g. " [**Claude Code**](https://claude.ai) " -> "Claude Code"
row_label = cells[1].strip()
name_match = re.search(r'\[\*\*(.*?)\*\*\]', row_label)
if not name_match:
continue
display_name = name_match.group(1)
agent_key = agent_names.get(display_name)
if not agent_key:
continue
row_changed = False
for cloud_dir, col_idx in cloud_columns.items():
combo = f"{cloud_dir}/{agent_key}"
if combo not in results:
continue
if col_idx >= len(cells):
continue
status = results[combo]
old_cell = cells[col_idx]
# Preserve whitespace padding
stripped = old_cell.strip()
if status == "pass" and stripped != "\u2713":
cells[col_idx] = old_cell.replace(stripped, "\u2713") if stripped else " \u2713 "
row_changed = True
elif status == "fail" and stripped != "\u2717":
cells[col_idx] = old_cell.replace(stripped, "\u2717") if stripped else " \u2717 "
row_changed = True
if row_changed:
lines[i] = "|".join(cells)
if not lines[i].endswith("\n"):
lines[i] += "\n"
changed = True
if changed:
with open(readme_path, "w") as f:
f.writelines(lines)
print(f"README.md updated with {len(results)} test results.")
else:
print("No changes needed in README.md.")
if __name__ == "__main__":
main()