fix(ai-review): 避免建問題模式阻擋失效與連結累積
This commit is contained in:
+44
-15
@@ -72,7 +72,7 @@ function saveFindings({ cwd, ctx, tool, kept, excluded }) {
|
||||
* 供下一回合 `main()` 步驟 1 比對辨識、直接回報結果而不重複審查。
|
||||
* 依 `commitAndPushFindings` 的回傳值記錄不同日誌:true=已 commit/push;
|
||||
* false=檔案無實際變更(空 commit 防護),記「略過 commit/push」。
|
||||
* commit/push 失敗(例如與開發者新 commit 競態)時僅記 WRN log,不拋出例外、不改變審查結果。
|
||||
* commit/push 失敗(例如與開發者新 commit 競態)時僅記 WRN log,不拋出例外;呼叫端可依回傳值決定是否阻擋。
|
||||
*
|
||||
* @param {Object} params - 解構參數。
|
||||
* @param {string} params.cwd - repo 根目錄(workspace)絕對路徑,git 操作在此目錄執行。
|
||||
@@ -84,7 +84,7 @@ function saveFindings({ cwd, ctx, tool, kept, excluded }) {
|
||||
* @param {string} params.ctx.repository - `owner/repo` 形式的 repo 名稱。
|
||||
* @param {string[]} params.files - 要 commit 的檔案 repo 相對路徑陣列(如 findings 檔、`.gitea/ai-review/exclusions.json`);全數無變更時只記 INF 略過。
|
||||
* @param {'success'|'failure'} params.result - 本回合審查結果:success=無嚴重問題、failure=有嚴重問題;會拼進 commit 訊息尾端。
|
||||
* @returns {void} 無回傳值;成敗僅反映在 log 上。
|
||||
* @returns {boolean} true=已 commit/push;false=無變更或 commit/push 失敗。
|
||||
* @remarks
|
||||
* 使用情境:`main()` 於流程尾端依 `severe.length === 0 ? 'success' : 'failure'` 決定 result、
|
||||
* 依模式組出 filesToCommit(一般模式:findings 檔+有變更時的 exclusions.json;
|
||||
@@ -107,12 +107,15 @@ function commitFindings({ cwd, ctx, files, result }) {
|
||||
});
|
||||
if (committed) {
|
||||
log('收尾', 'INF', `審查結果檔已 commit 並 push 回 ${ctx.headRef}(結果:${result})。`);
|
||||
return true;
|
||||
} else {
|
||||
log('收尾', 'INF', '審查結果檔無實際變更,略過 commit/push。');
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
// push 失敗(例如與開發者新 commit 競態)時只記錄,不改變審查結果。
|
||||
// push 失敗(例如與開發者新 commit 競態)時只記錄,交由呼叫端依嚴重度決定是否阻擋。
|
||||
log('收尾', 'WRN', `commit/push 審查結果檔失敗:${err.message}。`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +168,7 @@ async function main() {
|
||||
// 建問題模式:追蹤 issue 於「確定有保留問題」後才建立;在那之前的情境留言(工具/diff/角色)
|
||||
// 先暫存於 pendingIssueCommentBodies,建立 issue 後一次寫入。
|
||||
const pendingIssueCommentBodies = [];
|
||||
let issueModeActive = ctx.createIssue;
|
||||
let trackingIssue = null;
|
||||
/**
|
||||
* 發布一則審查留言。依模式決定去向:
|
||||
@@ -179,7 +183,7 @@ async function main() {
|
||||
* 警告/建議彙整等留言。若 Gitea API 失敗,例外會往上拋出並由主流程頂層 catch 收斂。
|
||||
*/
|
||||
const queueOrPostComment = async (body) => {
|
||||
if (ctx.createIssue) {
|
||||
if (issueModeActive) {
|
||||
if (trackingIssue) return gitea.createCommentOnIssue(ctx, trackingIssue.number, body);
|
||||
pendingIssueCommentBodies.push(body);
|
||||
return null;
|
||||
@@ -210,13 +214,27 @@ async function main() {
|
||||
}
|
||||
pendingIssueCommentBodies.length = 0;
|
||||
};
|
||||
/**
|
||||
* 建問題模式降級:追蹤 issue 無法建立或寫入時,改把已暫存的情境留言發回 PR,後續沿用一般模式。
|
||||
*
|
||||
* @returns {Promise<void>} 無回傳值;會關閉建問題模式並把 PR 留言 id 登錄到 `currentRunCommentIds`。
|
||||
*/
|
||||
const fallbackToPrComments = async () => {
|
||||
issueModeActive = false;
|
||||
trackingIssue = null;
|
||||
for (const body of pendingIssueCommentBodies) {
|
||||
const created = await gitea.createIssueComment(ctx, body);
|
||||
currentRunCommentIds.add(created.id);
|
||||
}
|
||||
pendingIssueCommentBodies.length = 0;
|
||||
};
|
||||
|
||||
// ── 步驟 2:延後執行 ───────────────────────────────────────────────────
|
||||
// 「將 PR 既有留言標記為解決」原本在此執行,但若工具偵測/diff/攻防裁決任一失敗,
|
||||
// 舊結果會先被清掉卻沒有新結果。故延後到「本回合審查已成功產生結果、發布問題留言前」
|
||||
// 才呼叫 review.resolveOldComments(見下方步驟 4 空變更路徑與步驟 9 前);
|
||||
// 屆時本回合的工具/diff/角色留言已登錄於 currentRunCommentIds,不會被誤標為過時。
|
||||
// 建問題模式全程不觸碰 PR 既有留言(審查內容改發到 issue)。
|
||||
// 建問題模式不清理 PR 既有審查內容,只在收束時標記舊追蹤 issue 連結。
|
||||
|
||||
// ── 步驟 3:偵測 AI agent 工具並留言 ──────────────────────────────────
|
||||
const tool = agents.detectTool();
|
||||
@@ -248,7 +266,7 @@ async function main() {
|
||||
if (files.length === 0) {
|
||||
// 沒有可審查的變更:保存空 findings、以 success 收場。
|
||||
// 一般模式在 PR 留言告知;建問題模式靜默通過(不建 issue、PR 也不留言,暫存的情境留言捨棄)。
|
||||
if (ctx.createIssue) {
|
||||
if (issueModeActive) {
|
||||
log('步驟4', 'INF', '建問題模式且無可審查變更:靜默通過(不建 issue、PR 不留言)。');
|
||||
} else {
|
||||
await queueOrPostComment(templates.nothingToReviewComment(ignoredCount));
|
||||
@@ -256,7 +274,7 @@ async function main() {
|
||||
await review.resolveOldComments({ ctx, gitea, currentRunCommentIds });
|
||||
}
|
||||
const relativePath = saveFindings({ cwd, ctx, tool, kept: [], excluded: [] });
|
||||
if (ctx.createIssue) {
|
||||
if (issueModeActive) {
|
||||
// 建問題模式下 findings 不進版控,且 exclusions.json 無變更 → 沒東西可提交。
|
||||
log('收尾', 'INF', '建問題模式且無可審查變更,略過 commit/push。');
|
||||
} else {
|
||||
@@ -299,7 +317,7 @@ async function main() {
|
||||
|
||||
// ── 建問題模式:確定有保留問題才建立 issue,並把暫存的情境留言一次寫入;
|
||||
// 無保留問題則不建 issue、PR 也完全不留言(靜默通過,暫存的情境留言捨棄)。 ──────
|
||||
if (ctx.createIssue) {
|
||||
if (issueModeActive) {
|
||||
if (kept.length > 0) {
|
||||
// 先依保留問題挑好標籤,於建立 issue 時一次帶入(省去「先建空標籤 issue 再補掛」的多餘 API 往返);
|
||||
// 標籤挑選失敗一律降級為不掛標籤,不阻斷建 issue 流程。
|
||||
@@ -318,7 +336,12 @@ async function main() {
|
||||
} catch (err) {
|
||||
log('建問題', 'WRN', `標籤挑選失敗(${err.message}),issue 不掛標籤。`);
|
||||
}
|
||||
await createIssueAndFlushBufferedComments(labelIds);
|
||||
try {
|
||||
await createIssueAndFlushBufferedComments(labelIds);
|
||||
} catch (err) {
|
||||
log('建問題', 'WRN', `建立或寫入追蹤 issue 失敗(${err.message}),改用 PR 留言與 findings 檔流程。`);
|
||||
await fallbackToPrComments();
|
||||
}
|
||||
} else {
|
||||
// 無保留問題 → 不建 issue、PR 也不留言(靜默通過,暫存的情境留言捨棄)。
|
||||
log('建問題', 'INF', '沒有保留的問題:靜默通過(不建 issue、PR 不留言)。');
|
||||
@@ -329,13 +352,13 @@ async function main() {
|
||||
// 延後到此可避免工具偵測/diff/攻防裁決任一失敗時舊結果先被清掉卻無新結果;
|
||||
// 本回合的工具/diff/角色留言已登錄於 currentRunCommentIds,不會被誤標為過時;
|
||||
// 嚴重/其他問題留言於本步驟之後才發布,同樣不受影響。
|
||||
if (!ctx.createIssue) {
|
||||
if (!issueModeActive) {
|
||||
await review.resolveOldComments({ ctx, gitea, currentRunCommentIds });
|
||||
}
|
||||
|
||||
// ── 步驟 9:嚴重問題留言(一般模式掛在 PR 程式碼行上;建問題模式逐條發到 issue)─
|
||||
if (severe.length > 0) {
|
||||
if (ctx.createIssue) {
|
||||
if (issueModeActive && trackingIssue) {
|
||||
await review.postSevereToIssue({ ctx, gitea, issueNumber: trackingIssue.number, severe });
|
||||
} else {
|
||||
await review.postSevereComments({ ctx, gitea, severe, cwd });
|
||||
@@ -345,7 +368,7 @@ async function main() {
|
||||
// ── 步驟 10:警告+建議——一般模式彙整為單一表格留言到 PR;
|
||||
// 建問題模式逐條發到 issue,讓每條問題都能被個別回覆。 ──
|
||||
if (others.length > 0) {
|
||||
if (ctx.createIssue) {
|
||||
if (issueModeActive && trackingIssue) {
|
||||
await review.postOthersToIssue({ ctx, gitea, issueNumber: trackingIssue.number, others });
|
||||
} else {
|
||||
await queueOrPostComment(templates.othersComment(others));
|
||||
@@ -355,7 +378,8 @@ async function main() {
|
||||
|
||||
// ── 建問題模式收束:在 PR 回貼 issue 連結(雙向關聯);僅在有嚴重問題時才讓 PR 相依於該 issue ─
|
||||
// 標籤已於建立 issue 時一次帶入(見上方 selectLabels → createIssueAndFlushBufferedComments),此處不再補掛。
|
||||
if (ctx.createIssue && trackingIssue) {
|
||||
if (issueModeActive && trackingIssue) {
|
||||
await review.resolveOldIssueLinkComments({ ctx, gitea });
|
||||
await gitea.createIssueComment(
|
||||
ctx,
|
||||
templates.prIssueLinkComment({
|
||||
@@ -385,13 +409,14 @@ async function main() {
|
||||
// 若有嚴重問題,仍 commit findings 檔產生 [failure] 結果 commit,避免相依 API 不支援時 fail-open。
|
||||
const result = severe.length === 0 ? 'success' : 'failure';
|
||||
const filesToCommit = review.resultFilesToCommit({
|
||||
createIssue: ctx.createIssue,
|
||||
createIssue: issueModeActive,
|
||||
severeCount: severe.length,
|
||||
relativePath,
|
||||
exclusionsChanged,
|
||||
});
|
||||
let resultCommitted = false;
|
||||
if (filesToCommit.length > 0) {
|
||||
commitFindings({ cwd, ctx, files: filesToCommit, result });
|
||||
resultCommitted = commitFindings({ cwd, ctx, files: filesToCommit, result });
|
||||
} else {
|
||||
log('收尾', 'INF', '建問題模式且 exclusions.json 無變更,略過 commit/push。');
|
||||
}
|
||||
@@ -399,6 +424,10 @@ async function main() {
|
||||
// 由它再觸發的下一輪在步驟 1 讀 commit 訊息時才回報失敗(exit 1)。如此失敗檢查落在帶有結果
|
||||
// 標記的最新 head 上,與合併判定一致。(result 僅用於上方 commit 訊息的結果標記。)
|
||||
if (result === 'failure') {
|
||||
if (!resultCommitted) {
|
||||
log('收尾', 'ERR', '本輪有嚴重問題,但未成功產生 [failure] 結果 commit;直接回報失敗避免 fail-open。');
|
||||
return 1;
|
||||
}
|
||||
log('收尾', 'INF', '本輪有嚴重問題:已標記結果 commit 為 [failure],失敗檢查由下一輪步驟 1 讀 commit 訊息回報。');
|
||||
}
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user