feat: 嚴重問題改用 Gitea 行內 review comment 標註檔案行數
每個新的嚴重問題改以行內 review comment 標註在問題所在的檔案與行數上, 留言內容為等級/審查員/建議;無法解析出行號(未標行號或一次列出多個 檔案),或行內留言失敗(該行不在 diff 範圍)時,降級為原本的一般 PR comment。 - gitea.js:新增 postPullReviewComment,呼叫 pull reviews API,以 new_position 對應新版檔案行號、commit_id 帶 PR_HEAD_SHA - comments.js:新增 parseLocation(支援 file:19 / file:70-82,取起始行)與 行內留言內容組裝;postNewCriticalComments 先試行內、失敗降級,deps 可注入 - 補 11 個測試(API payload、parseLocation 各情境、行內成功與兩種降級路徑) - README 更新流程第 7 步說明 app/ 測試 123 pass。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+38
-6
@@ -1,8 +1,8 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { postComment } from './gitea.js';
|
||||
import { postComment, postPullReviewComment } from './gitea.js';
|
||||
import { FINDINGS_PATH } from './config.js';
|
||||
import { ok, line } from './log.js';
|
||||
import { ok, line, warn } from './log.js';
|
||||
|
||||
const LEVEL_EMOJI = { critical: '🔴', warning: '🟡', info: '🔵' };
|
||||
const LEVEL_LABEL = { critical: '嚴重', warning: '警告', info: '建議' };
|
||||
@@ -16,6 +16,26 @@ function buildTable(findings) {
|
||||
return `| 等級 | 審查員 | 位置 | 建議 |\n|------|--------|------|------|\n${rows}`;
|
||||
}
|
||||
|
||||
const levelText = f => `${LEVEL_EMOJI[f.level] || ''} ${LEVEL_LABEL[f.level] || f.level}`.trim();
|
||||
|
||||
/**
|
||||
* 解析 finding 的 location 取出檔案與行號,供行內 comment 標註使用。
|
||||
* 支援 "file:19" 與 "file:70-82"(取起始行);無行號或含多個檔案(逗號)時回傳 null。
|
||||
*/
|
||||
export function parseLocation(location) {
|
||||
if (typeof location !== 'string') return null;
|
||||
const trimmed = location.trim();
|
||||
if (trimmed.includes(',')) return null;
|
||||
const match = trimmed.match(/^(.+?):(\d+)(?:-\d+)?$/);
|
||||
if (!match) return null;
|
||||
return { file: match[1], line: Number(match[2]) };
|
||||
}
|
||||
|
||||
/** 行內 comment 內容:等級/審查員/建議 */
|
||||
function inlineCommentBody(f) {
|
||||
return `**等級**:${levelText(f)}\n**審查員**:${f.role}\n**建議**:${f.suggestion}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 寫入 findings.json。
|
||||
* 預設寫到 workspace;若提供 mirrorDir,則同步寫入另一份供 repo commit 使用。
|
||||
@@ -61,17 +81,29 @@ export async function postNewNonCriticalComment(findings) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每個新 critical 問題各發一個 comment
|
||||
* 每個新 critical 問題各發一個 comment。
|
||||
* 優先用 Gitea 行內 review comment 標註問題檔案與行數(內容為等級/審查員/建議);
|
||||
* 若 location 無法解析出行號,或行內發布失敗(例如該行不在 diff 範圍),則降級為一般 comment。
|
||||
*/
|
||||
export async function postNewCriticalComments(findings) {
|
||||
export async function postNewCriticalComments(findings, deps = {}) {
|
||||
const { postInline = postPullReviewComment, postIssue = postComment } = deps;
|
||||
const criticals = findings.filter(f => f.is_new && f.level === 'critical');
|
||||
if (criticals.length === 0) {
|
||||
line('無新的嚴重問題,跳過');
|
||||
return;
|
||||
}
|
||||
for (const f of criticals) {
|
||||
const body = `## 🚨 嚴重問題\n\n${buildTable([f])}`;
|
||||
await postComment(body);
|
||||
const loc = parseLocation(f.location);
|
||||
if (loc) {
|
||||
try {
|
||||
await postInline({ path: loc.file, line: loc.line, body: inlineCommentBody(f) });
|
||||
ok(`嚴重問題 行內 comment 發布: [${f.role}] ${loc.file}:${loc.line}`);
|
||||
continue;
|
||||
} catch (e) {
|
||||
warn(`行內 comment 發布失敗,改用一般 comment: [${f.role}] ${f.location} error=${e.message}`);
|
||||
}
|
||||
}
|
||||
await postIssue(`## 🚨 嚴重問題\n\n${buildTable([f])}`);
|
||||
ok(`嚴重問題 comment 發布: [${f.role}] ${f.location}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user