@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,246 @@
1
+ """版本、分支、会议、成员的对外 Schema。"""
2
+
3
+ import uuid
4
+ from datetime import date, datetime
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+ from app.models.classification import PRKind
9
+
10
+
11
+ class BranchSummary(BaseModel):
12
+ """分支/版本概览。"""
13
+
14
+ name: str
15
+ raw_names: list[str]
16
+ """库中实际出现过的写法。构造 PR 列表链接时用它们做筛选值。"""
17
+
18
+ kind: str
19
+ series: str | None = None
20
+ sp: int | None = None
21
+ upstream_base: str | None = None
22
+ state: str = "active"
23
+ note: str | None = None
24
+ keepers: list[str] = []
25
+
26
+ open_count: int = 0
27
+ merged_count: int = 0
28
+ merged_recent: int = 0
29
+ last_merged_at: datetime | None = None
30
+
31
+ latest_tag: str | None = None
32
+ last_report_on: date | None = None
33
+
34
+
35
+ class MergeBucketRead(BaseModel):
36
+ label: str
37
+ start: date
38
+ end: date
39
+ total: int = 0
40
+ by_kind: dict[str, int] = {}
41
+
42
+
43
+ class PeriodSummaryRead(BaseModel):
44
+ label: str
45
+ start: date
46
+ end: date
47
+ total: int = 0
48
+ cve_count: int = 0
49
+ by_branch: dict[str, int] = {}
50
+ by_kind: dict[str, int] = {}
51
+ top_contributors: list[tuple[str, int]] = []
52
+
53
+
54
+ class ReleaseReportPatchRead(BaseModel):
55
+ model_config = ConfigDict(from_attributes=True)
56
+
57
+ sequence: int
58
+ group_name: str | None
59
+ title: str
60
+ pull_number: int | None
61
+ url: str | None
62
+
63
+
64
+ class ReleaseReportRead(BaseModel):
65
+ """一期一分支的版本进展报告。既是双周合入总结,也是该版本的 release notes。"""
66
+
67
+ id: uuid.UUID
68
+ branch: str
69
+ from_tag: str | None
70
+ to_tag: str | None
71
+ patch_count: int | None
72
+ listed_count: int
73
+ iso_urls: list[str] | None
74
+ cve_ids: list[str] | None
75
+ groups: list[dict] | None = None
76
+ meeting_held_on: date | None = None
77
+
78
+
79
+ class PullRef(BaseModel):
80
+ """议题里引用的 PR。
81
+
82
+ 只给出编号无法跳转 —— 详情页的路由用的是内部 id。
83
+ 服务端在返回整场会议时一次性把编号解析成 id,前端就不必为每个链接
84
+ 再发一次请求。
85
+ """
86
+
87
+ number: int
88
+ id: uuid.UUID | None = None
89
+ title: str | None = None
90
+ state: str | None = None
91
+ kind: PRKind | None = None
92
+
93
+
94
+ class MeetingAgendaRead(BaseModel):
95
+ model_config = ConfigDict(from_attributes=True)
96
+
97
+ id: uuid.UUID
98
+ sequence: int
99
+ num: str | None
100
+ title: str
101
+ owner: str | None
102
+ body: str | None
103
+ pull_numbers: list[int] | None
104
+ issue_numbers: list[int] | None
105
+ pull_refs: list[PullRef] = []
106
+
107
+
108
+ class MeetingSummary(BaseModel):
109
+ model_config = ConfigDict(from_attributes=True)
110
+
111
+ id: uuid.UUID
112
+ held_on: date
113
+ start_time: str | None
114
+ title: str
115
+ host: str | None
116
+ next_host: str | None
117
+ host_order: list[str] | None
118
+ meeting_url: str | None
119
+ minutes_url: str | None
120
+ status: str
121
+ agenda_count: int = 0
122
+ report_count: int = 0
123
+
124
+
125
+ class MeetingDetail(MeetingSummary):
126
+ legacy: str | None = None
127
+ agendas: list[MeetingAgendaRead] = []
128
+ reports: list[ReleaseReportRead] = []
129
+
130
+
131
+ class MemberRead(BaseModel):
132
+ model_config = ConfigDict(from_attributes=True)
133
+
134
+ id: uuid.UUID
135
+ name: str
136
+ role: str
137
+ roles: list[str] | None = None
138
+ atomgit_id: str | None
139
+ gitee_id: str | None
140
+ github_id: str | None
141
+ email: str | None
142
+ org: str | None
143
+ is_active: bool
144
+ repositories: list[str] | None = None
145
+ admin_repositories: list[str] | None = None
146
+ kept_branches: list[str] | None = None
147
+
148
+
149
+ class SubsystemOwnerRead(BaseModel):
150
+ model_config = ConfigDict(from_attributes=True)
151
+
152
+ id: uuid.UUID
153
+ module: str
154
+ section: str | None
155
+ paths: list[str] | None
156
+ committer_logins: list[str] | None
157
+
158
+
159
+ class MemberStats(BaseModel):
160
+ """成员贡献统计。按需现算,不落库。"""
161
+
162
+ login: str
163
+ name: str | None = None
164
+ role: str | None = None
165
+ authored: int = 0
166
+ """提交的 PR 数。"""
167
+
168
+ merged: int = 0
169
+ reviews: int = 0
170
+ """产生的评审事件数(LGTM / NACK 等)。"""
171
+
172
+ subsystems: list[str] = []
173
+
174
+
175
+ class KindCountRead(BaseModel):
176
+ kind: PRKind
177
+ count: int
178
+
179
+
180
+ class AttentionSampleRead(BaseModel):
181
+ number: int
182
+ title: str
183
+ subject_type: str
184
+ subject_id: str
185
+ url: str | None
186
+ severity: str
187
+ detail: str | None
188
+ age_days: int | None
189
+ # 认领作用于关注项本身,而不是被关注的对象 —— 界面据此调
190
+ # /attention/{id}/acknowledge
191
+ item_id: str
192
+ acknowledged_by: str | None = None
193
+
194
+
195
+ class AttentionThemeRead(BaseModel):
196
+ name: str
197
+ count: int
198
+ samples: list[AttentionSampleRead] = []
199
+
200
+
201
+ class AttentionGroupRead(BaseModel):
202
+ """一条规则的聚合结果。"""
203
+
204
+ rule: str
205
+ label: str
206
+ meaning: str
207
+ action: str
208
+ subject: str
209
+ count: int
210
+ severity: str
211
+ oldest_days: int | None
212
+ samples: list[AttentionSampleRead] = []
213
+ themes: list[AttentionThemeRead] = []
214
+ high_volume: bool = False
215
+ # 已被认领的条数。被认领的未必落在样例里,所以单独统计
216
+ acknowledged_count: int = 0
217
+
218
+
219
+ class AttentionSummaryRead(BaseModel):
220
+ """首页摘要。分组与计数出自同一次查询,两个数字不会互相矛盾。"""
221
+
222
+ total: int
223
+ group_count: int
224
+ by_severity: dict[str, int]
225
+ blocking: int
226
+ top: list[AttentionGroupRead] = []
227
+
228
+
229
+ class AttentionSettingRead(BaseModel):
230
+ rule: str
231
+ label: str
232
+ meaning: str
233
+ action: str
234
+ subject: str
235
+ enabled: bool
236
+ days: int
237
+ default_days: int
238
+ subject_scope: str
239
+ high_volume: bool
240
+ open_count: int = 0
241
+
242
+
243
+ class AttentionSettingUpdate(BaseModel):
244
+ enabled: bool | None = None
245
+ days: int | None = Field(default=None, ge=0, le=3650)
246
+ subject_scope: str | None = Field(default=None, pattern="^(all|pull_request|issue)$")
@@ -0,0 +1,79 @@
1
+ """用户 Schema。
2
+
3
+ ``Role`` 是 ``IntEnum``(用于角色强弱比较),但对外一律以**名称**传输
4
+ (``"ADMIN"`` 而非 ``50``)—— 整数会随枚举顺序调整而变化,不能作为对外契约。
5
+ """
6
+
7
+ import uuid
8
+ from datetime import datetime
9
+ from typing import Annotated, Any
10
+
11
+ from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, PlainSerializer, field_validator
12
+
13
+ from app.core.permissions import Permission, Role
14
+
15
+
16
+ def _parse_role(value: Any) -> Any:
17
+ """接受角色名或数值,统一转为 Role。"""
18
+ if isinstance(value, str) and not value.lstrip("-").isdigit():
19
+ try:
20
+ return Role[value.upper()]
21
+ except KeyError as exc:
22
+ raise ValueError(f"未知角色:{value}") from exc
23
+ return value
24
+
25
+
26
+ RoleField = Annotated[
27
+ Role,
28
+ BeforeValidator(_parse_role),
29
+ PlainSerializer(lambda role: role.name, return_type=str),
30
+ ]
31
+
32
+
33
+ class UserRead(BaseModel):
34
+ model_config = ConfigDict(from_attributes=True)
35
+
36
+ id: uuid.UUID
37
+ username: str
38
+ display_name: str
39
+ email: str
40
+ role: RoleField
41
+ atomgit_login: str | None
42
+ is_active: bool
43
+ last_login_at: datetime | None
44
+ created_at: datetime
45
+
46
+
47
+ class CurrentUser(UserRead):
48
+ """当前登录用户,附带计算出的权限列表。"""
49
+
50
+ permissions: list[Permission] = Field(default_factory=list)
51
+
52
+
53
+ class UserCreate(BaseModel):
54
+ username: str = Field(min_length=3, max_length=64)
55
+ display_name: str = Field(min_length=1, max_length=128)
56
+ email: str = Field(min_length=3, max_length=255)
57
+ password: str = Field(min_length=8, max_length=256)
58
+ role: RoleField = Role.VIEWER
59
+ atomgit_login: str | None = Field(default=None, max_length=64)
60
+
61
+ @field_validator("username")
62
+ @classmethod
63
+ def _validate_username(cls, v: str) -> str:
64
+ # 用户名会被用于日志与审计检索,限制字符集避免歧义
65
+ if not v.replace("_", "").replace("-", "").isalnum():
66
+ raise ValueError("用户名只能包含字母、数字、下划线与连字符")
67
+ return v
68
+
69
+
70
+ class UserUpdate(BaseModel):
71
+ display_name: str | None = Field(default=None, min_length=1, max_length=128)
72
+ role: RoleField | None = None
73
+ atomgit_login: str | None = Field(default=None, max_length=64)
74
+ is_active: bool | None = None
75
+
76
+
77
+ class PasswordChange(BaseModel):
78
+ current_password: str = Field(min_length=1, max_length=256)
79
+ new_password: str = Field(min_length=8, max_length=256)
File without changes