|
12 | 12 | def build_github_issue_templates_context(project: dict[str, Any]) -> dict[str, Any] | None: |
13 | 13 | app = project.get("app", {}) |
14 | 14 | issue_templates = app.get("issue_templates") |
15 | | - if not issue_templates: |
| 15 | + if issue_templates is None: |
16 | 16 | return None |
17 | 17 | if not isinstance(issue_templates, dict): |
18 | 18 | raise RuntimeError("app.issue_templates must be a table.") |
19 | 19 |
|
20 | | - blank_issues_enabled = issue_templates.get("blank_issues_enabled") |
21 | | - if not isinstance(blank_issues_enabled, bool): |
22 | | - raise RuntimeError("app.issue_templates.blank_issues_enabled must be a boolean.") |
23 | | - |
24 | 20 | assignee = str(issue_templates.get("assignee", "")).strip() |
25 | 21 | if not assignee: |
26 | 22 | raise RuntimeError("app.issue_templates.assignee must be a non-empty string.") |
27 | 23 |
|
28 | | - contact_links = issue_templates.get("contact_links") |
29 | | - if not isinstance(contact_links, list) or not contact_links: |
30 | | - raise RuntimeError("app.issue_templates.contact_links must be a non-empty list.") |
31 | | - |
32 | | - normalized_contact_links: list[dict[str, str]] = [] |
33 | | - for index, link in enumerate(contact_links): |
34 | | - if not isinstance(link, dict): |
35 | | - raise RuntimeError(f"app.issue_templates.contact_links[{index}] must be a table.") |
36 | | - name = str(link.get("name", "")).strip() |
37 | | - url = str(link.get("url", "")).strip() |
38 | | - about = str(link.get("about", "")).strip() |
39 | | - if not name or not url or not about: |
40 | | - raise RuntimeError(f"app.issue_templates.contact_links[{index}] must define name, url, and about.") |
41 | | - normalized_contact_links.append( |
42 | | - { |
43 | | - "name": name, |
44 | | - "url": url, |
45 | | - "about": about, |
46 | | - } |
47 | | - ) |
48 | | - |
49 | | - templates: dict[str, dict[str, Any]] = {} |
50 | | - for template_name in ("bug_report", "documentation_issue", "feature_request"): |
51 | | - template_value = issue_templates.get(template_name) |
52 | | - if not isinstance(template_value, dict): |
53 | | - raise RuntimeError(f"app.issue_templates.{template_name} must be a table.") |
54 | | - |
55 | | - name = str(template_value.get("name", "")).strip() |
56 | | - description = str(template_value.get("description", "")).strip() |
57 | | - title = str(template_value.get("title", "")).strip() |
58 | | - labels = template_value.get("labels") |
59 | | - if not isinstance(labels, list) or not labels: |
60 | | - raise RuntimeError(f"app.issue_templates.{template_name}.labels must be a non-empty list.") |
61 | | - normalized_labels: list[str] = [] |
62 | | - for index, label in enumerate(labels): |
63 | | - text = str(label).strip() |
64 | | - if not text: |
65 | | - raise RuntimeError(f"app.issue_templates.{template_name}.labels[{index}] must be a non-empty string.") |
66 | | - normalized_labels.append(text) |
67 | | - if not name or not description or not title: |
68 | | - raise RuntimeError(f"app.issue_templates.{template_name} must define name, description, and title.") |
69 | | - |
70 | | - templates[template_name] = { |
71 | | - "name": name, |
72 | | - "description": description, |
73 | | - "title": title, |
74 | | - "labels": normalized_labels, |
75 | | - } |
| 24 | + contact_links = build_issue_template_contact_links(app) |
76 | 25 |
|
77 | 26 | return { |
78 | | - "blank_issues_enabled": blank_issues_enabled, |
79 | 27 | "assignee": assignee, |
80 | | - "contact_links": normalized_contact_links, |
| 28 | + "contact_links": contact_links, |
81 | 29 | "yaml_value": lambda value: json.dumps(value, ensure_ascii=False), |
82 | | - **templates, |
83 | 30 | } |
84 | 31 |
|
85 | 32 |
|
| 33 | +def build_issue_template_contact_links(app: dict[str, Any]) -> list[dict[str, str]]: |
| 34 | + package_owner = str(app.get("package_owner", "")).strip() |
| 35 | + package_name = str(app.get("package_name", "")).strip() |
| 36 | + if not package_owner or not package_name: |
| 37 | + raise RuntimeError("app.package_owner and app.package_name are required to generate issue templates.") |
| 38 | + |
| 39 | + contact_links: list[dict[str, str]] = [ |
| 40 | + { |
| 41 | + "name": "📖 Read the docs", |
| 42 | + "url": f"https://{package_owner.lower()}.github.io/{package_name}/quick_start.html", |
| 43 | + "about": "Start here for “how-to” questions.", |
| 44 | + } |
| 45 | + ] |
| 46 | + |
| 47 | + social = app.get("social") |
| 48 | + if social is None: |
| 49 | + return contact_links |
| 50 | + if not isinstance(social, dict): |
| 51 | + raise RuntimeError("app.social must be a table when issue templates are enabled.") |
| 52 | + |
| 53 | + discord_url = str(social.get("discord", "")).strip() |
| 54 | + if discord_url: |
| 55 | + contact_links.append( |
| 56 | + { |
| 57 | + "name": "💬 Discord server (Discussions)", |
| 58 | + "url": discord_url, |
| 59 | + "about": "General Q&A and community support.", |
| 60 | + } |
| 61 | + ) |
| 62 | + |
| 63 | + telegram_url = str(social.get("telegram", "")).strip() |
| 64 | + if telegram_url: |
| 65 | + contact_links.append( |
| 66 | + { |
| 67 | + "name": "💬 Telegram channel (Discussions)", |
| 68 | + "url": telegram_url, |
| 69 | + "about": "General Q&A and community support.", |
| 70 | + } |
| 71 | + ) |
| 72 | + |
| 73 | + return contact_links |
| 74 | + |
| 75 | + |
86 | 76 | def generate_github_issue_templates_project(project: dict[str, Any], output_dir: Path) -> None: |
87 | 77 | issue_templates_root = output_dir / ".github" / "ISSUE_TEMPLATE" |
88 | 78 | context = build_github_issue_templates_context(project) |
|
0 commit comments