feat(建問題模式): 審查留言改發到 issue、跳過舊留言處理並回貼 PR 連結
This commit is contained in:
+22
-78
@@ -578,30 +578,6 @@ function sortFindings(findings) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 建問題模式的就地排序:依檔案路徑、再依嚴重等級(嚴重→警告→建議)、再依起始行遞增。
|
||||
*
|
||||
* 與 {@link sortFindings}(嚴重度優先)不同,本排序以檔案路徑為第一鍵,
|
||||
* 讓 issue 上逐條留言的問題「同檔集中」,便於開發者逐檔處理。
|
||||
* 嚴重等級權重取自 `templates.SEVERITY_ORDER`;未知等級排最後。
|
||||
* 注意:直接修改傳入陣列(in-place),無回傳值。
|
||||
*
|
||||
* @param {Array<{file: string, severity: string, startLine: number}>} findings - 要排序的 finding 陣列(通常為保留問題 `kept` 的複本)。
|
||||
* @returns {void} 無回傳值;排序結果反映在傳入陣列本身。
|
||||
* @remarks
|
||||
* 使用情境:建問題模式(input: create-issue)下,{@link createIssueWithFindings}
|
||||
* 先以 `[...findings]` 複製保留問題(不動原陣列的嚴重度排序),
|
||||
* 再對複本呼叫本函式,依「檔案→嚴重度→行號」的順序逐條留言到新 issue。
|
||||
*/
|
||||
function sortFindingsForIssue(findings) {
|
||||
findings.sort(
|
||||
(a, b) =>
|
||||
a.file.localeCompare(b.file) ||
|
||||
(templates.SEVERITY_ORDER[a.severity] ?? 9) - (templates.SEVERITY_ORDER[b.severity] ?? 9) ||
|
||||
a.startLine - b.startLine,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 建問題模式:以 AI 依 PR 標題/描述與問題列表摘要,
|
||||
* 從存取庫可用標籤中挑選適合掛在追蹤 issue 上的標籤子集合。
|
||||
@@ -620,9 +596,9 @@ function sortFindingsForIssue(findings) {
|
||||
* @param {Array<Object>} params.findings - 保留的問題列表;每條取 severity/focus/file 與截斷 120 字的 problem 作為挑選依據。
|
||||
* @returns {Promise<number[]>} 挑中的標籤 id 陣列(可用標籤的子集合);無適合標籤或任何失敗時為空陣列。
|
||||
* @remarks
|
||||
* 使用情境:建問題模式(input: create-issue)下,{@link createIssueWithFindings}
|
||||
* 先呼叫 `gitea.listLabels` 取得可用標籤,再以本函式取得標籤 id 子集合,
|
||||
* 傳給 `gitea.createIssue` 讓新 issue 自動掛上合適標籤。
|
||||
* 使用情境:建問題模式(input: create-issue)下,`main()`(src/index.js)
|
||||
* 在建立追蹤 issue 後先呼叫 `gitea.listLabels` 取得可用標籤,再以本函式依保留問題
|
||||
* 挑出標籤 id 子集合,交給 `gitea.addLabelsToIssue` 補掛到 issue 上。
|
||||
*/
|
||||
async function selectLabels({ tool, model, cwd, labels, prTitle, prBody, findings }) {
|
||||
if (labels.length === 0) return [];
|
||||
@@ -670,61 +646,30 @@ ${JSON.stringify(brief)}
|
||||
}
|
||||
|
||||
/**
|
||||
* 建問題模式:把保留的審查問題建成存取庫的追蹤 issue 並逐條留言明細。
|
||||
* 建問題模式:把嚴重 findings 逐條以一般留言發到追蹤 issue。
|
||||
*
|
||||
* 流程:AI 挑標籤(`listLabels` + {@link selectLabels},失敗不掛標籤)
|
||||
* → 建立 issue(標題=PR 標題、本文=PR 描述加追溯資訊;失敗記 ERR 並回傳 null 不阻斷主流程)
|
||||
* → 複製 findings 依「檔案路徑→嚴重等級→起始行」排序({@link sortFindingsForIssue})
|
||||
* → 逐條以 `templates.issueFindingComment` 留言到 issue。
|
||||
* issue 無法把留言掛在程式碼行上(沒有 diff 定位),故改以
|
||||
* {@link templates.issueFindingComment} 在內文標明位置逐條發布——
|
||||
* 等同一般模式 PR 步驟 9 的嚴重問題,改以 issue 留言呈現。
|
||||
* findings 由呼叫端事先以 {@link sortFindings} 排序(嚴重度→檔案→行號),本函式不再排序。
|
||||
*
|
||||
* @param {Object} params - 解構參數。
|
||||
* @param {Object} params.ctx - 執行環境 context(`loadContext()` 回傳);使用 `prNumber`、`prTitle`、`prBody` 及 Gitea API 認證欄位。
|
||||
* @param {Object} params.gitea - Gitea API 模組(src/lib/gitea.js);以參數注入便於測試替換,使用 `listLabels`、`createIssue`、`createCommentOnIssue`。
|
||||
* @param {Object} params.tool - `detectTool()` 偵測到的 AI CLI 工具描述物件(挑標籤用)。
|
||||
* @param {string} params.model - 指定 AI 模型名稱;空字串=工具預設。
|
||||
* @param {string} params.cwd - agent 的工作目錄(repo 根目錄)。
|
||||
* @param {Array<Object>} params.findings - 要寫進 issue 的問題列表(通常為防守方裁決後保留的 `kept`);本函式以複本排序,不改動原陣列順序。
|
||||
* @returns {Promise<object|null>} 建立成功的 Gitea issue 物件(含 `number` 等欄位);建立 issue 失敗時為 null。
|
||||
* @throws {Error} 逐條留言(`createCommentOnIssue`)失敗時未攔截、向上拋出;列標籤與建 issue 的失敗則已於函式內降級處理。
|
||||
* @param {Object} params.ctx - 執行環境 context(`loadContext()` 回傳);供 Gitea API 認證。
|
||||
* @param {Object} params.gitea - Gitea API 模組(src/lib/gitea.js);以參數注入便於測試替換,使用 `createCommentOnIssue`。
|
||||
* @param {number} params.issueNumber - 目標追蹤 issue 的編號。
|
||||
* @param {Array<Object>} params.severe - severity 為「嚴重」的 finding 列表(已排序;呼叫端保證非空)。
|
||||
* @returns {Promise<void>} 無回傳值;結果反映在 issue 留言與日誌。
|
||||
* @throws {Error} 逐條留言(`createCommentOnIssue`)失敗時未攔截、向上拋出,由主流程頂層 catch 收斂。
|
||||
* @remarks
|
||||
* 使用情境:`main()`(src/index.js)在步驟 10 之後、收尾之前,
|
||||
* 於 `ctx.createIssue` 為 true 且 `kept.length > 0` 時呼叫本函式;
|
||||
* 此模式下問題明細已保存在 issue 留言,收尾只 commit exclusions.json、findings 檔不進版控。
|
||||
* 使用情境:建問題模式(input: create-issue)下,`main()`(src/index.js)於防守方裁決後
|
||||
* 建立追蹤 issue、寫入情境留言,再以本函式把嚴重問題逐條留言到該 issue;
|
||||
* 警告+建議則以 {@link templates.othersComment} 彙整成單一表格另發到同一 issue。
|
||||
*/
|
||||
async function createIssueWithFindings({ ctx, gitea, tool, model, cwd, findings }) {
|
||||
let labelIds = [];
|
||||
try {
|
||||
const labels = await gitea.listLabels(ctx);
|
||||
labelIds = await selectLabels({
|
||||
tool,
|
||||
model,
|
||||
cwd,
|
||||
labels,
|
||||
prTitle: ctx.prTitle,
|
||||
prBody: ctx.prBody,
|
||||
findings,
|
||||
});
|
||||
} catch (err) {
|
||||
log('建問題', 'WRN', `取得存取庫標籤失敗(${err.message}),issue 不掛標籤。`);
|
||||
async function postSevereToIssue({ ctx, gitea, issueNumber, severe }) {
|
||||
for (const finding of severe) {
|
||||
await gitea.createCommentOnIssue(ctx, issueNumber, templates.issueFindingComment(finding));
|
||||
}
|
||||
let issue;
|
||||
try {
|
||||
issue = await gitea.createIssue(ctx, {
|
||||
title: ctx.prTitle || `AI Code Review:PR #${ctx.prNumber}`,
|
||||
body: templates.issueBody({ prNumber: ctx.prNumber, prBody: ctx.prBody }),
|
||||
labels: labelIds,
|
||||
});
|
||||
} catch (err) {
|
||||
log('建問題', 'ERR', `建立 issue 失敗:${err.message}。`);
|
||||
return null;
|
||||
}
|
||||
const sorted = [...findings];
|
||||
sortFindingsForIssue(sorted);
|
||||
for (const finding of sorted) {
|
||||
await gitea.createCommentOnIssue(ctx, issue.number, templates.issueFindingComment(finding));
|
||||
}
|
||||
log('建問題', 'INF', `issue #${issue.number} 已建立並逐條留言 ${sorted.length} 條問題。`);
|
||||
return issue;
|
||||
log('步驟9', 'INF', `已將 ${severe.length} 條嚴重問題留言到 issue #${issueNumber}。`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -882,9 +827,8 @@ module.exports = {
|
||||
runDefenders,
|
||||
sortFindings,
|
||||
appendExclusions,
|
||||
sortFindingsForIssue,
|
||||
selectLabels,
|
||||
createIssueWithFindings,
|
||||
postSevereToIssue,
|
||||
resolveOldComments,
|
||||
postSevereComments,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user