@kernel-sig/console
v0.1.1
Published
openEuler Kernel SIG 的 PR/Issue 管理平台:一条命令在本机起一整套实例
Maintainers
Readme
Kernel SIG Console
openEuler Kernel SIG 的本地自托管协作平台,用于管理 AtomGit 上的 PR 与 Issue。
为什么需要它
openEuler 内核仓库当前有 1000+ 个开放的 PR,评审状态由 lgtm-* / approved /
ci_* 标签承载,没有统一视图。Maintainer 难以快速识别优先级、统计评审 SLA、
发现被淹没的重要 PR。
本平台把这些信息结构化,并提供 AI 辅助分析。
功能
| 模块 | 说明 | 状态 | |------|------|------| | 认证与权限 | 五级 RBAC(观察者/评审者/提交者/维护者/管理员),审计留痕 | 已实现 | | PR 管理 | 门禁阶段筛选、标题/编号检索、分支与排序、详情含提交/文件 diff/评审时间线 | 已实现 | | Issue 管理 | 按 AtomGit 原生类型与优先级筛选、状态着色 | 已实现 | | 评审跟踪 | 从评论解析评审事件,归属评审人工作量;合并门禁四段推导 | 已实现 | | 数据同步 | 分层增量同步(列表/详情/评论),幂等,支持 webhook 触发 | 已实现 | | 定时轮询 | ARQ worker 按任务错峰调度:列表 / 详情 / 分类 / 关注项 / AI 补判 | 已实现 | | 统计分析 | 积压时长、合并周期、门禁分布、分支分布、评审人排行 | 已实现 | | 凭据管理 | AES-256-GCM 加密存储、指纹展示、有效性校验 | 已实现 | | 自动分类 | 规则优先、AI 兜底,识别 CVE / Backport / 子系统归属,支持人工指定 | 已实现 | | 关注队列 | 13 条规则收敛成可认领的待办,每条附判定依据 | 已实现 | | AI 对接 | OpenAI 兼容协议、按任务路由模型、类型补判 / 摘要 / 风险审查 / backport 核对 | 已实现 |
实测数据(openeuler/kernel)
平台在真实仓库上验证,以下为某次同步的实际结果:
| 指标 | 数值 | |------|------| | 未合并 PR | 1,133 | | 已合并 PR | 4,000 | | 未关闭 Issue | 2,031 | | 可立即合入 | 179 | | 待评审 | 772 | | 超 30 天未评审 | 457 | | 合并周期中位数 | 2 天 |
「可合入」指 CLA 已签署、CI 通过、有 LGTM 且已关联 Issue —— 在这之前,这些信息散落在上千个 PR 的标签里,无法一眼看出。
关于评审状态的两个反直觉事实
实现过程中通过真实数据确认,与直觉相反,且都会导致维护者看到错误信息:
- CI 失败标签是
ci_failed,而非ci_must_go_failed(实测前者 36 次、后者 1 次)。 approved不是合入前的前置门禁:200 个 open PR 中 0 个带此标签, 而 200 个已合并 PR 中 199 个带 —— 它是合入时补记的标记。 把它当作前置条件会让「可合入」状态永不可能出现。- 评审人身份在评论里,不在标签里:PR 上用的是整体
lgtm标签, 具体是谁评审的,记录在人类的/lgtm评论中。
自动分类、关注队列与 AI
三者是同一条链路上的三环:规则能判的判定,判不出的交给模型,需要人看的排成待办。
分类:规则优先,AI 兜底,人工最高
规则是确定性的(标题里有 CVE-\d{4}-\d{4,} 就是 CVE),因此先跑规则。
实测 3164 个 open PR/Issue 上规则覆盖率约 54% —— 剩余部分不是规则写少了,
而是内核补丁标题描述的是「改了什么」而非「属于哪一类」。这部分交给模型补判。
三档优先级严格有序,且每条记录都保留判定来源与依据:
| 来源 | 何时写入 | 会被谁覆盖 | |------|----------|-----------| | 规则 | 每次同步后重算 | 规则、AI、人工 | | AI | 规则判不出时补判 | 人工;规则判不出来时不覆盖 | | 人工 | 维护者显式指定 | 只有维护者自己撤销 |
第二条约束不是可选项。规则每 10 分钟全量重算一次;若允许它把 AI 的结论打回
unknown,那条记录下一轮又会被挑去补判 —— 模型会被反复调用同一个对象,
既不收敛也持续计费。
关注队列:只给结论是没有用的
13 条规则覆盖 PR 与 Issue 两侧(评审超期、CI 失败、CVE 未推进、缺签名、 Issue 无人认领……)。每条命中都带证据:等了几天、哪个文件是二进制、 缺几个签名、涉及哪些 CVE。维护者据此可以直接决定下一步动作, 而不是只看到一个「有问题」的标记。
状态是保留而非覆盖的:命中时保留 first_detected_at,不再命中的置
resolved_at 而不删除 —— 「这条挂了多久才被处理」本身就是要看的信息。
AI:唯一按次计费的部分
- 按任务配模型:分类这类轻任务用便宜模型,代码风险审查才用强模型
- 内容指纹幂等:输入没变就不重复调用,
force=true才强制重跑 - 失败不中断:单条上游失败只落状态,不让整轮定时任务停摆; 但未配置模型属于部署问题,直接抛错,不留失败记录污染用量统计
- Prompt 内嵌领域规则:KABI 兼容性约束、补丁格式要求、backport 核对方法, 而不是让它「通用地评审一段代码」
快速开始
前提只有一个:本机装了 Docker。
npx @kernel-sig/console # 起一整套实例,首次要构建镜像、几分钟
npx @kernel-sig/console --open # 顺手打开浏览器首次运行会自动生成 ~/.kernel-sig/.env(含随机的 KSC_SECRET_KEY)、构建镜像、
起容器,就绪后打印地址。访问 http://localhost:18080 会引导创建管理员账号。
装成全局之后命令短得多,日常用这个:
npm i -g @kernel-sig/console
ksc # 再次启动,秒级
ksc logs # 看日志
ksc status # 看容器状态
ksc down # 停止;数据留在 docker 卷里,下次启动仍在新实例默认已纳管 openeuler/kernel。给个 AtomGit token 就开始拉数据:
ksc --token <你的 AtomGit token>不给也能起,只是没有数据 —— 界面上的仓库卡片会标明「未配置凭据」并指向凭据设置, 之后随时在「设置 → 凭据」里补。
注意:这是在你本机跑一个独立实例,不是连到别人的服务;和已有的实例各有各的 数据库。另外平台的定时同步需要机器常驻,合上笔记本就停了 —— 适合试用与评估, 正式给团队用仍然建议部署到服务器(见下一节)。
从源码部署
服务器上部署走这条,ksc 只是它的封装:
cp .env.example .env
# 生成密钥并写入 .env 的 KSC_SECRET_KEY
openssl rand -hex 32
make up常用命令
make help # 查看全部命令
make up # 启动
make down # 停止
make logs # 查看日志
make migrate # 执行数据库迁移
make test # 运行测试
make lint # 静态检查配置
所有配置通过环境变量提供,见 .env.example。
| 变量 | 说明 | 默认值 |
|------|------|--------|
| KSC_SECRET_KEY | 凭据加密与 JWT 签名密钥(≥32 字符),必填 | 无 |
| DOCKER_REGISTRY | 镜像源前缀 | docker.m.daocloud.io |
| PIP_INDEX_URL | 构建时的 PyPI 源 | 清华源 |
| KSC_WEB_PORT | Web 访问端口 | 18080 |
| KSC_ENVIRONMENT | development / production | production |
| KSC_BOOTSTRAP_REPOSITORY | 首次启动自动纳管的仓库(owner/name) | openeuler/kernel |
| KSC_ATOMGIT_TOKEN | 首次启动导入成凭据;不填则在界面上添加 | 空 |
端口说明:默认使用 18080 / 18000 / 15432 / 16379, 主动避开常见的 80 / 443 / 3000 / 8000 / 6379,以免与本机既有服务冲突。
镜像源说明:部分网络环境直连 Docker Hub 与 pypi.org 极慢或不可达, 因此默认走国内镜像源。其他网络环境改回官方源即可。
架构
浏览器 → Nginx (:18080) → FastAPI (:8000) → PostgreSQL
↘
ARQ Worker → AtomGit API / LLM
↘ Valkey(队列)后端分层为 API / Service / Model / Core,依赖单向:api → services → models,
integrations 不依赖任何内部模块,保证领域逻辑能被 worker 与测试独立复用。
worker 与 API 复用同一镜像、只换启动命令。迁移由 API 进程负责执行, worker 等它就绪再接活,避免两个进程同时跑 Alembic。
定时任务节奏
分钟点刻意错开:全部压在整点会让上游在瞬间收到成倍请求, 自己的数据库也会同时承受多个全量重算。
| 任务 | 频率 | 超时 |
|------|------|------|
| sync_pulls | 每 10 分钟 | 600s |
| sync_issues | 每 30 分钟 | 600s |
| sync_pull_details | 每 5 分钟(一批 50 个) | 900s |
| classify_repository | 每 10 分钟 | 600s |
| compute_attention | 每 15 分钟 | 600s |
| run_ai_analysis | 每 30 分钟(一批 5 个) | 1800s |
详情同步刻意小批量高频:单次请求量大时上游会限流, 而列表同步必须先跑完 —— 关注项规则读的是它写下的派生列。
安全
- AtomGit token 与 LLM API Key 使用 AES-256-GCM 加密落库,
密钥由
KSC_SECRET_KEY经 HKDF-SHA256 派生(不直接复用配置密钥) - 界面不回显凭据明文,仅展示指纹
- 密码使用 Argon2id 哈希
- 会话令牌存 HttpOnly + SameSite Cookie
- 所有写操作记入审计日志,凭据类字段自动脱敏
开发
# 仅启动依赖服务
docker compose up -d postgres valkey
# 后端(本机)
cd backend
python3 -m venv .venv
.venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -e ".[dev]"
.venv/bin/uvicorn app.main:app --reload --port 18000
# 前端(本机)
cd frontend && npm install && npm run dev # http://localhost:15173验证脚本
除单元测试外,仓库内有三个跑在真实数据 / 真实浏览器上的校验脚本。 单元测试证明代码符合预期,这三个脚本证明系统在真实环境下确实工作。
# 浏览器端到端(100+ 项断言,覆盖全部页面与交互)
# 需要 playwright:pip install playwright && playwright install chromium
python3 scripts/e2e-verify.py
# 分类与关注项:跑在真实数据库上,验证覆盖率与重算幂等
cd backend && KSC_SECRET_KEY=... .venv/bin/python ../scripts/verify-analysis-pipeline.py
# AI 链路:本地桩服务冒充 OpenAI 兼容端点,验证请求头/payload/
# JSON 解析/幂等/失败降级/人工覆盖优先
cd backend && KSC_SECRET_KEY=... .venv/bin/python ../scripts/verify-ai-pipeline.py后两个脚本读的是本机活实例的数据库,断言不假设环境为空; 它们验证的是数据的性质(覆盖率、幂等、不打回 AI 判定), 这些性质只有数据真实存在时才可验证。
发布到 npm(维护者)
package.json 顶部的 files 是显式白名单,不是可选项。实测 npm 10.9.8 会把
白名单里列出的目录原样整目录收进包 —— frontend/node_modules(290 MB)
就是这么做进去的,.npmignore 拦不住。所以新增构建产物类的文件时,要同步更新这份
名单,不要指望忽略文件。
发布前先看一遍清单,尤其确认里面没有 .env(那是明文密钥):
npm pack --dry-run # 看清单
npm pack # 出 tarball,可先用它本地验一遍
npm publish --access public --otp=<六位码>name@version 一旦发布就永远不能重用(即使 npm unpublish 也不行),
所以先用 npm exec --package=./kernel-sig-console-0.1.0.tgz -- ksc up 跑通再发。
许可
木兰宽松许可证,第 2 版(Mulan Permissive Software License, Version 2,
SPDX 标识符 MulanPSL-2.0)。
Copyright (c) 2026 Xie XiuQi
