@kernel-sig/console 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.env.example +50 -0
  2. package/LICENSE +127 -0
  3. package/Makefile +66 -0
  4. package/README.md +279 -0
  5. package/backend/.dockerignore +12 -0
  6. package/backend/Dockerfile +43 -0
  7. package/backend/alembic/env.py +75 -0
  8. package/backend/alembic/script.py.mako +25 -0
  9. package/backend/alembic/versions/0001_initial.py +88 -0
  10. package/backend/alembic/versions/350e2d9b6553_add_pr_comment_table.py +48 -0
  11. package/backend/alembic/versions/4f8342e727fe_sig_info_roster_fields_and_branch_.py +39 -0
  12. package/backend/alembic/versions/5a31ade90136_add_repository_credential_pull_request_.py +293 -0
  13. package/backend/alembic/versions/87a008142f17_add_classification_attention_and_ai_.py +436 -0
  14. package/backend/alembic/versions/a1c7d90e4b52_branch_belongs_to_a_repository.py +72 -0
  15. package/backend/alembic/versions/b41c9d7e5f28_extend_pr_kind_categories.py +43 -0
  16. package/backend/alembic/versions/c8e4f2a71b93_add_release_kind.py +40 -0
  17. package/backend/alembic/versions/d1275091dfdc_add_needs_detail_flag_to_pull_request.py +44 -0
  18. package/backend/alembic/versions/e5b27c9d3a41_downgrade_cve_without_ids.py +54 -0
  19. package/backend/alembic/versions/ee3159a4eff0_add_sig_meeting_member_and_release_.py +164 -0
  20. package/backend/alembic/versions/fcf3c186d63b_attention_rule_subscriptions.py +42 -0
  21. package/backend/alembic.ini +40 -0
  22. package/backend/app/__init__.py +0 -0
  23. package/backend/app/api/__init__.py +0 -0
  24. package/backend/app/api/deps.py +74 -0
  25. package/backend/app/api/v1/__init__.py +0 -0
  26. package/backend/app/api/v1/ai.py +386 -0
  27. package/backend/app/api/v1/analytics.py +48 -0
  28. package/backend/app/api/v1/attention.py +142 -0
  29. package/backend/app/api/v1/auth.py +106 -0
  30. package/backend/app/api/v1/classification.py +192 -0
  31. package/backend/app/api/v1/health.py +29 -0
  32. package/backend/app/api/v1/issues.py +143 -0
  33. package/backend/app/api/v1/pulls.py +388 -0
  34. package/backend/app/api/v1/repositories.py +273 -0
  35. package/backend/app/api/v1/router.py +32 -0
  36. package/backend/app/api/v1/sig.py +565 -0
  37. package/backend/app/api/v1/users.py +87 -0
  38. package/backend/app/api/v1/webhooks.py +135 -0
  39. package/backend/app/core/__init__.py +0 -0
  40. package/backend/app/core/config.py +72 -0
  41. package/backend/app/core/crypto.py +74 -0
  42. package/backend/app/core/db.py +79 -0
  43. package/backend/app/core/exceptions.py +60 -0
  44. package/backend/app/core/logging.py +69 -0
  45. package/backend/app/core/permissions.py +87 -0
  46. package/backend/app/core/queue.py +57 -0
  47. package/backend/app/core/security.py +69 -0
  48. package/backend/app/domain/__init__.py +0 -0
  49. package/backend/app/domain/attention.py +618 -0
  50. package/backend/app/domain/classification.py +689 -0
  51. package/backend/app/domain/meeting.py +408 -0
  52. package/backend/app/domain/release.py +163 -0
  53. package/backend/app/domain/review.py +416 -0
  54. package/backend/app/domain/sig.py +178 -0
  55. package/backend/app/domain/sig_info.py +201 -0
  56. package/backend/app/integrations/__init__.py +0 -0
  57. package/backend/app/integrations/atomgit/__init__.py +0 -0
  58. package/backend/app/integrations/atomgit/client.py +505 -0
  59. package/backend/app/integrations/atomgit/models.py +311 -0
  60. package/backend/app/integrations/llm/__init__.py +0 -0
  61. package/backend/app/integrations/llm/prompts.py +222 -0
  62. package/backend/app/integrations/llm/provider.py +326 -0
  63. package/backend/app/main.py +242 -0
  64. package/backend/app/middleware/__init__.py +0 -0
  65. package/backend/app/middleware/audit.py +129 -0
  66. package/backend/app/middleware/request_context.py +42 -0
  67. package/backend/app/models/__init__.py +112 -0
  68. package/backend/app/models/ai.py +214 -0
  69. package/backend/app/models/attention.py +131 -0
  70. package/backend/app/models/attention_settings.py +61 -0
  71. package/backend/app/models/audit.py +53 -0
  72. package/backend/app/models/base.py +35 -0
  73. package/backend/app/models/classification.py +144 -0
  74. package/backend/app/models/credential.py +56 -0
  75. package/backend/app/models/issue.py +132 -0
  76. package/backend/app/models/meeting.py +189 -0
  77. package/backend/app/models/pull_request.py +288 -0
  78. package/backend/app/models/repository.py +196 -0
  79. package/backend/app/models/sig.py +194 -0
  80. package/backend/app/models/user.py +44 -0
  81. package/backend/app/schemas/__init__.py +0 -0
  82. package/backend/app/schemas/ai.py +120 -0
  83. package/backend/app/schemas/attention.py +43 -0
  84. package/backend/app/schemas/auth.py +26 -0
  85. package/backend/app/schemas/classification.py +58 -0
  86. package/backend/app/schemas/common.py +43 -0
  87. package/backend/app/schemas/pull_request.py +214 -0
  88. package/backend/app/schemas/repository.py +94 -0
  89. package/backend/app/schemas/sig.py +246 -0
  90. package/backend/app/schemas/user.py +79 -0
  91. package/backend/app/services/__init__.py +0 -0
  92. package/backend/app/services/ai_service.py +569 -0
  93. package/backend/app/services/analytics_service.py +390 -0
  94. package/backend/app/services/attention_queue.py +451 -0
  95. package/backend/app/services/attention_service.py +432 -0
  96. package/backend/app/services/auth_service.py +74 -0
  97. package/backend/app/services/classification_service.py +658 -0
  98. package/backend/app/services/credential_service.py +105 -0
  99. package/backend/app/services/pull_query.py +273 -0
  100. package/backend/app/services/release_service.py +446 -0
  101. package/backend/app/services/repository_service.py +132 -0
  102. package/backend/app/services/sig_service.py +385 -0
  103. package/backend/app/services/sync_service.py +752 -0
  104. package/backend/app/services/user_service.py +68 -0
  105. package/backend/app/worker.py +389 -0
  106. package/backend/entrypoint.sh +10 -0
  107. package/backend/pyproject.toml +68 -0
  108. package/backend/tests/test_analysis_api.py +154 -0
  109. package/backend/tests/test_atomgit_client.py +360 -0
  110. package/backend/tests/test_atomgit_models.py +257 -0
  111. package/backend/tests/test_attention.py +291 -0
  112. package/backend/tests/test_audit_middleware.py +135 -0
  113. package/backend/tests/test_classification.py +498 -0
  114. package/backend/tests/test_config.py +41 -0
  115. package/backend/tests/test_crypto.py +68 -0
  116. package/backend/tests/test_exceptions.py +62 -0
  117. package/backend/tests/test_health.py +63 -0
  118. package/backend/tests/test_llm_provider.py +320 -0
  119. package/backend/tests/test_meeting_domain.py +169 -0
  120. package/backend/tests/test_permissions.py +69 -0
  121. package/backend/tests/test_pull_query_wiring.py +66 -0
  122. package/backend/tests/test_pull_schemas.py +82 -0
  123. package/backend/tests/test_release_domain.py +82 -0
  124. package/backend/tests/test_review_parser.py +301 -0
  125. package/backend/tests/test_schemas_user.py +97 -0
  126. package/backend/tests/test_security.py +92 -0
  127. package/cli/index.js +338 -0
  128. package/compose.yaml +105 -0
  129. package/frontend/.dockerignore +4 -0
  130. package/frontend/Dockerfile +27 -0
  131. package/frontend/index.html +14 -0
  132. package/frontend/nginx.conf +47 -0
  133. package/frontend/package-lock.json +5020 -0
  134. package/frontend/package.json +35 -0
  135. package/frontend/src/api/ai.ts +124 -0
  136. package/frontend/src/api/analytics.ts +66 -0
  137. package/frontend/src/api/attention.ts +181 -0
  138. package/frontend/src/api/auth.ts +74 -0
  139. package/frontend/src/api/classification.ts +170 -0
  140. package/frontend/src/api/issues.ts +90 -0
  141. package/frontend/src/api/pulls.ts +233 -0
  142. package/frontend/src/api/repositories.ts +95 -0
  143. package/frontend/src/api/sig.ts +232 -0
  144. package/frontend/src/app/antd-theme.ts +94 -0
  145. package/frontend/src/app/providers.tsx +59 -0
  146. package/frontend/src/app/router.tsx +411 -0
  147. package/frontend/src/app/search.ts +30 -0
  148. package/frontend/src/components/ClassificationBadge.tsx +58 -0
  149. package/frontend/src/components/GateBadge.tsx +13 -0
  150. package/frontend/src/components/SeverityBadge.tsx +20 -0
  151. package/frontend/src/components/layout/AppShell.tsx +16 -0
  152. package/frontend/src/components/layout/AuthLayout.tsx +32 -0
  153. package/frontend/src/components/layout/Sidebar.tsx +223 -0
  154. package/frontend/src/components/layout/TopBar.tsx +47 -0
  155. package/frontend/src/components/pulls/DiscussionTimeline.tsx +145 -0
  156. package/frontend/src/components/pulls/FacetRail.tsx +199 -0
  157. package/frontend/src/components/pulls/LabelChips.tsx +87 -0
  158. package/frontend/src/components/ui/alert.tsx +30 -0
  159. package/frontend/src/components/ui/badge.tsx +53 -0
  160. package/frontend/src/components/ui/button.tsx +51 -0
  161. package/frontend/src/components/ui/card.tsx +64 -0
  162. package/frontend/src/components/ui/chart-theme.ts +65 -0
  163. package/frontend/src/components/ui/data-table.tsx +39 -0
  164. package/frontend/src/components/ui/echart.tsx +70 -0
  165. package/frontend/src/components/ui/empty-state.tsx +22 -0
  166. package/frontend/src/components/ui/input.tsx +39 -0
  167. package/frontend/src/components/ui/lazy-chart.tsx +21 -0
  168. package/frontend/src/components/ui/skeleton.tsx +19 -0
  169. package/frontend/src/hooks/use-current-repository.ts +44 -0
  170. package/frontend/src/hooks/use-current-user.ts +38 -0
  171. package/frontend/src/lib/api-client.ts +93 -0
  172. package/frontend/src/lib/css-color.ts +60 -0
  173. package/frontend/src/lib/utils.ts +45 -0
  174. package/frontend/src/main.tsx +22 -0
  175. package/frontend/src/pages/attention/AttentionQueuePage.tsx +316 -0
  176. package/frontend/src/pages/attention/RuleSettingsPanel.tsx +177 -0
  177. package/frontend/src/pages/branches/BranchDetailPage.tsx +632 -0
  178. package/frontend/src/pages/branches/BranchListPage.tsx +308 -0
  179. package/frontend/src/pages/dashboard/DashboardPage.tsx +657 -0
  180. package/frontend/src/pages/issues/IssueListPage.tsx +284 -0
  181. package/frontend/src/pages/login/LoginPage.tsx +95 -0
  182. package/frontend/src/pages/meetings/MeetingDetailPage.tsx +403 -0
  183. package/frontend/src/pages/meetings/MeetingListPage.tsx +264 -0
  184. package/frontend/src/pages/members/MembersPage.tsx +534 -0
  185. package/frontend/src/pages/pulls/PullDetailPage.tsx +833 -0
  186. package/frontend/src/pages/pulls/PullListPage.tsx +399 -0
  187. package/frontend/src/pages/settings/AiSettingsPage.tsx +449 -0
  188. package/frontend/src/pages/settings/SettingsPage.tsx +321 -0
  189. package/frontend/src/pages/setup/SetupPage.tsx +144 -0
  190. package/frontend/src/styles/globals.css +147 -0
  191. package/frontend/tsconfig.json +22 -0
  192. package/frontend/vite.config.ts +57 -0
  193. package/package.json +48 -0
  194. package/scripts/e2e-auth-flow.py +131 -0
  195. package/scripts/e2e-pr-detail.py +128 -0
  196. package/scripts/e2e-verify.py +773 -0
  197. package/scripts/verify-ai-pipeline.py +616 -0
  198. package/scripts/verify-analysis-pipeline.py +303 -0
@@ -0,0 +1,201 @@
1
+ """``sig-info.yaml`` 解析。
2
+
3
+ 这是 openEuler 各 SIG 的**权威元数据文件**,机器可读,位于
4
+ ``openeuler/community`` 的 ``sig/Kernel/sig-info.yaml``。成员名单、
5
+ 仓库清单、分支负责人都在这里,由社区工具链维护。
6
+
7
+ 此前用的是 ``README.md`` 的 Maintainer 列表加 ``committers.md`` 的模块表,
8
+ 两者都是给人看的文档:README 只有 4 位维护者的展示名与 gitee handle,
9
+ committers.md 是按内核路径划分的分工表。合起来只有 101 人,且**没有邮箱、
10
+ 没有组织、没有 atomgit 账号**(实测 14% 的人只有 gitee 账号)。
11
+ ``sig-info.yaml`` 有 133 人且字段完整。
12
+
13
+ 两份材料不冲突,但用途不同,因此都保留:
14
+ - ``sig-info.yaml`` → 成员是谁、管哪些仓库、负责哪些分支
15
+ - ``committers.md`` → 改动了哪个内核路径该找谁(yaml 里没有路径信息)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass, field
21
+
22
+ import yaml
23
+
24
+ # 角色优先级。一个人可以同时是维护者与某仓库的 committer,
25
+ # 取最高的那个作为主角色,全部角色另存。
26
+ ROLE_RANK = {"maintainer": 3, "committer": 2, "contributor": 1}
27
+
28
+
29
+ @dataclass
30
+ class RosterEntry:
31
+ """名单里的一个人。"""
32
+
33
+ name: str
34
+ atomgit_id: str | None = None
35
+ gitee_id: str | None = None
36
+ email: str | None = None
37
+ organization: str | None = None
38
+
39
+ role: str = "contributor"
40
+ """最高角色。"""
41
+
42
+ roles: list[str] = field(default_factory=list)
43
+ repositories: list[str] = field(default_factory=list)
44
+ """作为 committer / contributor 参与的仓库。"""
45
+
46
+ admin_repositories: list[str] = field(default_factory=list)
47
+ kept_branches: list[str] = field(default_factory=list)
48
+ """作为 keeper 负责的分支。"""
49
+
50
+ @property
51
+ def key(self) -> str:
52
+ """去重键。优先用 atomgit 账号 —— 那是在本平台能匹配到 PR 作者的字段。"""
53
+ return self.atomgit_id or self.gitee_id or self.name
54
+
55
+
56
+ @dataclass
57
+ class BranchKeeper:
58
+ repo: str
59
+ branch: str
60
+ keepers: list[str] = field(default_factory=list)
61
+ """keeper 的 atomgit/gitee 账号。yaml 里只有 id,姓名要回名单里查。"""
62
+
63
+
64
+ @dataclass
65
+ class SIGInfo:
66
+ name: str
67
+ description: str | None = None
68
+ mailing_list: str | None = None
69
+ roster: list[RosterEntry] = field(default_factory=list)
70
+ repositories: list[str] = field(default_factory=list)
71
+ branch_keepers: list[BranchKeeper] = field(default_factory=list)
72
+
73
+
74
+ def _person(raw: object) -> RosterEntry | None:
75
+ if not isinstance(raw, dict):
76
+ return None
77
+ name = str(raw.get("name") or "").strip()
78
+ atomgit = str(raw.get("atomgit_id") or "").strip() or None
79
+ gitee = str(raw.get("gitee_id") or "").strip() or None
80
+ if not name and not atomgit and not gitee:
81
+ return None
82
+ return RosterEntry(
83
+ name=name or (atomgit or gitee or ""),
84
+ atomgit_id=atomgit,
85
+ gitee_id=gitee,
86
+ email=str(raw.get("email") or "").strip() or None,
87
+ organization=str(raw.get("organization") or "").strip() or None,
88
+ )
89
+
90
+
91
+ def _as_list(value: object) -> list:
92
+ if value is None:
93
+ return []
94
+ return value if isinstance(value, list) else [value]
95
+
96
+
97
+ def parse_sig_info(text: str) -> SIGInfo | None:
98
+ """解析 sig-info.yaml。结构不合预期时返回 None,不抛异常。
99
+
100
+ yaml 是上游维护的,字段增删由他们决定;解析失败只该让这轮同步跳过,
101
+ 不该让整个名单同步任务报红 —— 名单是低频数据,下轮再试即可。
102
+ """
103
+ try:
104
+ payload = yaml.safe_load(text)
105
+ except yaml.YAMLError:
106
+ return None
107
+ if not isinstance(payload, dict):
108
+ return None
109
+
110
+ info = SIGInfo(
111
+ name=str(payload.get("name") or "Kernel"),
112
+ description=str(payload.get("description") or "") or None,
113
+ mailing_list=str(payload.get("mailing_list") or "") or None,
114
+ )
115
+
116
+ entries: dict[str, RosterEntry] = {}
117
+
118
+ def merge(entry: RosterEntry | None, role: str) -> RosterEntry | None:
119
+ if entry is None:
120
+ return None
121
+ existing = entries.get(entry.key)
122
+ if existing is None:
123
+ entries[entry.key] = entry
124
+ existing = entry
125
+ # 补全字段:同一个人在不同段落出现时可能只写了 id
126
+ for attr in ("name", "atomgit_id", "gitee_id", "email", "organization"):
127
+ if not getattr(existing, attr) and getattr(entry, attr):
128
+ setattr(existing, attr, getattr(entry, attr))
129
+ if role not in existing.roles:
130
+ existing.roles.append(role)
131
+ if ROLE_RANK.get(role, 0) > ROLE_RANK.get(existing.role, 0):
132
+ existing.role = role
133
+ return existing
134
+
135
+ for raw in _as_list(payload.get("maintainers")):
136
+ merge(_person(raw), "maintainer")
137
+
138
+ for repo_entry in _as_list(payload.get("repositories")):
139
+ if not isinstance(repo_entry, dict):
140
+ continue
141
+ repo = repo_entry.get("repo")
142
+ # repo 是仓库名列表(同一份配置同时管源码仓与构建仓)
143
+ repos = [str(item) for item in _as_list(repo) if isinstance(item, str)]
144
+ info.repositories.extend(repos)
145
+
146
+ for raw in _as_list(repo_entry.get("repo_admin")):
147
+ entry = merge(_person(raw), "committer")
148
+ if entry is not None:
149
+ entry.admin_repositories.extend(repos)
150
+ for raw in _as_list(repo_entry.get("committers")):
151
+ entry = merge(_person(raw), "committer")
152
+ if entry is not None:
153
+ entry.repositories.extend(repos)
154
+ for raw in _as_list(repo_entry.get("contributors")):
155
+ entry = merge(_person(raw), "contributor")
156
+ if entry is not None:
157
+ entry.repositories.extend(repos)
158
+
159
+ for raw_branch in _as_list(payload.get("branches")):
160
+ if not isinstance(raw_branch, dict):
161
+ continue
162
+ # keeper 只有 id,没有姓名与邮箱 —— 姓名要回名单里查,
163
+ # 因此这里先存账号,解析结束后统一补名
164
+ keeper_logins = []
165
+ for raw in _as_list(raw_branch.get("keeper")):
166
+ person = _person(raw)
167
+ if person is not None:
168
+ keeper_logins.append(person.key)
169
+ for target in _as_list(raw_branch.get("repo_branch")):
170
+ if not isinstance(target, dict):
171
+ continue
172
+ branch = str(target.get("branch") or "").strip()
173
+ repo = str(target.get("repo") or "").strip()
174
+ if branch:
175
+ info.branch_keepers.append(
176
+ BranchKeeper(repo=repo, branch=branch, keepers=list(keeper_logins))
177
+ )
178
+
179
+ # keeper 若不在 committer 名单里,也要作为成员出现 —— 能管分支的人
180
+ # 不列在成员页上会让人以为名单漏了
181
+ for branch in info.branch_keepers:
182
+ for login in branch.keepers:
183
+ if login not in entries:
184
+ entries[login] = RosterEntry(name=login, role="committer", roles=["committer"])
185
+ if login.startswith("http") or "." in login:
186
+ entries[login].gitee_id = login
187
+ else:
188
+ entries[login].atomgit_id = login
189
+
190
+ for entry in entries.values():
191
+ for branch in info.branch_keepers:
192
+ if entry.key in branch.keepers and branch.branch not in entry.kept_branches:
193
+ entry.kept_branches.append(branch.branch)
194
+ entry.repositories = sorted(set(entry.repositories))
195
+ entry.admin_repositories = sorted(set(entry.admin_repositories))
196
+
197
+ info.roster = sorted(
198
+ entries.values(), key=lambda item: (-ROLE_RANK.get(item.role, 0), item.name)
199
+ )
200
+ info.repositories = sorted(set(info.repositories))
201
+ return info
File without changes
File without changes