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 - 审查背景改为多行文本域(可滚动) - 分支改为可点选列表,管理分支高亮 - 仓库表格展示管理分支与检查分支
This commit is contained in:
+25
-12
@@ -115,20 +115,19 @@ export class OcrRunner {
|
||||
};
|
||||
}
|
||||
|
||||
async gitClone({ cloneUrl, token, dir, extraHeader = true }) {
|
||||
const args = ["clone", "--no-tags", "--filter=blob:none"];
|
||||
async gitClone({ cloneUrl, token, dir }) {
|
||||
// No partial clone: a blob:none clone defers blob downloads to later
|
||||
// `git show` / `git grep` calls, and those lazily-fetched requests do not
|
||||
// inherit the per-command extraHeader, so private repositories fail with
|
||||
// "could not read Username". A full clone keeps every read local.
|
||||
const args = ["clone", "--no-tags"];
|
||||
const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
||||
if (token) {
|
||||
if (extraHeader) {
|
||||
env.GIT_CONFIG_COUNT = "1";
|
||||
env.GIT_CONFIG_KEY_0 = "http.extraHeader";
|
||||
env.GIT_CONFIG_VALUE_0 = `Authorization: token ${token}`;
|
||||
} else {
|
||||
const url = new URL(cloneUrl);
|
||||
url.username = "oauth2";
|
||||
url.password = token;
|
||||
cloneUrl = url.toString();
|
||||
}
|
||||
// Written into .git/config by `git clone`, so every later fetch/rebase in
|
||||
// this workspace authenticates without extra plumbing.
|
||||
env.GIT_CONFIG_COUNT = "1";
|
||||
env.GIT_CONFIG_KEY_0 = "http.extraHeader";
|
||||
env.GIT_CONFIG_VALUE_0 = `Authorization: token ${token}`;
|
||||
}
|
||||
args.push(cloneUrl, dir);
|
||||
const res = await run("git", args, { env, timeoutMs: this.gitTimeoutMs });
|
||||
@@ -137,10 +136,24 @@ export class OcrRunner {
|
||||
exitCode: res.code, stderr: res.stderr, stdout: res.stdout,
|
||||
});
|
||||
}
|
||||
// Persist the auth header for this workspace so later fetches keep working
|
||||
// even if the caller forgets to pass a token.
|
||||
if (token) {
|
||||
const cfg = await run("git", ["config", "--local", "http.extraHeader", `Authorization: token ${token}`], {
|
||||
cwd: dir, timeoutMs: 60000,
|
||||
});
|
||||
if (cfg.code !== 0) {
|
||||
throw new OcrError("failed to persist repository credentials", {
|
||||
exitCode: cfg.code, stderr: cfg.stderr, stdout: cfg.stdout,
|
||||
});
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
async fetch(dir, { refs = [], token } = {}) {
|
||||
// GIT_TERMINAL_PROMPT=0 keeps a missing credential a hard error instead of
|
||||
// a hanging prompt inside the container.
|
||||
const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
||||
if (token) {
|
||||
env.GIT_CONFIG_COUNT = "1";
|
||||
|
||||
Reference in New Issue
Block a user