Files
Darksidewalker 760e51418e chore: auto-sync bug-report node dropdown from NODE_DISPLAY_NAME_MAPPINGS
Adds .github/scripts/sync_issue_form.py (AST-parses __init__.py, no
runtime deps) and a workflow that regenerates the dropdown and
auto-commits to main when __init__.py or the form changes.
2026-08-17 18:24:27 +02:00

85 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Keep the node dropdown in .github/ISSUE_TEMPLATE/bug-report.yml in sync
with the node display names registered in __init__.py.
Source of truth: the string values of NODE_DISPLAY_NAME_MAPPINGS in __init__.py.
We read them via `ast` (no imports), so this runs anywhere without torch/av/etc.
installed. It rewrites only the `options:` block under the dropdown with
`id: node`, leaving every other field in the form untouched.
Exit code is always 0 on success. The final printed line is `changed=0` or
`changed=1`, which the calling workflow uses to decide whether to commit.
"""
import ast
import re
import sys
import pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
INIT = ROOT / "__init__.py"
TEMPLATE = ROOT / ".github" / "ISSUE_TEMPLATE" / "bug-report.yml"
EXTRA_OPTIONS = ["Other / general"]
def display_names():
"""Return the node display names (dict values, order-preserving, de-duped)."""
tree = ast.parse(INIT.read_text())
for node in ast.walk(tree):
if isinstance(node, ast.Assign):
for t in node.targets:
if getattr(t, "id", None) == "NODE_DISPLAY_NAME_MAPPINGS":
d = ast.literal_eval(node.value)
seen, out = set(), []
for name in d.values():
if name not in seen:
seen.add(name)
out.append(name)
return out
sys.exit("ERROR: NODE_DISPLAY_NAME_MAPPINGS not found in __init__.py")
def quote(value: str) -> str:
"""Double-quote an option value so YAML stays valid for names with & ( )."""
return '"' + value.replace('"', '\\"') + '"'
def main() -> None:
names = display_names()
lines = TEMPLATE.read_text().splitlines(keepends=True)
# Locate the dropdown's `id: node` line.
id_idx = next(
(i for i, l in enumerate(lines) if l.strip() == "id: node"), None
)
if id_idx is None:
sys.exit("ERROR: no 'id: node' dropdown found in bug-report.yml")
# Locate its `options:` line (first one after the id).
opt_idx = next(
(j for j in range(id_idx, len(lines)) if lines[j].strip() == "options:"),
None,
)
if opt_idx is None:
sys.exit("ERROR: no 'options:' block under 'id: node' in bug-report.yml")
# End of the options list: first line that is not a `- item`.
end = opt_idx + 1
while end < len(lines) and re.match(r"^\s+- ", lines[end]):
end += 1
new_options = [f" - {quote(n)}\n" for n in names + EXTRA_OPTIONS]
new_text = "".join(lines[: opt_idx + 1] + new_options + lines[end:])
old_text = "".join(lines)
if new_text != old_text:
TEMPLATE.write_text(new_text)
print(f"Synced {len(names)} registered nodes (+{len(EXTRA_OPTIONS)} fallback option) into the issue-form dropdown.")
print("changed=1")
else:
print(f"Dropdown already in sync ({len(names)} nodes).")
print("changed=0")
if __name__ == "__main__":
main()