Issue 130 - ShaoClean/Alune GitHub Wiki
Issue #130:PR/MR Review 评论、合并与关闭
- 类型:功能
- 状态:已实现,草稿 PR 待审阅;真实托管平台写入及跨平台桌面待验收
- Issue:https://github.com/ShaoClean/Alune/issues/130
- PR:https://github.com/ShaoClean/Alune/pull/133
- 最近更新:2026-09-30
- 代码基线:
9a11d77;主体实现:dee6dc4;恢复指引补充:b347a60
背景与目标
#122 已提供 PR/MR 详情、Diff 和讨论读取。Review 工作仍需跳转平台发表评论、合并或关闭。本次复用现有仓库、远端及访问令牌,补齐 GitHub、GitLab 与现有自建 GitLab 支持范围内的写操作。
范围包含 Overview 普通评论、代码单行评论、连续多行评论、确认合并及确认关闭。回复、编辑/删除评论、解决讨论、Approve / Request changes、重新打开及检出不在本次范围。
方案与取舍
评论定位
- 点击 Diff 旧行或新行行号发起评论;Shift 点击另一行扩展选区。仅允许同一文件、同一 Diff 区块、同一侧的连续行,不允许跨新增/删除两侧、缺失行或区块;二进制、截断及不可用的 Diff 不提供定位评论。
- GitHub Overview 使用 issue comments;行评论使用 pull request review comments,携带
commit_id/path/side/line,多行增加start_side/start_line。 - GitLab Overview 使用 notes;代码评论使用 discussions,携带
base_sha/start_sha/head_sha、新旧路径和行号。多行line_range的 line code 由当前路径 SHA-1 与新旧 Diff 游标生成;新增/删除行的另一侧游标不能简单替换为零。已核对 GitLab 开源实现的Diff::File#file_path、Diff::Line#legacy_id与Diff::Parser。 - 服务端验证选区确实存在于对应文件页的最新 Diff。文件请求读取前后核对版本,提交前再核对,防止正在看的代码与提交代码错位。
- 成功结果立即加入讨论并刷新远端资源;文件页展示相关代码讨论,GitLab 可在当前 Diff 中补充范围代码上下文。保留已加载文件页和当前文件,第二页文件评论后不跳回第一页文件。
合并与关闭
- 独立读取当前用户、项目设置与 PR/MR 的操作能力,界面显示阻塞原因。
- GitHub 仅展示仓库允许的 merge/squash/rebase,检查权限、草稿、冲突及 mergeable 状态;GitLab 遵循项目 merge method 和 squash option,检查
user.can_merge与detailed_merge_status。 - 合并和关闭都有确认弹窗,显示仓库 URL、编号、目标分支。合并额外显示可用方式;关闭明确说明代码不会合入目标分支。
- 提交前重新检查状态和代码版本,合并请求携带 head SHA,由平台原子核验提交并最终执行权限、检查、审批和保护规则。
- 已关闭/合并的条目不能再次执行状态操作;评论继续遵循平台权限和讨论锁定状态。旧版 GitLab 缺少完整合并条件时保守禁用合并并提示到平台核对。
身份、重试与草稿
- 每次请求绑定仓库 ID、远端名、目标地址、平台、PR/MR 编号。写入前重新读取 Git 远端并核验已保存凭据;不跟随重定向。
- 前端草稿按仓库/远端/平台/编号隔离,只保留于应用会话内存。刷新、切换条目及失败保留正文与选区;重载或关闭应用清空。
- 客户端提交互斥,服务端用操作 ID 合并并发请求并保留成功回执;相同 ID 不能改为另一个对象或内容。
- 明确的 4xx 拒绝可以使用原操作重试。已发送但无法确认结果的网络/5xx 故障保留草稿并提示先核对平台,同一操作不自动重发。去重记录在当前服务进程保留 24 小时,最多 2000 条;服务重启不保留。核对平台确认未发布后可编辑草稿重新提交;状态操作可取消弹窗并重新打开,在读取最新能力后作一次新的明确确认。同一弹窗内的重试继续使用原操作 ID。
- 已经发送的写操作不能因稍后的令牌切换撤回,因此保留其成功回执,避免把成功误报为可重试失败。切换条目后的旧响应不覆盖新条目。
验收状态
- 两平台 Overview、单行、新增/删除及连续多行定位通过适配器与 HTTP fixture 测试。
- 合并条件、确认合并/关闭、成功状态及列表同步通过本地 fixture 验证。
- 无权限、Diff 变化、远端变化、重复点击/重试和不确定网络结果有对应测试。
- 页面验证覆盖草稿恢复、权限失败重试、第二页文件上下文、提交中切换条目。
- 使用真实 GitHub/GitLab/自建实例核对平台实际评论定位及保护规则结果。
- Windows/Linux 原生桌面验收。
上述本地通过结果不等同于真实托管平台写入验收,Issue 保持开放,PR 保持草稿。
验证记录
环境:macOS arm64,Node 25.5.0,2026-09-30。API 集成使用真实 Nest、SQLite、本机 SSH 测试服务器与 Git;只模拟托管平台 HTTP 响应,所有令牌均为测试字符串。
| 命令 / 场景 | 结果 |
|---|---|
npm run build |
共享、SSH、服务端、Web、桌面 5 个任务通过;Vite 提示既有大包体积 |
npm test -w server -- --runInBand |
23 套件,265 项通过 |
npm test -w web |
194 项通过 |
npm test -w server -- --runInBand pull-request |
PR/MR 84 项通过 |
node --test apps/web/tests/pull-requests.integration.cjs |
4 项通过;GitHub/GitLab/自建 HTTPS 路径、令牌与状态同步 |
git diff --check、commitlint |
通过 |
| Ego Chromium:GitHub Overview / 11–12 行评论 | 发布后显示正文、文件与起止行号 |
| Ego Chromium:403 后重试 | 正文和选区保留;恢复权限后一次成功发布 |
| Ego Chromium:GitHub 关闭、GitLab 合并确认 | 弹窗显示正确目标;完成后状态变更且开放列表同步 |
| Ego Chromium:GitLab 多行评论 | 正确显示起止行号,文件下可展开当前范围代码 |
| Ego Chromium:98 → 94 → 98 | 94 的输入为空;返回 98 恢复其草稿 |
| Ego Chromium:第二页 file-30 评论失败与重试 | 始终保留 file-30,31 个已加载文件保留,正文成功显示于该文件讨论 |
| Ego Chromium:提交中切换到 94 | 远端记录仅写入 /issues/98/comments;94 未出现旧正文或成功状态 |
| 真实托管平台写入、原生 Windows/Linux | 未运行;未使用真实令牌、真实 PR/MR 执行写操作 |
补充验证(b347a60):结果不明时的恢复说明与重新打开确认窗口逻辑已通过服务端/Web 构建及 84 项 PR/MR 专项测试;浏览器截图和交互记录来自主体实现 dee6dc4,此补充未再次执行浏览器操作。
前后截图
只读基线:主工作区现有构建,PR/MR 页面来自 #122,使用隔离 fixture。

新增多行选区与评论框:

GitLab 合并确认:

第二页文件评论后保留上下文:

风险与回退
- 无数据结构迁移,复用命名及临时令牌。GitHub 需要 Pull requests 写权限;GitLab 需要 api 权限及对应项目角色。
- 关闭 API 不支持 SHA 条件字段,存在最后一次读取到写入间的远端竞争窗口;本实现缩短窗口并检查返回状态,平台仍是最终状态来源。
- 评论固定到已读取的提交或 diff refs;平台推进后可能成为旧版本评论。超时结果不明时先核对平台,不能假定写入失败。
- 回退代码不会撤销已发表的评论或平台状态变化。Wiki 是独立 Git 历史,代码回退时应同步更新本页。