fix(diagnostics): 處理 ai review findings #32
+69
-36
@@ -90,6 +90,7 @@ function saveFindings({ cwd, ctx, tool, kept, excluded }) {
|
|||||||
* 依模式組出 filesToCommit(一般模式:findings 檔+有變更時的 exclusions.json;
|
* 依模式組出 filesToCommit(一般模式:findings 檔+有變更時的 exclusions.json;
|
||||||
* 建問題模式:只有 exclusions.json)後呼叫本函式;另在步驟 4 判定無可審查變更且非建問題模式時,
|
* 建問題模式:只有 exclusions.json)後呼叫本函式;另在步驟 4 判定無可審查變更且非建問題模式時,
|
||||||
* 也會以 result: 'success' 提交空 findings。
|
* 也會以 result: 'success' 提交空 findings。
|
||||||
|
* 推送以 `ctx.pushToken`(PAT)優先,使結果 commit 再觸發 CI、由步驟 1 快速回報;
|
||||||
* 注意 commit 訊息與模組常數 `BOT_COMMIT_PREFIX` 耦合,修改前綴會使步驟 1 的快速回報失效。
|
* 注意 commit 訊息與模組常數 `BOT_COMMIT_PREFIX` 耦合,修改前綴會使步驟 1 的快速回報失效。
|
||||||
*/
|
*/
|
||||||
function commitFindings({ cwd, ctx, files, result }) {
|
function commitFindings({ cwd, ctx, files, result }) {
|
||||||
@@ -100,6 +101,7 @@ function commitFindings({ cwd, ctx, files, result }) {
|
|||||||
message: `${BOT_COMMIT_PREFIX}[${result}]`,
|
message: `${BOT_COMMIT_PREFIX}[${result}]`,
|
||||||
files,
|
files,
|
||||||
token: ctx.token,
|
token: ctx.token,
|
||||||
|
pushToken: ctx.pushToken,
|
||||||
serverUrl: ctx.serverUrl,
|
serverUrl: ctx.serverUrl,
|
||||||
repository: ctx.repository,
|
repository: ctx.repository,
|
||||||
});
|
});
|
||||||
@@ -119,7 +121,9 @@ function commitFindings({ cwd, ctx, files, result }) {
|
|||||||
*
|
*
|
||||||
* 流程概要(步驟 2~10 描述一般模式;建問題模式差異見末段):
|
* 流程概要(步驟 2~10 描述一般模式;建問題模式差異見末段):
|
||||||
* 1. 快速回報 — 最新 commit 若為 ai-review-bot 的結果 commit([success]/[failure]),直接回報 0/1 不重審;
|
* 1. 快速回報 — 最新 commit 若為 ai-review-bot 的結果 commit([success]/[failure]),直接回報 0/1 不重審;
|
||||||
* 2. 將 PR 既有舊留言標記為解決(跳過本回合留言;建問題模式不執行此步);
|
* 2. 將 PR 既有舊留言標記為解決(跳過本回合留言;建問題模式不執行此步)——
|
||||||
|
* 此步延後到「本回合審查已成功產生結果、即將發布問題留言前」才執行,避免工具偵測/diff/
|
||||||
|
* 攻防裁決任一失敗時舊結果先被清掉卻沒有新結果(一般模式);
|
||||||
* 3. 偵測 AI 工具(antigravity/codex/claude)並留言;
|
* 3. 偵測 AI 工具(antigravity/codex/claude)並留言;
|
||||||
* 4. 讀 .reviewignore、整理 git diff 並留言(無可審查變更時:留言+保存空 findings,
|
* 4. 讀 .reviewignore、整理 git diff 並留言(無可審查變更時:留言+保存空 findings,
|
||||||
* 一般模式 commit success、建問題模式略過 commit,回傳 0);
|
* 一般模式 commit success、建問題模式略過 commit,回傳 0);
|
||||||
@@ -129,9 +133,11 @@ function commitFindings({ cwd, ctx, files, result }) {
|
|||||||
* 9. 嚴重問題逐條掛在程式碼行上留言;
|
* 9. 嚴重問題逐條掛在程式碼行上留言;
|
||||||
* 10. 警告+建議彙整為單一表格留言;
|
* 10. 警告+建議彙整為單一表格留言;
|
||||||
* 建問題模式(input: create-issue):不執行步驟 2、不觸碰 PR 既有留言;步驟 3~10 的所有留言
|
* 建問題模式(input: create-issue):不執行步驟 2、不觸碰 PR 既有留言;步驟 3~10 的所有留言
|
||||||
* 改發到追蹤 issue(工具/diff/角色留言先暫存,確定有保留問題後才建立 issue 並一次寫入,
|
* 改發到追蹤 issue(工具/diff/角色留言先暫存,確定有保留問題後先挑好標籤、連同標籤一次建立 issue
|
||||||
* 嚴重問題與警告+建議亦發到該 issue);無保留問題或無可審查變更則不建 issue、PR 也完全不留言(靜默通過);
|
* 並寫入暫存留言,嚴重問題與警告+建議再逐條發到該 issue,讓每條問題都能被個別回覆);
|
||||||
* 收束時依保留問題補掛 issue 標籤,並在 PR 回貼 issue 連結形成雙向關聯;
|
* 無保留問題或無可審查變更則不建 issue、PR 也完全不留言(靜默通過);
|
||||||
|
* 收束時在 PR 回貼 issue 連結形成雙向關聯,
|
||||||
|
* 並讓 PR 相依於該 issue(addIssueDependency,issue 完成/關閉前 PR 無法合併;需 repo 啟用問題相依功能);
|
||||||
* 收尾:組 filesToCommit —— 一般模式 commit findings 檔(+有變更的 exclusions.json)、
|
* 收尾:組 filesToCommit —— 一般模式 commit findings 檔(+有變更的 exclusions.json)、
|
||||||
* 建問題模式只 commit exclusions.json、無檔案可 commit 時略過;
|
* 建問題模式只 commit exclusions.json、無檔案可 commit 時略過;
|
||||||
* commit 訊息帶結果標記(success=無嚴重問題、failure=有嚴重問題)。
|
* commit 訊息帶結果標記(success=無嚴重問題、failure=有嚴重問題)。
|
||||||
@@ -199,17 +205,19 @@ async function main() {
|
|||||||
return created;
|
return created;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* 建問題模式:建立追蹤 issue(標題=PR 標題、本文=PR 描述+回溯 PR 的引言,先不掛標籤),
|
* 建問題模式:建立追蹤 issue(標題=PR 標題、本文=PR 描述+回溯 PR 的引言,連同挑好的標籤一次建立),
|
||||||
* 並把 `issueBuffer` 內暫存的情境留言依序寫入 issue;設定閉包變數 `issue` 供後續留言直接發到 issue。
|
* 並把 `issueBuffer` 內暫存的情境留言依序寫入 issue;設定閉包變數 `issue` 供後續留言直接發到 issue。
|
||||||
* 僅於「確定有保留問題」時呼叫一次。
|
* 僅於「確定有保留問題」時呼叫一次。標籤於建立時一次帶入,省去「先建空標籤 issue 再補掛」的多餘 API 往返。
|
||||||
*
|
*
|
||||||
|
* @param {number[]} [labelIds] - 建立 issue 時要一併掛上的標籤 id 陣列(由 `review.selectLabels` 事先挑選);
|
||||||
|
* 空陣列或省略時不掛任何標籤(`gitea.createIssue` 對空陣列不帶 labels 欄位)。
|
||||||
* @returns {Promise<void>} 無回傳值;結果反映在閉包變數 `issue` 與 issue 留言。
|
* @returns {Promise<void>} 無回傳值;結果反映在閉包變數 `issue` 與 issue 留言。
|
||||||
*/
|
*/
|
||||||
const ensureIssueCreated = async () => {
|
const ensureIssueCreated = async (labelIds = []) => {
|
||||||
issue = await gitea.createIssue(ctx, {
|
issue = await gitea.createIssue(ctx, {
|
||||||
title: ctx.prTitle || `AI Code Review:PR #${ctx.prNumber}`,
|
title: ctx.prTitle || `AI Code Review:PR #${ctx.prNumber}`,
|
||||||
body: templates.issueBody({ prNumber: ctx.prNumber, prBody: ctx.prBody }),
|
body: templates.issueBody({ prNumber: ctx.prNumber, prBody: ctx.prBody }),
|
||||||
labels: [],
|
labels: labelIds,
|
||||||
});
|
});
|
||||||
log('建問題', 'INF', `已建立追蹤 issue #${issue.number},寫入 ${issueBuffer.length} 則情境留言。`);
|
log('建問題', 'INF', `已建立追蹤 issue #${issue.number},寫入 ${issueBuffer.length} 則情境留言。`);
|
||||||
for (const body of issueBuffer) {
|
for (const body of issueBuffer) {
|
||||||
@@ -218,12 +226,12 @@ async function main() {
|
|||||||
issueBuffer.length = 0;
|
issueBuffer.length = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── 步驟 2:將 PR 既有留言標記為解決(本回合留言除外)───────────────────
|
// ── 步驟 2:延後執行 ───────────────────────────────────────────────────
|
||||||
// 僅一般模式執行;建問題模式不觸碰 PR 既有留言(審查內容改發到 issue)。
|
// 「將 PR 既有留言標記為解決」原本在此執行,但若工具偵測/diff/攻防裁決任一失敗,
|
||||||
// 早於偵測工具與所有本回合留言:先把上一回合的 bot 留言標為過時(此時尚無本回合留言)。
|
// 舊結果會先被清掉卻沒有新結果。故延後到「本回合審查已成功產生結果、發布問題留言前」
|
||||||
if (!ctx.createIssue) {
|
// 才呼叫 review.resolveOldComments(見下方步驟 4 空變更路徑與步驟 9 前);
|
||||||
await review.resolveOldComments({ ctx, gitea, currentRunCommentIds });
|
// 屆時本回合的工具/diff/角色留言已登錄於 currentRunCommentIds,不會被誤標為過時。
|
||||||
}
|
// 建問題模式全程不觸碰 PR 既有留言(審查內容改發到 issue)。
|
||||||
|
|
||||||
// ── 步驟 3:偵測 AI agent 工具並留言 ──────────────────────────────────
|
// ── 步驟 3:偵測 AI agent 工具並留言 ──────────────────────────────────
|
||||||
const tool = agents.detectTool();
|
const tool = agents.detectTool();
|
||||||
@@ -259,6 +267,8 @@ async function main() {
|
|||||||
log('步驟4', 'INF', '建問題模式且無可審查變更:靜默通過(不建 issue、PR 不留言)。');
|
log('步驟4', 'INF', '建問題模式且無可審查變更:靜默通過(不建 issue、PR 不留言)。');
|
||||||
} else {
|
} else {
|
||||||
await postComment(templates.nothingToReviewComment(ignoredCount));
|
await postComment(templates.nothingToReviewComment(ignoredCount));
|
||||||
|
// 已成功產生本回合結果留言(無可審查變更),此時才把舊留言標為過時(本回合留言已排除)。
|
||||||
|
await review.resolveOldComments({ ctx, gitea, currentRunCommentIds });
|
||||||
}
|
}
|
||||||
const relativePath = saveFindings({ cwd, ctx, tool, kept: [], excluded: [] });
|
const relativePath = saveFindings({ cwd, ctx, tool, kept: [], excluded: [] });
|
||||||
if (ctx.createIssue) {
|
if (ctx.createIssue) {
|
||||||
@@ -301,17 +311,42 @@ async function main() {
|
|||||||
log('步驟8', 'INF', `分組結果:嚴重 ${severe.length} 條、警告+建議 ${others.length} 條。`);
|
log('步驟8', 'INF', `分組結果:嚴重 ${severe.length} 條、警告+建議 ${others.length} 條。`);
|
||||||
|
|
||||||
// ── 建問題模式:確定有保留問題才建立 issue,並把暫存的情境留言一次寫入;
|
// ── 建問題模式:確定有保留問題才建立 issue,並把暫存的情境留言一次寫入;
|
||||||
// 無保留問題則不建 issue,改在 PR 留一則審查通過提示。 ──────────────────
|
// 無保留問題則不建 issue、PR 也完全不留言(靜默通過,暫存的情境留言捨棄)。 ──────
|
||||||
if (ctx.createIssue) {
|
if (ctx.createIssue) {
|
||||||
if (kept.length > 0) {
|
if (kept.length > 0) {
|
||||||
await ensureIssueCreated();
|
// 先依保留問題挑好標籤,於建立 issue 時一次帶入(省去「先建空標籤 issue 再補掛」的多餘 API 往返);
|
||||||
|
// 標籤挑選失敗一律降級為不掛標籤,不阻斷建 issue 流程。
|
||||||
|
let labelIds = [];
|
||||||
|
try {
|
||||||
|
const labels = await gitea.listLabels(ctx);
|
||||||
|
labelIds = await review.selectLabels({
|
||||||
|
tool,
|
||||||
|
model: ctx.model,
|
||||||
|
cwd,
|
||||||
|
labels,
|
||||||
|
prTitle: ctx.prTitle,
|
||||||
|
prBody: ctx.prBody,
|
||||||
|
findings: kept,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
log('建問題', 'WRN', `標籤挑選失敗(${err.message}),issue 不掛標籤。`);
|
||||||
|
}
|
||||||
|
await ensureIssueCreated(labelIds);
|
||||||
} else {
|
} else {
|
||||||
// 無保留問題 → 不建 issue、PR 也不留言(靜默通過,暫存的情境留言捨棄)。
|
// 無保留問題 → 不建 issue、PR 也不留言(靜默通過,暫存的情境留言捨棄)。
|
||||||
log('建問題', 'INF', '沒有保留的問題:靜默通過(不建 issue、PR 不留言)。');
|
log('建問題', 'INF', '沒有保留的問題:靜默通過(不建 issue、PR 不留言)。');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 步驟 9:嚴重問題留言(一般模式掛在 PR 程式碼行上;建問題模式發到 issue)─
|
// ── 步驟 2(延後執行,一般模式):審查已成功產生結果,發布問題留言前才把舊留言標為過時 ─
|
||||||
|
// 延後到此可避免工具偵測/diff/攻防裁決任一失敗時舊結果先被清掉卻無新結果;
|
||||||
|
// 本回合的工具/diff/角色留言已登錄於 currentRunCommentIds,不會被誤標為過時;
|
||||||
|
// 嚴重/其他問題留言於本步驟之後才發布,同樣不受影響。
|
||||||
|
if (!ctx.createIssue) {
|
||||||
|
await review.resolveOldComments({ ctx, gitea, currentRunCommentIds });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 步驟 9:嚴重問題留言(一般模式掛在 PR 程式碼行上;建問題模式逐條發到 issue)─
|
||||||
if (severe.length > 0) {
|
if (severe.length > 0) {
|
||||||
if (ctx.createIssue) {
|
if (ctx.createIssue) {
|
||||||
await review.postSevereToIssue({ ctx, gitea, issueNumber: issue.number, severe });
|
await review.postSevereToIssue({ ctx, gitea, issueNumber: issue.number, severe });
|
||||||
@@ -320,29 +355,20 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 步驟 10:警告+建議彙整為單一表格留言(去向由 postComment 依模式決定)──
|
// ── 步驟 10:警告+建議——一般模式彙整為單一表格留言到 PR;
|
||||||
|
// 建問題模式逐條發到 issue,讓每條問題都能被個別回覆。 ──
|
||||||
if (others.length > 0) {
|
if (others.length > 0) {
|
||||||
await postComment(templates.othersComment(others));
|
if (ctx.createIssue) {
|
||||||
log('步驟10', 'INF', `警告+建議表格留言已發布(${others.length} 條)。`);
|
await review.postOthersToIssue({ ctx, gitea, issueNumber: issue.number, others });
|
||||||
|
} else {
|
||||||
|
await postComment(templates.othersComment(others));
|
||||||
|
log('步驟10', 'INF', `警告+建議表格留言已發布(${others.length} 條)。`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 建問題模式收束:依保留問題補掛 issue 標籤,並在 PR 回貼 issue 連結(雙向關聯)─
|
// ── 建問題模式收束:在 PR 回貼 issue 連結(雙向關聯),並讓 PR 相依於該 issue ─
|
||||||
|
// 標籤已於建立 issue 時一次帶入(見上方 selectLabels → ensureIssueCreated),此處不再補掛。
|
||||||
if (ctx.createIssue && issue) {
|
if (ctx.createIssue && issue) {
|
||||||
try {
|
|
||||||
const labels = await gitea.listLabels(ctx);
|
|
||||||
const labelIds = await review.selectLabels({
|
|
||||||
tool,
|
|
||||||
model: ctx.model,
|
|
||||||
cwd,
|
|
||||||
labels,
|
|
||||||
prTitle: ctx.prTitle,
|
|
||||||
prBody: ctx.prBody,
|
|
||||||
findings: kept,
|
|
||||||
});
|
|
||||||
await gitea.addLabelsToIssue(ctx, issue.number, labelIds);
|
|
||||||
} catch (err) {
|
|
||||||
log('建問題', 'WRN', `補掛標籤失敗(${err.message}),issue 不掛標籤。`);
|
|
||||||
}
|
|
||||||
await gitea.createIssueComment(
|
await gitea.createIssueComment(
|
||||||
ctx,
|
ctx,
|
||||||
templates.issueLinkComment({
|
templates.issueLinkComment({
|
||||||
@@ -352,6 +378,13 @@ async function main() {
|
|||||||
otherCount: others.length,
|
otherCount: others.length,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
// 讓 PR 相依於此追蹤 issue:issue 完成/關閉前 PR 無法合併(需 repo 啟用「問題相依」功能)。
|
||||||
|
try {
|
||||||
|
await gitea.addIssueDependency(ctx, ctx.prNumber, issue.number);
|
||||||
|
log('建問題', 'INF', `已將 PR #${ctx.prNumber} 設為相依於 issue #${issue.number}:issue 關閉前無法合併。`);
|
||||||
|
} catch (err) {
|
||||||
|
log('建問題', 'WRN', `設定 PR 相依失敗(可能未啟用「問題相依」功能):${err.message}。`);
|
||||||
|
}
|
||||||
log('建問題', 'INF', `issue #${issue.number} 已寫入審查內容,並在 PR 回貼連結。`);
|
log('建問題', 'INF', `issue #${issue.number} 已寫入審查內容,並在 PR 回貼連結。`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-7
@@ -134,9 +134,9 @@ function createIssueComment(ctx, body) {
|
|||||||
* @returns {Promise<object[]>} 標籤物件陣列(每筆含 `id`、`name`、`color` 等欄位,
|
* @returns {Promise<object[]>} 標籤物件陣列(每筆含 `id`、`name`、`color` 等欄位,
|
||||||
* 依 Gitea API 回應而定);存取庫無標籤時為空陣列。
|
* 依 Gitea API 回應而定);存取庫無標籤時為空陣列。
|
||||||
* @throws {Error} 任一頁請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
|
* @throws {Error} 任一頁請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
|
||||||
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 收束時
|
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 於確定有保留問題後
|
||||||
* 先以本函式取得可用標籤,再交給 `review.selectLabels` 讓 AI 挑出適合的標籤子集合,
|
* 先以本函式取得可用標籤,再交給 `review.selectLabels` 讓 AI 挑出適合的標籤子集合,
|
||||||
* 最後以 `addLabelsToIssue` 補掛到追蹤 issue 上。
|
* 最後於建立追蹤 issue 時(`createIssue`)一次帶入這些標籤。
|
||||||
*/
|
*/
|
||||||
function listLabels(ctx) {
|
function listLabels(ctx) {
|
||||||
return listAll(ctx, `/repos/${ctx.owner}/${ctx.repo}/labels`);
|
return listAll(ctx, `/repos/${ctx.owner}/${ctx.repo}/labels`);
|
||||||
@@ -157,8 +157,8 @@ function listLabels(ctx) {
|
|||||||
* `html_url` 等欄位,依 Gitea API 回應而定)。
|
* `html_url` 等欄位,依 Gitea API 回應而定)。
|
||||||
* @throws {Error} 請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
|
* @throws {Error} 請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
|
||||||
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 的 `ensureIssueCreated`
|
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 的 `ensureIssueCreated`
|
||||||
* 以 PR 標題/描述為 issue 標題與本文(先不掛標籤)呼叫本函式建立追蹤問題的 issue,
|
* 以 PR 標題/描述為 issue 標題與本文,並帶入 `review.selectLabels` 事先挑好的標籤 id
|
||||||
* 之後再把審查內容留言到該 issue、並以 `addLabelsToIssue` 補掛標籤。
|
* 呼叫本函式一次建立追蹤問題的 issue(連同標籤),之後再把審查內容逐條留言到該 issue。
|
||||||
*/
|
*/
|
||||||
function createIssue(ctx, { title, body, labels }) {
|
function createIssue(ctx, { title, body, labels }) {
|
||||||
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues`, {
|
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues`, {
|
||||||
@@ -180,15 +180,42 @@ function createIssue(ctx, { title, body, labels }) {
|
|||||||
* @returns {Promise<object[]|null>} 追加後該 issue 的標籤陣列(依 Gitea API 回應而定);
|
* @returns {Promise<object[]|null>} 追加後該 issue 的標籤陣列(依 Gitea API 回應而定);
|
||||||
* `labels` 為空時回傳 `null`(未發出請求)。
|
* `labels` 為空時回傳 `null`(未發出請求)。
|
||||||
* @throws {Error} 請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
|
* @throws {Error} 請求失敗(非 2xx)由底層 `api` 丟出,錯誤附 `status`、`data`。
|
||||||
* @remarks 使用情境:建問題模式(input: create-issue)下先以空標籤建立 issue、
|
* @remarks 使用情境:為既有 issue 動態追加標籤的通用工具(不影響既有標籤)。
|
||||||
* 待防守方裁決得到保留問題後,再以 `selectLabels` 挑出的標籤 id 呼叫本函式補掛,
|
* 註:建問題模式主流程已改為「建立 issue 時一次帶入標籤」(見 `createIssue`),
|
||||||
* 讓標籤挑選能參考最終的問題清單。
|
* 不再於事後補掛;本函式保留為 Gitea 客戶端的通用能力,供需要事後追加標籤的情境使用。
|
||||||
*/
|
*/
|
||||||
function addLabelsToIssue(ctx, issueNumber, labels) {
|
function addLabelsToIssue(ctx, issueNumber, labels) {
|
||||||
if (!labels || labels.length === 0) return Promise.resolve(null);
|
if (!labels || labels.length === 0) return Promise.resolve(null);
|
||||||
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues/${issueNumber}/labels`, { labels });
|
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues/${issueNumber}/labels`, { labels });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建立「問題相依」關係:讓 URL 上的 issue/PR 相依於(被阻擋於)表單指定的 issue。
|
||||||
|
* 對應 endpoint:`POST /repos/{owner}/{repo}/issues/{issueNumber}/dependencies`
|
||||||
|
* (body 為 IssueMeta:`{index, owner, repo}`)。
|
||||||
|
*
|
||||||
|
* 語義:URL 的 issue(`issueNumber`)相依於 body 的 issue(`dependency`)——
|
||||||
|
* 在 `dependency` 關閉前,`issueNumber` 無法合併/關閉。本 endpoint 需 repo 啟用
|
||||||
|
* 「問題相依(issue dependencies)」功能,屬版本/設定相依;未啟用或不支援時 API 會回非 2xx。
|
||||||
|
*
|
||||||
|
* @param {object} ctx - 執行環境 context。必要欄位:`apiBase`、`token`、
|
||||||
|
* `owner`(repo 擁有者)、`repo`(repo 名稱)。
|
||||||
|
* @param {number|string} issueNumber - 要被阻擋的 issue/PR 編號(相依方)。
|
||||||
|
* @param {number} dependency - 作為阻擋來源的 issue 編號(同一 repo)。
|
||||||
|
* @returns {Promise<object>} 建立成功的相依關係物件(依 Gitea API 回應而定)。
|
||||||
|
* @throws {Error} 請求失敗(非 2xx,例如未啟用問題相依功能)由底層 `api` 丟出,錯誤附 `status`、`data`。
|
||||||
|
* @remarks 使用情境:建問題模式(input: create-issue)下,`main()` 建立追蹤 issue 後,
|
||||||
|
* 以本函式把「PR(`ctx.prNumber`)相依於追蹤 issue」,讓 issue 完成/關閉前 PR 無法合併;
|
||||||
|
* 呼叫端以 try/catch 降級(功能未啟用時記 WRN、不阻斷流程)。
|
||||||
|
*/
|
||||||
|
function addIssueDependency(ctx, issueNumber, dependency) {
|
||||||
|
return api(ctx, 'POST', `/repos/${ctx.owner}/${ctx.repo}/issues/${issueNumber}/dependencies`, {
|
||||||
|
index: dependency,
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.repo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 列出 PR 上的全部一般留言(自動分頁撈取,每頁 50 筆直到取完)。
|
* 列出 PR 上的全部一般留言(自動分頁撈取,每頁 50 筆直到取完)。
|
||||||
* 對應 endpoint:`GET /repos/{owner}/{repo}/issues/{prNumber}/comments`。
|
* 對應 endpoint:`GET /repos/{owner}/{repo}/issues/{prNumber}/comments`。
|
||||||
@@ -324,6 +351,7 @@ module.exports = {
|
|||||||
listLabels,
|
listLabels,
|
||||||
createIssue,
|
createIssue,
|
||||||
addLabelsToIssue,
|
addLabelsToIssue,
|
||||||
|
addIssueDependency,
|
||||||
listIssueComments,
|
listIssueComments,
|
||||||
editIssueComment,
|
editIssueComment,
|
||||||
createReview,
|
createReview,
|
||||||
|
|||||||
Reference in New Issue
Block a user