Files
code-review/README.md
T
Jeffery 40ebfe99a8
AI / Code Review (pull_request) Failing after 2m48s
AI / 計算版本號 (pull_request) Successful in 2s
feat: 前置驗證納入 git push 認證檢查
git push 走 askpass + HTTP 認證,與 Gitea REST API 是兩套機制,API token
有效不代表 push 能用(曾出現 askpass 無法執行、could not read Username 而
push 失敗)。新增 git.js verifyRemoteAccess() 以相同 askpass + remote URL
跑唯讀 git ls-remote,preflight 呼叫並在失敗時 exit 1,提前攔下設定問題。

新增 git.test.js 對 verifyRemoteAccess 的測試(成功、失敗不丟例外、token
不外洩、askpass 清理)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:39:04 +08:00

254 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 簡介
這是一個 AI Code Review Action。Gitea Workflow 可以使用此 Action 讓 AI 助理根據不同面向分析 Pull Request 中變更的內容後,將問題分級 Comment 到 Pull Request 中。
# 流程(Pull Request opened / synchronize 觸發;若偵測到 AI 助理的自動提交則直接跳過)
0. 前置驗證(action 最開始執行、做任何分析或發 comment 前):檢查所有驗證相關設定是否都可用,全部通過才繼續;任何一項失敗就印出明確訊息並立即 `exit 1`
- 必要環境變數齊全:`GITEA_TOKEN``GITEA_REPOSITORY``PR_NUMBER`(缺一即失敗)
- Gitea API 可連線且 `GITEA_TOKEN` 有權限讀取此 repo(呼叫 `GET /api/v1/repos/{repo}` 驗證 token 與 repo 同時有效)
- 若有提供 `GITEA_COMMENT_TOKEN`,額外用它驗證可用(呼叫 `GET /api/v1/user`),確保後續發 comment 不會因 token 失效而中斷
- git push 認證可用:用與第 8 點 commit/push 完全相同的 askpass + remote URL 機制跑一次唯讀的 `git ls-remote`,提前抓出 askpass 無法執行或 HTTP 認證失敗(例如 `could not read Username`)的問題。此路徑與上面的 REST API 不同,API token 有效不代表 git push 一定能用,故獨立驗證
- 已選定一個 LLM provider,且其 API Key 至少有一把通過驗證:實際送出一個最小請求確認認證可用;逗號分隔的多把 Key 只要一把成功即可,逐把記錄成敗;Ollama 無 Key,改為檢查 `OLLAMA_BASE_URL` 可連線
1. 服務名稱、模型名稱、角色資訊(個性、符合個性的英文名稱、工作內容),Comment 到 Pull Request
2. 每個角色個別分析 Git Diff 的內容產生新問題表格(問題等級、角色名稱、問題位置或行數、修改建議)
3. 讀取來源分支中的所有未解決舊問題(問題檔案 `.gitea/ai-review/findings.json`)加上新問題後,去除重複產生本次 PR 的問題表格(PR問題表格)覆蓋問題檔案
4. 讀取來源分支中的排除問題檔案(`.gitea/ai-review/exclusions.json`),用來過濾 PR 問題表格中不需要處理的問題
5. 從 PR 問題表格中取出所有舊問題,依照等級排序後 Comment 到 Pull Request
6. 從 PR 問題表格中取出所有新問題,排除嚴重等級的問題後 Comment 到 Pull Request
7. 從 PR 問題表格中取出所有新問題,將每個嚴重等級的問題 Comment 到 Pull Request
8. Commit 問題檔案,將 workspace 中實際存在的同步檔覆蓋到記憶區;workspace 沒有的同步檔就略過,不會刪除記憶區既有內容。自動提交的 commit message 會帶上 `[ai-review-bot]`,供 workflow 判斷是否要跳過重跑
9. 如果 PR 問題表格中有嚴重問題,則不要讓 workflow 執行成功(exit 1)
# 設計
1. Gitea 相關參數中,`GITEA_TOKEN` 必須由 inputs 明確提供;`GITEA_SERVER_URL``GITEA_REPOSITORY``PR_NUMBER``PR_HEAD_BRANCH``PR_BASE_BRANCH` 等欄位若 inputs 沒有定義,則從 `${{ gitea.* }}` 取得
2. BASE_URL 如果 inputs 沒有定義,則使用預設值
3. Comment 加上些許 emoji 讓資訊有點活力
4. 盡量將應用程式放在 ./app,修改 entrypoint.sh 與 Dockerfile 讓程式可以正常運行
5. 將提示詞放到 ./app/prompts 內供程式讀取
6. API Key 支援逗號分隔傳入多個,隨機順序各嘗試一次,全部失敗則 exit 1
7. 讀取 Git Diff 時排除 `.gitea/``.amazonq/``.agents/``.antigravity/``.claude/``.codex/``.gemini/``.github/` 資料夾,以及 `AGENTS.md``ANTIGRAVITY.md``CLAUDE.md``GEMINI.md``TODO.md``README.md`,避免 AI 分析 workflow 設定、skill 入口與文件等非業務程式碼
8. 階段七驗證來源分支中的 `findings.json``exclusions.json` 是否為合法 JSON 格式,格式錯誤時先嘗試透過 AI 修正內容,再重新驗證;修正後仍不合法才 exit 1;之後才檢查檔案是否存在,不存在則建立並寫入 `[]`
9. 傳給 AI 的 findings 只保留必要欄位(level、role、location、suggestion),排除 `is_new` 等內部欄位;system prompt 精簡為指令核心;exclusions hint 只傳 location 與 suggestion,減少 token 用量
10. 執行時會額外記錄來源分支狀態、`findings.json` / `exclusions.json` 的檔案路徑、大小、mtime 與 raw/normalized 筆數,方便追查讀檔與分支內容不一致的問題
11. action 一啟動就先做「前置驗證」(流程第 0 點):集中檢查 Gitea REST API token、comment token、git push 認證與 LLM 的所有驗證相關設定是否可用,全部通過才往下跑。驗證邏輯獨立成 `app/preflight.js`git push 驗證委派給 `app/git.js``verifyRemoteAccess`),由 `main.js` 在 Step1 之後、其餘步驟之前呼叫;任何一項失敗都印出是哪一項、原因為何後 `exit 1`,避免在分析到一半、發 comment 或最後 push 時才因 token / key / 認證無效而中斷
# 使用說明
1. 在 Gitea 專案中建立 `.gitea/workflows` 資料夾
2.`.gitea/workflows` 資料夾中建立 `ai-review.yaml`
3.`ai-review.yaml` 中填入以下內容(選擇一個使用)
> **自動提交排除說明**:此 Action 會將自己的 commit message 標記為 `[ai-review-bot][success]` 或 `[ai-review-bot][failure]`,而且 action 執行時會先透過 Gitea API 檢查這次觸發的 PR head commit(優先用 `pull_request.head.sha`)是否含有這個 marker,若有就直接成功結束,避免 bot commit 造成重複觸發。若外層 workflow 也能先檢查一次,效果最好。
> **權限說明**:此 Action 需要 `contents: write`(寫入 findings.json)、`pull-requests: write`(發佈 PR comment)、`issues: write`(發佈 issue comment)三項權限,為正常運作所必要,無法縮減。若你想讓 comment 用不同權限的 token,可額外傳 `GITEA_COMMENT_TOKEN`,其餘 Gitea 操作仍使用 `GITEA_TOKEN`。
### 1. OpenAI
```yaml
name: AI
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
on:
pull_request:
branches-ignore:
- master
types: [opened, synchronize]
jobs:
code-review:
name: Code Review
runs-on: ubuntu
steps:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with:
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} # 支援逗號分隔多個 Key
OPENAI_BASE_URL: https://api.openai.com/v1
OPENAI_MODEL: ${{ vars.OPENAI_MODEL }}
permissions:
contents: write
pull-requests: write
issues: write
```
### 2. OpenRouter
```yaml
name: AI
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
on:
pull_request:
branches-ignore:
- master
types: [opened, synchronize]
jobs:
code-review:
name: Code Review
runs-on: ubuntu
steps:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with:
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENROUTER_API_KEY }},${{ secrets.OPENROUTER_API_KEY_1 }}
OPENAI_BASE_URL: https://openrouter.ai/api/v1
OPENAI_MODEL: ${{ vars.OPENROUTER_MODEL }}
permissions:
contents: write
pull-requests: write
issues: write
```
### 3. Anthropic Claude
```yaml
name: AI
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
on:
pull_request:
branches-ignore:
- master
types: [opened, synchronize]
jobs:
code-review:
name: Code Review
runs-on: ubuntu
steps:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with:
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }} # 支援逗號分隔多個 Key
CLAUDE_BASE_URL: https://api.anthropic.com/v1
permissions:
contents: write
pull-requests: write
issues: write
```
### 4. Google Gemini
```yaml
name: AI
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
on:
pull_request:
branches-ignore:
- master
types: [opened, synchronize]
jobs:
code-review:
name: Code Review
runs-on: ubuntu
steps:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with:
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }},${{ secrets.GEMINI_API_KEY_1 }},${{ secrets.GEMINI_API_KEY_2 }},${{ secrets.GEMINI_API_KEY_3 }},${{ secrets.GEMINI_API_KEY_4 }},${{ secrets.GEMINI_API_KEY_5 }},${{ secrets.GEMINI_API_KEY_6 }},${{ secrets.GEMINI_API_KEY_7 }},${{ secrets.GEMINI_API_KEY_8 }},${{ secrets.GEMINI_API_KEY_9 }},${{ secrets.GEMINI_API_KEY_10 }},${{ secrets.GEMINI_API_KEY_11 }},${{ secrets.GEMINI_API_KEY_12 }},${{ secrets.GEMINI_API_KEY_13 }},${{ secrets.GEMINI_API_KEY_14 }},${{ secrets.GEMINI_API_KEY_15 }},${{ secrets.GEMINI_API_KEY_16 }},${{ secrets.GEMINI_API_KEY_17 }},${{ secrets.GEMINI_API_KEY_18 }},${{ secrets.GEMINI_API_KEY_19 }}
GEMINI_BASE_URL: https://generativelanguage.googleapis.com/v1beta
GEMINI_MODEL: ${{ vars.GEMINI_MODEL }}
permissions:
contents: write
pull-requests: write
issues: write
```
### 5. Amazon Q
```yaml
name: AI
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
on:
pull_request:
branches-ignore:
- master
types: [opened, synchronize]
jobs:
code-review:
name: Code Review
runs-on: ubuntu
steps:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with:
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
AMAZONQ_API_KEY: ${{ secrets.AMAZONQ_API_KEY }} # 支援逗號分隔多個 Key
AMAZONQ_BASE_URL: https://q.api.aws
permissions:
contents: write
pull-requests: write
issues: write
```
### 6. Ollama
```yaml
name: AI
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true
on:
pull_request:
branches-ignore:
- master
types: [opened, synchronize]
jobs:
code-review:
name: Code Review
runs-on: ubuntu
steps:
- name: AI Code Review
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with:
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
GITEA_COMMENT_TOKEN: ${{ secrets.GITEA_TOKEN }}
OLLAMA_BASE_URL: https://ollama.jsc.idv.me/v1
OLLAMA_MODEL: ${{ vars.OLLAMA_MODEL }}
permissions:
contents: write
pull-requests: write
issues: write
```
## SkillTriage Findings
這份 skill 用來處理 review 問題清單。
### 規則
1. 合併問題。
2. 依嚴重度排序:`critical` -> `warning` -> `info`
3. 重新編號。
4. 真問題就修。
5. 誤判就加到 `.gitea/ai-review/exclusions.json`
6. 有變更就補測試。
### 使用方式
Codex`$triage-findings 問題原始檔(文字或截圖)`
Copilot`/triage-findings 問題原始檔(文字或截圖)`
Claude:直接輸入 `triage-findings 問題原始檔(文字或截圖)`
Gemini:直接輸入 `triage-findings 問題原始檔(文字或截圖)`
Amazon Q:直接輸入 `triage-findings 問題原始檔(文字或截圖)`
Antigravity:直接輸入 `triage-findings 問題原始檔(文字或截圖)`
### 適用情境
`triage-findings 問題原始檔(文字或截圖)` 用在 review 問題整併、排序、修正、排除誤判。
### 版本包含
提交時一併包含 `triage-findings` skill 與各平台入口檔;其中 `AGENTS.md``ANTIGRAVITY.md``CLAUDE.md``GEMINI.md` 會在目標專案已存在時先做規則化合併,並在可用 LLM 時再用 AI 輔助檢查是否有遺失任何 skill、command 或規則;其餘同步檔則以來源覆蓋;若 workspace 沒有某個同步檔,記憶區會保留原檔,不做刪除。`findings.json``exclusions.json` 都從使用此 action 的存取庫來源分支讀取,而不是從 action 本地 workspace 讀取。寫入 `.gitea/ai-review/exclusions.json` 時,盡量保留原始問題文字的語言與語意,避免過度改寫。未來若新增任何 skill 或新增其他平台的 skill 入口,必須同時把對應檔案複製進 Docker image,並把同步清單更新到會使用此 action 的目標專案,避免 action 與目標專案內容脫節。