5 Commits
Author SHA1 Message Date
kgod 7cd2c85888 fix: 所有问题都建 Issue、合并失败重试、push 与 PR 事件分流
1. 所有问题都建 Issue(原先只有阻断级才建)
   建 Issue 的判据从 blocking 改为 findings:任何等级的问题都创建/更新 Issue,
   等级体现在标题 [OCR][medium] 与标签 medium 上,阻断项在正文标注
   「(阻断合并)」。低等级问题不再丢失,也不会挡住合并。
   无任何发现时才关闭该分支的 Issue。

2. 合并失败自动重试
   Gitea 在算完 PR 可合并性之前会返回 405 Please try again later,
   原先直接放弃,晋级随机失败。现在对 405/409/5xx 按指数退避重试 4 次;
   权限不足、真实冲突等永久失败立即放弃并往 PR 留言说明。
   PR 已被合并(405 already merged)视为成功,幂等收尾。

3. push 与 PR 事件分流(本次新发现的 bug)
   合并路径原先按「是否找到关联 PR」判断,于是一个残留的 test→prd PR
   会让后续 push 被当成 PR 事件,走错分支并跳过晋级,日志还会给出
   「PR targets prd, which is not a checked branch」这种与实际不符的原因。
   现在按事件类型决定:trigger 以 pull_request 开头才走合并 PR 路径,
   push/manual 一律走晋级分支。关联 PR 仅用于评论归属。

验证:
- 直接 push test → 建出 issue #4([OCR][medium],标签 code-review,medium)
- 合并重试与幂等分支的判定表全部通过
- 事件分流判定表:push/manual → promote,pull_request.* → merge PR
2026-09-21 15:16:06 +08:00
kgod a0b15fb2d9 feat: 打通 推送→审查→晋级→关闭 Issue 的完整闭环
直接推送也能晋级
  往受保护分支直接推代码(没有 PR)时,审查通过后自动开一个
  <分支> → <管理分支> 的 PR 并合并它,而不是放弃自动合并。
  已存在同类 open PR 时复用它,不重复创建。

晋级 PR 不再被重复审查
  自动创建的 PR 带 PROMOTION_MARKER,其 pull_request 事件直接跳过。
  同一批提交在 push 时已经审过,重复审会在合并完成后凭空造出 Issue
  (此前确实产生了这样一个幽灵 Issue)。

合并即关闭该仓库全部审查 Issue
  两条触发路径:
  - 服务自己完成合并后立即关闭
  - 人在 Gitea 手动合并时,pull_request closed + merged=true 事件触发关闭
  手动合并是「代码已落地」最可靠的信号,不再依赖 push 侧的 merge 提交探测。

晋级使用 merge 而非 squash
  squash 会把晋级提交重写成全新提交,两个长期分支每晋级一次就多分叉
  一点,最终必然冲突(已在 offerpai_h5 上复现 add/add 冲突)。
  merge 保留共同祖先,下次晋级只携带新提交。

合并可等待性
  Gitea 异步计算 mergeable,原先只轮询 5 秒就放弃。改为指数退避约 30 秒,
  并区分「尚未算出」(继续等)与「确实冲突」(放弃)。失败时往 PR 留评论
  说明原因,不再只写服务日志。

验证(真实 webhook):
  推送 test → 审查通过 → 自动开 PR #9 (test→prd) → merge 合并
  → pull_request merged 事件 → 关闭 0 个待处理 Issue
  prd 顶端前进为合并提交,test/prd 保持一致,无幽灵 Issue 产生
2026-09-20 16:23:45 +08:00
kgod 34f2897888 fix: 自动安装 Webhook、修正 PR 审查范围与分支匹配语义
三个导致「提交了新 PR 但没有任何反应」的独立问题:

1. 从不创建 Webhook
   服务只被动接收事件,但配置仓库时不会去 Gitea 里建 Webhook,
   结果仓库永远收不到 push / PR 事件,看起来和坏掉一样。
   - 保存仓库时自动创建或更新 Webhook(POST /repos 返回值带 webhook 结果)
   - 新增 GET/POST /api/repos/:id/webhook 查询与修复
   - 仓库列表新增 Webhook 列,未配置可一键「修复 Webhook」
   - 新增 CR_WEBHOOK_URL,留空则推导为「Gitea 主机名 + 本服务端口」

2. 审查范围用了错的分支做基准
   resolveRange 优先取 managed_branch 而不是 job.base_ref,
   于是 PR 被拿去和一个无关分支比较;当两者内容相同就报
   「base and head resolve to the same commit」直接跳过。
   改为优先用该改动实际要合入的分支(PR 的目标分支),
   仅 push 事件回退到 managed_branch。

3. 检查分支的匹配语义反了
   原先要求 PR 的 head(功能分支)出现在 check_branches 里,
   而用户配置的是「要保护的目标分支」(如 prd/test),
   功能分支永远不会被列出,所以 PR 一律被过滤掉。
   新增 pullRequestMatches:PR 的 base 命中检查分支即审查,
   head 命中仍保留支持。这样「审查所有合入 prd/test 的改动」成立。

验证:PR offerpai/offerpai_h5#2 重跑通过,7 条内联评论、
2 条阻断、commit status failure、Issue #3 创建。
2026-09-20 14:44:26 +08:00
kgod 6dc77ce907 feat: 仓库导入、分支模型重构与审查历史摘要
仓库接入
- 新增「从 Gitea 导入」:用全局 Token 列出可见仓库,一键建配置
- 新增仓库只填 Git 地址,自动解析 owner/name,支持 https/ssh/scp 写法
- 新增「测试连接」按钮,保存前即可校验地址并拉取分支

分支模型
- 由单一 base_branch + glob 改为「一个管理分支 + 多个检查分支」
- 管理分支是唯一合并目标;检查分支全部纳入监控
- 支持从任意分支克隆创建管理分支
- 审查基准改为管理分支的 merge-base;仅目标为管理分支的 PR 才自动合并
- 旧库自动迁移:base_branch 播种 managed_branch,branch_patterns 展开为检查分支

审查历史
- 新增 review_records 表与「审查历史」页
- 记录触发来源、PR 链接、审查范围、阻断阈值、发布方式、自动合并设置、
  排除路径、LLM 模型、token 消耗、耗时、需求背景与全部审查意见
- 详情弹窗一览,支持按仓库过滤

AI 摘要
- 新增独立摘要模块(app/lib/summary.js),与代码审查提示词分离
- 专用提示词输出固定四节、500 字内的中文记录:结论/范围/问题/要点
- 与代码审查共用全局 LLM 设置;摘要失败不影响审查与合并,可单条重跑

修复
- 摘要改用内置 fetch:运行镜像没有 curl,原先 spawn curl 必然 ENOENT
- 去掉 blob:none 部分克隆并把凭据写入 .git/config:
  惰性取 blob 不会带上 per-command extraHeader,私有库会报 could not read Username

UI
- 审查背景改为多行文本域(可滚动)
- 分支改为可点选列表,管理分支高亮
- 仓库表格展示管理分支与检查分支
2026-09-20 12:38:23 +08:00
kgod ed172bf369 feat: Gitea 自动代码审查服务
基于 OpenCodeReview 的 webhook 服务:监听 Gitea 的 push 与 Pull Request 事件,
调用 OCR 审查 diff,把结果发布回 Gitea,并按阻断阈值决定是否自动合并。

主要能力:
- Push / PR 事件触发,支持分支 glob 过滤与 PR-only / push-only 范围
- PR 内联评论(按 diff 行号定位)、汇总评论、Issue 生命周期、提交状态
- 可配置阻断阈值(严重级别 / 类别 / 任意意见)
- 无阻断问题时自动合并,审查覆盖不完整时拒绝合并
- 内置 Web 后台:仓库配置、任务日志、失败重跑、连通性自检
- SQLite 持久化,worker 重启回收卡死任务,失败自动重试

实现为独立服务而非 Gitea Action:本机 act_runner 指向的实例不可达,
且后台配置与任务历史需要独立进程承载。
2026-09-20 12:09:46 +08:00