update-supported-devices.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #!/usr/bin/env python3
  2. """Update docs/supported-devices.md from a device-support issue."""
  3. import re, sys, datetime, pathlib
  4. ISSUE_BODY = pathlib.Path(sys.argv[1]).read_text() if len(sys.argv) > 1 else sys.stdin.read()
  5. MD = pathlib.Path("docs/supported-devices.md")
  6. def validate_cell(name, value):
  7. if any(ch in value for ch in ('|', '\r', '\n')):
  8. print(f"invalid {name}: Markdown delimiters and line breaks are not allowed", file=sys.stderr)
  9. raise SystemExit(1)
  10. return value
  11. def field(id):
  12. # issue form bodies render as "### <Label>\n\nvalue"
  13. # we match by id label text variations
  14. labels = {
  15. "manufacturer": "Manufacturer",
  16. "custom_manufacturer": "Custom manufacturer (if Other)",
  17. "device": "Device",
  18. "codename": "Codename",
  19. "gki_kernel": "GKI Kernel",
  20. "firmware": "Firmware",
  21. "status": "Status",
  22. }
  23. label = labels.get(id, id)
  24. m = re.search(rf"### {re.escape(label)}\s*\n+([^\n#]+)", ISSUE_BODY)
  25. if m:
  26. return m.group(1).strip()
  27. # fallback: try id
  28. m = re.search(rf"### {re.escape(id)}\s*\n+([^\n#]+)", ISSUE_BODY)
  29. return m.group(1).strip() if m else ""
  30. heading_map = {
  31. "Google Pixel": "## Google Pixel",
  32. "Samsung": "## Samsung",
  33. "OnePlus": "## OnePlus",
  34. "OPPO": "## OPPO",
  35. "Realme": "## Realme",
  36. "Xiaomi": "## Xiaomi",
  37. "POCO": "## POCO",
  38. "Redmi": "## Redmi",
  39. "Nothing": "## Nothing",
  40. "Other": "## Other",
  41. }
  42. manufacturer = field("manufacturer") or "Other"
  43. custom_oem = field("custom_manufacturer").strip()
  44. if manufacturer == "Other":
  45. custom_oem = validate_cell("custom_oem", custom_oem)
  46. device = validate_cell("device", field("device").strip())
  47. codename = validate_cell("codename", field("codename").strip())
  48. gki = validate_cell("gki", field("gki_kernel").strip())
  49. firmware = validate_cell("firmware", field("firmware").strip() or "stock")
  50. status = validate_cell("status", field("status").strip() or "Supported")
  51. # handle custom OEM when Other is selected
  52. if manufacturer == "Other" and custom_oem and custom_oem.lower() not in ("none", "_no response_", ""):
  53. manufacturer = custom_oem.strip().title()
  54. heading_map[manufacturer] = f"## {manufacturer}"
  55. if not device or not codename or not gki:
  56. print("missing required fields", file=sys.stderr)
  57. sys.exit(0)
  58. heading = heading_map.get(manufacturer, f"## {manufacturer}")
  59. # Xiaomi/POCO/Redmi share same file section expansion if not present -> create heading
  60. text = MD.read_text()
  61. if heading not in text and manufacturer in ("Xiaomi", "POCO", "Redmi"):
  62. # ensure sections exist (already added as separate? current file has Xiaomi / POCO / Redmi combined? we split?)
  63. # if missing, append before end
  64. pass
  65. today = datetime.date.today().isoformat()
  66. status_cell = f"{status} · {today}"
  67. row = f"| {device} | {codename} | {gki} | {firmware} | {status_cell} |"
  68. if heading not in text:
  69. # append new section at end before source
  70. text = text.rstrip() + f"\n\n{heading}\n\n| Device | Codename | GKI Kernel | Firmware | Status |\n|--------|----------|------------|----------|--------|\n{row}\n"
  71. MD.write_text(text)
  72. print(f"added new heading {heading}")
  73. sys.exit(0)
  74. # find table under heading
  75. # split by headings
  76. parts = re.split(r"(^## .+$)", text, flags=re.MULTILINE)
  77. out = []
  78. for i, part in enumerate(parts):
  79. if part.strip() == heading:
  80. # next part is body until next heading
  81. body = parts[i+1] if i+1 < len(parts) else ""
  82. # check duplicate codename
  83. if re.search(rf"\|\s*{re.escape(device)}\s*\|", body) or re.search(rf"\|\s*[^|]*\|\s*{re.escape(codename)}\s*\|", body):
  84. print("device already listed, updating not duplicating")
  85. # replace existing row's status/gki if needed? skip for now
  86. out.append(part)
  87. out.append(body)
  88. continue
  89. # replace placeholder row if present
  90. placeholder = "| — | — | — | — | Placeholder — add entries |"
  91. if placeholder in body:
  92. new_body = body.replace(placeholder, row, 1)
  93. else:
  94. # append row before next heading or at end of table (before blank line + ##)
  95. # find last table row line
  96. lines = body.splitlines()
  97. insert_idx = None
  98. for idx, line in enumerate(lines):
  99. if line.startswith("|") and "Device | Codename" in line:
  100. # header, continue
  101. continue
  102. if line.startswith("|") and "--------" in line:
  103. continue
  104. # find last row that starts with |
  105. last = -1
  106. for idx, line in enumerate(lines):
  107. if line.startswith("| "):
  108. last = idx
  109. if last >= 0:
  110. lines.insert(last+1, row)
  111. else:
  112. lines.append(row)
  113. new_body = "\n".join(lines)
  114. out.append(part)
  115. out.append(new_body)
  116. else:
  117. # already handled body as part of heading case? avoid double
  118. if i>0 and parts[i-1].strip() == heading:
  119. continue
  120. out.append(part)
  121. MD.write_text("".join(out))
  122. print(f"inserted {device} into {heading}")