feat(review): add workspace cleanup on PR close and scheduled stale cleanup

- Delete snapshot refs (refs/reviewed/pr/{n}/*) when PR is closed or merged
- Add daily 2:00 AM scheduled cleanup for mirrors/workspaces older than 3 days
- Expose deleteReviewedRefs, getMirrorPath, cleanStaleMirrors on LocalRepoManager
This commit is contained in:
jeffusion
2026-03-07 00:26:15 +08:00
committed by 路遥知码力
parent 272c832c43
commit 792ed7faa2
4 changed files with 233 additions and 0 deletions

View File

@@ -8,6 +8,8 @@ import { aiReviewService } from '../services/ai-review';
import { feishuService } from '../services/feishu';
import { PullRequestDetails, PullRequestFile, giteaService } from '../services/gitea';
import { logger } from '../utils/logger';
import { LocalRepoManager } from '../review/context/local-repo-manager';
import { SandboxExec } from '../review/context/sandbox-exec';
// Gitea webhook事件类型
@@ -95,6 +97,11 @@ async function handlePullRequestEvent(c: Context, body: any): Promise<Response>
body.action !== 'edited' &&
body.action !== 'review_requested'
) {
// PR 关闭/合并事件 → 清理审查快照 refs
if (body.action === 'closed') {
return handlePullRequestClosed(c, body);
}
return c.json({ status: 'ignored', message: '无需处理的事件类型' }, 200);
}
@@ -201,6 +208,55 @@ async function handlePullRequestEvent(c: Context, body: any): Promise<Response>
return c.json({ status: 'accepted', message: '代码审查请求已接受' }, 202);
}
/**
* 处理 PR 关闭/合并事件:清理审查快照 refs
*/
async function handlePullRequestClosed(c: Context, body: any): Promise<Response> {
const { pull_request: pullRequest, repository: repo } = body;
if (!pullRequest || !repo) {
return c.json({ status: 'ignored', message: 'PR close 无效数据' }, 200);
}
const prNumber = pullRequest.number;
const owner = repo.owner.login;
const repoName = repo.name;
logger.info('PR 已关闭,开始清理审查快照', { owner, repo: repoName, prNumber, merged: !!pullRequest.merged });
// 异步清理,不阻塞 webhook 响应
(async () => {
try {
const sandboxExec = new SandboxExec(config.review.allowedCommands);
const localRepoManager = new LocalRepoManager(
config.review.workdir,
sandboxExec,
config.review.commandTimeoutMs,
config.gitea.accessToken
);
const mirrorPath = localRepoManager.getMirrorPath(owner, repoName);
// 检查 mirror 是否存在(可能从未审查过该仓库)
const { access } = await import('node:fs/promises');
try {
await access(mirrorPath);
} catch {
return; // mirror 不存在,无需清理
}
await localRepoManager.deleteReviewedRefs(mirrorPath, prNumber);
} catch (error) {
logger.warn('PR 关闭清理失败(非致命)', {
owner,
repo: repoName,
prNumber,
error: error instanceof Error ? error.message : String(error),
});
}
})();
return c.json({ status: 'accepted', message: 'PR 关闭清理已触发' }, 200);
}
/**
* 处理提交状态更新事件
*/