fix(ai-review): 結果推送失敗時回報工作流失敗

This commit is contained in:
Jeffery
2026-07-21 17:21:57 +08:00
parent b60f50f2b4
commit 449b17480b
6 changed files with 84 additions and 21 deletions
+18 -19
View File
@@ -4,7 +4,7 @@
console.log('================================================');
console.log('Action : AI Code Review');
console.log('用途 : AI 多角色 code review:攻擊方找問題、防守方裁決誤報,結果留言到 PR 並保存 findings');
console.log('更新時間: 2026/07/17 18:49:58');
console.log('更新時間: 2026/07/21 17:19:15');
console.log('================================================');
const fs = require('fs');
@@ -126,15 +126,14 @@ function commitFindings({ cwd, ctx, files, result }) {
* 建問題模式會把審查情境與每條 finding 發到追蹤 issue;沒有保留 finding 時不建立 issue、PR 也不留言。
* 嚴重 finding 會寫入 failure 結果 commit,警告與建議只建立追蹤資訊,不直接阻擋合併。
*
* @returns {Promise<number>} process exit code本輪「審查」一律回傳 0(不因嚴重問題直接讓檢查失敗——
* 失敗改由推出的 `[ai-review-bot][failure]` 結果 commit,於下一輪在步驟 1 讀 commit 訊息時回報);
* 回傳 1 僅發生於:步驟 1 偵測到 `[ai-review-bot][failure]` 結果 commit,或前置條件不足
* (缺 PR 編號/token、找不到 AI 工具)等無法進行審查的情況。
* @returns {Promise<number>} process exit code沒有嚴重問題且結果 commit 已成功推送時回傳 0;
* 若步驟 1 偵測到 `[ai-review-bot][failure]`、前置條件不足,或需要推送結果檔卻未成功推送,
* 回傳 1,避免 token 權限不足或遠端競態讓 workflow 靜默通過。
* @remarks
* 使用情境:由本檔尾端的頂層呼叫端執行 —— `main().then((code) => process.exit(code))`
* 非預期例外由頂層 `catch` 記 ERR log 後以 exit code 1 收場,且刻意不 commit 結果標記,
* 讓下一次 workflow 觸發時重新完整審查。警告+建議等級不影響結果標記,只有「嚴重」會使結果 commit
* 標記為 failure而「失敗檢查(exit 1)」只由步驟 1 讀到該 failure 結果 commit 時產生,審查本輪直接 exit 1。
* 標記為 failure若結果檔需要 push 卻失敗,本輪直接 exit 1,避免缺權限時無聲放行
* 建問題模式只改變問題明細的落地方式(issue 留言取代 findings 進版控),不改變上述結果標記判定。
*/
async function main() {
@@ -193,7 +192,7 @@ async function main() {
return created;
};
/**
* 建問題模式:建立追蹤 issue(標題=PR 標題、本文=PR 描述+回溯 PR 的引言,連同挑好的標籤一次建立),
* 建問題模式:開啟追蹤 issue(標題=PR 標題、本文=PR 描述+回溯 PR 的引言,連同挑好的標籤一次建立),
* 並把 `pendingIssueCommentBodies` 內暫存的情境留言依流程順序寫入 issue;
* 設定閉包變數 `trackingIssue` 供後續留言直接發到 issue。
* 僅於「確定有保留問題」時呼叫一次。標籤於建立時一次帶入,省去「先建空標籤 issue 再補掛」的多餘 API 往返。
@@ -202,17 +201,18 @@ async function main() {
* 空陣列或省略時不掛任何標籤(`gitea.createIssue` 對空陣列不帶 labels 欄位)。
* @returns {Promise<void>} 無回傳值;結果反映在閉包變數 `trackingIssue` 與 issue 留言。
*/
const createIssueAndFlushBufferedComments = async (labelIds = []) => {
const openTrackingIssue = async (labelIds = []) => {
trackingIssue = await gitea.createIssue(ctx, {
title: ctx.prTitle || `AI Code ReviewPR #${ctx.prNumber}`,
body: templates.issueBody({ prNumber: ctx.prNumber, prBody: ctx.prBody }),
labels: labelIds,
});
log('建問題', 'INF', `已建立追蹤 issue #${trackingIssue.number},寫入 ${pendingIssueCommentBodies.length} 則情境留言。`);
for (const body of pendingIssueCommentBodies) {
while (pendingIssueCommentBodies.length > 0) {
const body = pendingIssueCommentBodies[0];
await gitea.createCommentOnIssue(ctx, trackingIssue.number, body);
pendingIssueCommentBodies.shift();
}
pendingIssueCommentBodies.length = 0;
};
/**
* 建問題模式降級:追蹤 issue 無法建立或寫入時,改把已暫存的情境留言發回 PR,後續沿用一般模式。
@@ -337,7 +337,7 @@ async function main() {
log('建問題', 'WRN', `標籤挑選失敗(${err.message}),issue 不掛標籤。`);
}
try {
await createIssueAndFlushBufferedComments(labelIds);
await openTrackingIssue(labelIds);
} catch (err) {
log('建問題', 'WRN', `建立或寫入追蹤 issue 失敗(${err.message}),改用 PR 留言與 findings 檔流程。`);
await fallbackToPrComments();
@@ -377,7 +377,7 @@ async function main() {
}
// ── 建問題模式收束:在 PR 回貼 issue 連結(雙向關聯);僅在有嚴重問題時才讓 PR 相依於該 issue ─
// 標籤已於建立 issue 時一次帶入(見上方 selectLabels → createIssueAndFlushBufferedComments),此處不再補掛。
// 標籤已於建立 issue 時一次帶入(見上方 selectLabels → openTrackingIssue),此處不再補掛。
if (issueModeActive && trackingIssue) {
await review.resolveOldIssueLinkComments({ ctx, gitea });
await gitea.createIssueComment(
@@ -420,14 +420,13 @@ async function main() {
} else {
log('收尾', 'INF', '建問題模式且 exclusions.json 無變更,略過 commit/push。');
}
// 本輪「審查」一律以成功收場、不直接讓檢查失敗;有嚴重問題時已推出 [failure] 結果 commit
// 由它再觸發的下一輪在步驟 1 讀 commit 訊息時才回報失敗(exit 1)。如此失敗檢查落在帶有結果
// 標記的最新 head 上,與合併判定一致。(result 僅用於上方 commit 訊息的結果標記。)
// 需要推送結果檔卻沒成功時直接失敗;這通常代表 token 權限不足、非快轉或分支保護阻擋。
if (review.shouldFailMissingResultCommit({ filesToCommit, resultCommitted })) {
log('收尾', 'ERR', '審查結果檔需要 push 但未成功產生結果 commit;直接回報失敗避免缺權限時靜默通過。');
return 1;
}
// 嚴重問題已推出 [failure] 結果 commit 時,由它再觸發的下一輪在步驟 1 讀 commit 訊息回報失敗。
if (result === 'failure') {
if (!resultCommitted) {
log('收尾', 'ERR', '本輪有嚴重問題,但未成功產生 [failure] 結果 commit;直接回報失敗避免 fail-open。');
return 1;
}
log('收尾', 'INF', '本輪有嚴重問題:已標記結果 commit 為 [failure],失敗檢查由下一輪步驟 1 讀 commit 訊息回報。');
}
return 0;