feat(ai-code-review): 建問題模式導向 issue,並整併流程調整、文件與 CI #9

Closed
opened 2026-07-20 07:26:21 +00:00 by gitea-actions · 18 comments

變更摘要

本 PR 將 ai-code-review action 分支的完整成果併入 develop,主要含四塊:

  1. 建問題模式重構(feat)create-issue: 'true' 時,審查留言(工具/diff/角色/嚴重問題/警告+建議)全部改發到追蹤 issue、不再張貼到 PR;不執行「舊留言標過時/resolve」;issue 延後到「確定有保留問題」才建立;無保留問題或無可審查變更則不建 issue、PR 也完全不留言(靜默通過);有問題時最後在 PR 回貼一則 issue 連結並補掛標籤,形成雙向關聯。
  2. 審查流程步驟調整(refactor):將「標記舊留言過時/resolve」提前到偵測 AI 工具之前,並依新順序重編所有步驟編號與說明。
  3. 修正(gitrepo):補抓淺層 checkout 的 base 歷史,避免 merge-base 計算失敗。
  4. 文件與 CI:補齊 action.ymlsrc 各模組文件、重建 readme.md,新增 .gitea/workflows/ci.yaml(多工具 code review 驗證)並啟用建問題模式。

影響範圍

範圍 說明
action 執行流程 src/index.js main() 依模式分流:留言去向、跳過 resolveOldComments、issue 延後建立、severe/others 導向 issue、有問題才在 PR 回貼連結
Gitea API src/lib/gitea.js 新增 addLabelsToIssue
留言模板 src/lib/templates.js 新增 issueLinkComment
審查核心 src/lib/review.js 新增 postSevereToIssue,移除 createIssueWithFindingssortFindingsForIssue
git base 解析 src/lib/gitrepo.js 補抓淺層 checkout 的 base 歷史
文件與 CI readme.mdaction.yml.gitea/workflows/ci.yaml.gitea/workflows/readme.md

風險與注意事項

  • 建問題模式行為明顯改變:啟用後 PR 上不再有審查留言(有問題時改為 issue + 一則連結留言;無問題時完全不留言),請確認團隊流程可接受。
  • 端對端行為需靠 CI 實測:本地僅完成語法與模組載入驗證,實際 issue/PR 互動需一次 CI 觸發確認。
  • readme.md 尚未反映「建問題模式導向 issue」的最新行為與新/移除的函式,建議後續以 doc-funcs 重建文件。
  • 各檔頭部「更新時間」時間戳仍為 2026/07/17 18:49:58,未刷新。

本問題由 AI Code Review 依 PR #6 的審查結果自動建立,問題明細見下方留言。

<!-- ai-code-review --> ## 變更摘要 本 PR 將 ai-code-review action 分支的完整成果併入 `develop`,主要含四塊: 1. **建問題模式重構(feat)**:`create-issue: 'true'` 時,審查留言(工具/diff/角色/嚴重問題/警告+建議)全部改發到追蹤 issue、不再張貼到 PR;不執行「舊留言標過時/resolve」;issue 延後到「確定有保留問題」才建立;**無保留問題或無可審查變更則不建 issue、PR 也完全不留言(靜默通過)**;有問題時最後在 PR 回貼一則 issue 連結並補掛標籤,形成雙向關聯。 2. **審查流程步驟調整(refactor)**:將「標記舊留言過時/resolve」提前到偵測 AI 工具之前,並依新順序重編所有步驟編號與說明。 3. **修正(gitrepo)**:補抓淺層 checkout 的 base 歷史,避免 merge-base 計算失敗。 4. **文件與 CI**:補齊 `action.yml`/`src` 各模組文件、重建 `readme.md`,新增 `.gitea/workflows/ci.yaml`(多工具 code review 驗證)並啟用建問題模式。 ## 影響範圍 | 範圍 | 說明 | | --- | --- | | action 執行流程 | `src/index.js` `main()` 依模式分流:留言去向、跳過 resolveOldComments、issue 延後建立、severe/others 導向 issue、有問題才在 PR 回貼連結 | | Gitea API | `src/lib/gitea.js` 新增 `addLabelsToIssue` | | 留言模板 | `src/lib/templates.js` 新增 `issueLinkComment` | | 審查核心 | `src/lib/review.js` 新增 `postSevereToIssue`,移除 `createIssueWithFindings`、`sortFindingsForIssue` | | git base 解析 | `src/lib/gitrepo.js` 補抓淺層 checkout 的 base 歷史 | | 文件與 CI | `readme.md`、`action.yml`、`.gitea/workflows/ci.yaml`、`.gitea/workflows/readme.md` | ## 風險與注意事項 - **建問題模式行為明顯改變**:啟用後 PR 上不再有審查留言(有問題時改為 issue + 一則連結留言;無問題時完全不留言),請確認團隊流程可接受。 - **端對端行為需靠 CI 實測**:本地僅完成語法與模組載入驗證,實際 issue/PR 互動需一次 CI 觸發確認。 - `readme.md` 尚未反映「建問題模式導向 issue」的最新行為與新/移除的函式,建議後續以 doc-funcs 重建文件。 - 各檔頭部「更新時間」時間戳仍為 `2026/07/17 18:49:58`,未刷新。 --- > 本問題由 AI Code Review 依 PR #6 的審查結果自動建立,問題明細見下方留言。

🤖 AI Code Review|審查工具

項目 內容
工具 codex
版本 codex-cli 0.144.6
模型 (工具預設)
審查 commit b2129e0bc6c533cc5cfabd198698950377495966
Run Job #21
flowchart LR
    A[整理 git diff] --> B[⚔️ 攻擊方找問題]
    B --> C[🛡️ 防守方裁決]
    C --> D[保存 findings]
    D --> E[留言到 PR]
<!-- ai-code-review --> ## 🤖 AI Code Review|審查工具 | 項目 | 內容 | | --- | --- | | 工具 | `codex` | | 版本 | `codex-cli 0.144.6` | | 模型 | (工具預設) | | 審查 commit | `b2129e0bc6c533cc5cfabd198698950377495966` | | Run Job | [#21](https://gitea.jsc.idv.tw/node-actions/ai-code-review/actions/runs/1589) | ```mermaid flowchart LR A[整理 git diff] --> B[⚔️ 攻擊方找問題] B --> C[🛡️ 防守方裁決] C --> D[保存 findings] D --> E[留言到 PR] ```

📋 變更摘要(送審 git diff)

檔案 用途 git diff 長度 最後更新時間
action.yml 定義 Action 輸入與執行入口 95 行/4205 字元 2026/07/20 15:00:23
readme.md 說明專案功能、設定與審查流程 274 行/23780 字元(過長截斷送審) 2026/07/20 09:46:12
src/index.js 編排多角色程式碼審查主流程 320 行/14204 字元 2026/07/20 15:00:15
src/lib/agents.js 偵測並執行可用的 AI 工具 14 行/503 字元 2026/07/20 09:46:12
src/lib/context.js 載入事件與 Action 執行環境 31 行/1398 字元 2026/07/20 15:00:23
src/lib/gitea.js 封裝 Gitea 留言與議題 API 143 行/6234 字元 2026/07/20 15:00:15
src/lib/gitrepo.js 處理 Git 差異、歷史與提交推送 177 行/7374 字元 2026/07/20 14:59:49
src/lib/review.js 執行審查、裁決與結果整理 578 行/24706 字元(過長截斷送審) 2026/07/20 14:59:38
src/lib/roles.js 載入並分類攻防審查角色 14 行/586 字元 2026/07/20 09:46:12
src/lib/templates.js 產生審查留言與表格模板 165 行/5949 字元 2026/07/20 15:00:32

共 10 個檔案納入審查;另有 6 個檔案依 .reviewignore 排除。

<!-- ai-code-review --> ## 📋 變更摘要(送審 git diff) | 檔案 | 用途 | git diff 長度 | 最後更新時間 | | --- | --- | --- | --- | | `action.yml` | 定義 Action 輸入與執行入口 | 95 行/4205 字元 | 2026/07/20 15:00:23 | | `readme.md` | 說明專案功能、設定與審查流程 | 274 行/23780 字元(過長截斷送審) | 2026/07/20 09:46:12 | | `src/index.js` | 編排多角色程式碼審查主流程 | 320 行/14204 字元 | 2026/07/20 15:00:15 | | `src/lib/agents.js` | 偵測並執行可用的 AI 工具 | 14 行/503 字元 | 2026/07/20 09:46:12 | | `src/lib/context.js` | 載入事件與 Action 執行環境 | 31 行/1398 字元 | 2026/07/20 15:00:23 | | `src/lib/gitea.js` | 封裝 Gitea 留言與議題 API | 143 行/6234 字元 | 2026/07/20 15:00:15 | | `src/lib/gitrepo.js` | 處理 Git 差異、歷史與提交推送 | 177 行/7374 字元 | 2026/07/20 14:59:49 | | `src/lib/review.js` | 執行審查、裁決與結果整理 | 578 行/24706 字元(過長截斷送審) | 2026/07/20 14:59:38 | | `src/lib/roles.js` | 載入並分類攻防審查角色 | 14 行/586 字元 | 2026/07/20 09:46:12 | | `src/lib/templates.js` | 產生審查留言與表格模板 | 165 行/5949 字元 | 2026/07/20 15:00:32 | > 共 10 個檔案納入審查;另有 6 個檔案依 `.reviewignore` 排除。

⚔️ 攻擊方登場

角色 面向 個性
🗡️ Assassin 安全性(security) 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用
🎼 Bard 風格(style) 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版
🧰 Leo 可維護性(maintainability) 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來
🔮 Mage 邏輯(logic) 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」
🧪 Maya 測試(testing) 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑
Rogue 效率(efficiency) 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」
<!-- ai-code-review --> ## ⚔️ 攻擊方登場 | 角色 | 面向 | 個性 | | --- | --- | --- | | 🗡️ **Assassin** | 安全性(security) | 多疑偏執、以攻擊者視角看世界,假設每筆輸入都是惡意的,每個信任都會被濫用 | | 🎼 **Bard** | 風格(style) | 唯美龜毛、追求優雅,把可讀性與一致性當作旋律,最受不了走調的命名與排版 | | 🧰 **Leo** | 可維護性(maintainability) | 有遠見、重視長期維護成本,凡事先問「六個月後的自己還看得懂嗎?」,討厭把債留給未來 | | 🔮 **Mage** | 邏輯(logic) | 嚴謹冷靜、滴水不漏,凡事推演到最壞情況,深信「沒驗證過的假設都是 bug」 | | 🧪 **Maya** | 測試(testing) | 對測試覆蓋率有執念,深信「沒有測試的程式碼等於沒寫完」,溫和但堅持,最在意邊界與失敗路徑 | | ⚡ **Rogue** | 效率(efficiency) | 急性子、講求速度,最痛恨被浪費的 CPU 週期與記憶體,凡事先問「這能不能更快、更省」 |

🛡️ 防守方登場

角色 面向 個性
🛡️ Paladin 裁決(verdict) 沉穩公正、就事論事,不護短也不冤枉,只依排除事項與原始碼脈絡裁定問題成立與否
<!-- ai-code-review --> ## 🛡️ 防守方登場 | 角色 | 面向 | 個性 | | --- | --- | --- | | 🛡️ **Paladin** | 裁決(verdict) | 沉穩公正、就事論事,不護短也不冤枉,只依排除事項與原始碼脈絡裁定問題成立與否 |

🔴 嚴重|🗡️ Assassin

位置src/lib/gitrepo.js 第 273–278 行

問題描述

攻擊者只要讓 PAT 推送失敗,git 執行錯誤就可能把含 pushToken 的完整遠端 URL收進錯誤訊息;例外再往上傳到主流程日誌後,具寫入權限的 PAT 便會暴露於 CI 紀錄。註解宣稱不輸出 URL 並不能阻止底層程序錯誤回顯命令列。

修改建議

不要把憑證放進 Git 命令列或遠端 URL。改用暫時的 GIT_ASKPASS/credential helper,並在所有錯誤跨越日誌邊界前強制移除 PAT;暫存憑證檔須設為僅擁有者可讀並於 finally 清除。

建議寫法

// 以 GIT_ASKPASS 或一次性 credential helper 提供 pushToken;
// git push 的參數只保留不含憑證的遠端 URL。
git(cwd, 'push', safeRemoteUrl, `HEAD:refs/heads/${headRef}`, {
  env: buildAskPassEnv(pushToken),
});
<!-- ai-code-review --> ### 🔴 嚴重|🗡️ Assassin **位置**:`src/lib/gitrepo.js` 第 273–278 行 **問題描述** 攻擊者只要讓 PAT 推送失敗,`git` 執行錯誤就可能把含 `pushToken` 的完整遠端 URL收進錯誤訊息;例外再往上傳到主流程日誌後,具寫入權限的 PAT 便會暴露於 CI 紀錄。註解宣稱不輸出 URL 並不能阻止底層程序錯誤回顯命令列。 **修改建議** 不要把憑證放進 Git 命令列或遠端 URL。改用暫時的 `GIT_ASKPASS`/credential helper,並在所有錯誤跨越日誌邊界前強制移除 PAT;暫存憑證檔須設為僅擁有者可讀並於 `finally` 清除。 **建議寫法** ``` // 以 GIT_ASKPASS 或一次性 credential helper 提供 pushToken; // git push 的參數只保留不含憑證的遠端 URL。 git(cwd, 'push', safeRemoteUrl, `HEAD:refs/heads/${headRef}`, { env: buildAskPassEnv(pushToken), }); ```

🟠 警告|🧰 Leo

位置src/index.js 第 121–145 行

問題描述

流程步驟編號被硬編碼在主流程註解、日誌字串、README 與多個函式 JSDoc 中;本次僅插入一個步驟,就必須同步修改大量檔案。這已顯示文件與實作存在高耦合,未來再次調整流程時很容易漏改,造成日誌、文件與實際執行順序互相矛盾。

修改建議

共用函式的 JSDoc 改以語意階段名稱描述,不引用易變動的數字;日誌則集中定義階段名稱或由單一流程描述產生編號。README 的流程圖也應以語意名稱為主,避免把編號當成跨模組契約。

建議寫法

const STAGE = Object.freeze({
  TOOL_DETECTION: '偵測工具',
  DIFF_COLLECTION: '整理差異',
  ATTACK_REVIEW: '攻擊方審查',
  DEFENSE_REVIEW: '防守方裁決',
});

log(STAGE.DIFF_COLLECTION, 'INF', message);
<!-- ai-code-review --> ### 🟠 警告|🧰 Leo **位置**:`src/index.js` 第 121–145 行 **問題描述** 流程步驟編號被硬編碼在主流程註解、日誌字串、README 與多個函式 JSDoc 中;本次僅插入一個步驟,就必須同步修改大量檔案。這已顯示文件與實作存在高耦合,未來再次調整流程時很容易漏改,造成日誌、文件與實際執行順序互相矛盾。 **修改建議** 共用函式的 JSDoc 改以語意階段名稱描述,不引用易變動的數字;日誌則集中定義階段名稱或由單一流程描述產生編號。README 的流程圖也應以語意名稱為主,避免把編號當成跨模組契約。 **建議寫法** ``` const STAGE = Object.freeze({ TOOL_DETECTION: '偵測工具', DIFF_COLLECTION: '整理差異', ATTACK_REVIEW: '攻擊方審查', DEFENSE_REVIEW: '防守方裁決', }); log(STAGE.DIFF_COLLECTION, 'INF', message); ```

🟠 警告|🔮 Mage

位置src/index.js 第 343–343 行

問題描述

舊審查留言仍在新問題留言發布前就被標記為過時。最小重現:裁決成功後執行此行,舊 finding 已被解決/加上過時標記;接著 postSevereComments 或步驟 10 的 PR 留言 API 失敗,流程以錯誤結束,PR 上便只剩工具、diff、角色等情境留言,既有問題已失效而新的問題尚未發布。這仍違反本次改動宣稱的「避免舊結果先被清掉卻沒有新結果」。

修改建議

先完整發布本回合所有問題留言,確認成功後再呼叫 resolveOldComments。為避免新留言被一併清理,讓嚴重問題留言函式回傳建立的留言/review ID,加入本回合排除集合;或讓清理函式只處理本回合開始前取得的舊留言快照。

<!-- ai-code-review --> ### 🟠 警告|🔮 Mage **位置**:`src/index.js` 第 343–343 行 **問題描述** 舊審查留言仍在新問題留言發布前就被標記為過時。最小重現:裁決成功後執行此行,舊 finding 已被解決/加上過時標記;接著 `postSevereComments` 或步驟 10 的 PR 留言 API 失敗,流程以錯誤結束,PR 上便只剩工具、diff、角色等情境留言,既有問題已失效而新的問題尚未發布。這仍違反本次改動宣稱的「避免舊結果先被清掉卻沒有新結果」。 **修改建議** 先完整發布本回合所有問題留言,確認成功後再呼叫 `resolveOldComments`。為避免新留言被一併清理,讓嚴重問題留言函式回傳建立的留言/review ID,加入本回合排除集合;或讓清理函式只處理本回合開始前取得的舊留言快照。

🟠 警告|🧪 Maya

位置src/lib/gitea.js 第 172–215 行

問題描述

新增加標籤與建立 issue 相依關係的 API 封裝沒有對應測試。尚未驗證空標籤不送請求、issue 編號與 repository 資訊正確放入 URL/body,以及非 2xx 錯誤是否原樣往上傳遞;端點或 payload 任一欄位錯誤都只會在實際 PR 執行時被發現。

修改建議

mock 底層 API,驗證 addLabelsToIssue([], null) 不發請求並回傳 null;有標籤時使用正確 endpoint 與 {labels}。另驗證 addIssueDependency 的 URL issue 編號及 {index, owner, repo} payload,並加入 4xx/5xx 拋錯案例,搭配主流程測試確認相依失敗會被降級而不阻斷審查。

<!-- ai-code-review --> ### 🟠 警告|🧪 Maya **位置**:`src/lib/gitea.js` 第 172–215 行 **問題描述** 新增加標籤與建立 issue 相依關係的 API 封裝沒有對應測試。尚未驗證空標籤不送請求、issue 編號與 repository 資訊正確放入 URL/body,以及非 2xx 錯誤是否原樣往上傳遞;端點或 payload 任一欄位錯誤都只會在實際 PR 執行時被發現。 **修改建議** mock 底層 API,驗證 `addLabelsToIssue([], null)` 不發請求並回傳 null;有標籤時使用正確 endpoint 與 `{labels}`。另驗證 `addIssueDependency` 的 URL issue 編號及 `{index, owner, repo}` payload,並加入 4xx/5xx 拋錯案例,搭配主流程測試確認相依失敗會被降級而不阻斷審查。

🟠 警告|🧰 Leo

位置src/lib/gitrepo.js 第 47–65 行

問題描述

tryGit 將所有 Git 失敗壓成布林值,呼叫端只能知道策略失敗,無法區分認證失敗、refspec 錯誤、網路問題或 Git 版本不支援。新增的 resolveMergeBase 雖彙整策略成敗,卻刻意捨棄真正原因;未來 CI 出錯時,維護者只能重跑或自行重現,診斷成本會很高。

修改建議

讓嘗試結果保留結構化且已清理的錯誤分類,例如 exit code、Git 子命令與安全化後的短訊息;仍可避免記錄遠端 URL 或憑證。如此既能依序降級,也能在最終錯誤中提供足以採取行動的原因,並可對各失敗類型做單元測試。

建議寫法

function tryGit(cwd, ...args) {
  try {
    git(cwd, ...args);
    return { ok: true };
  } catch (error) {
    return {
      ok: false,
      code: error.status ?? error.code ?? null,
      reason: sanitizeGitError(error),
    };
  }
}
<!-- ai-code-review --> ### 🟠 警告|🧰 Leo **位置**:`src/lib/gitrepo.js` 第 47–65 行 **問題描述** `tryGit` 將所有 Git 失敗壓成布林值,呼叫端只能知道策略失敗,無法區分認證失敗、refspec 錯誤、網路問題或 Git 版本不支援。新增的 `resolveMergeBase` 雖彙整策略成敗,卻刻意捨棄真正原因;未來 CI 出錯時,維護者只能重跑或自行重現,診斷成本會很高。 **修改建議** 讓嘗試結果保留結構化且已清理的錯誤分類,例如 exit code、Git 子命令與安全化後的短訊息;仍可避免記錄遠端 URL 或憑證。如此既能依序降級,也能在最終錯誤中提供足以採取行動的原因,並可對各失敗類型做單元測試。 **建議寫法** ``` function tryGit(cwd, ...args) { try { git(cwd, ...args); return { ok: true }; } catch (error) { return { ok: false, code: error.status ?? error.code ?? null, reason: sanitizeGitError(error), }; } } ```

🟠 警告| Rogue

位置src/lib/gitrepo.js 第 138–140 行

問題描述

淺層 checkout 找不到 merge-base 時,第一個補救策略直接執行 --unshallow,會先下載整個儲存庫歷史;大型或長壽 repo 可能多傳輸數百 MB、耗費數十秒到數分鐘,即使只加深少量歷史便足以找到共同祖先。

修改建議

先以固定深度分批加深 base 與 HEAD,並在每次 fetch 後重試 merge-base;只有達到合理上限仍失敗時才把 --unshallow 當最後手段。

建議寫法

strategies.push([
  'deepen base',
  'fetch', '--no-tags', '--deepen=1000', 'origin',
  `+refs/heads/${baseRef}:refs/remotes/${remoteBase}`,
]);
strategies.push(['deepen HEAD', 'fetch', '--no-tags', '--deepen=1000', 'origin', 'HEAD']);
if (gitTrim(cwd, 'rev-parse', '--is-shallow-repository') === 'true') {
  strategies.push(['unshallow', 'fetch', '--no-tags', '--unshallow', 'origin']);
}
<!-- ai-code-review --> ### 🟠 警告|⚡ Rogue **位置**:`src/lib/gitrepo.js` 第 138–140 行 **問題描述** 淺層 checkout 找不到 merge-base 時,第一個補救策略直接執行 `--unshallow`,會先下載整個儲存庫歷史;大型或長壽 repo 可能多傳輸數百 MB、耗費數十秒到數分鐘,即使只加深少量歷史便足以找到共同祖先。 **修改建議** 先以固定深度分批加深 base 與 HEAD,並在每次 fetch 後重試 merge-base;只有達到合理上限仍失敗時才把 `--unshallow` 當最後手段。 **建議寫法** ``` strategies.push([ 'deepen base', 'fetch', '--no-tags', '--deepen=1000', 'origin', `+refs/heads/${baseRef}:refs/remotes/${remoteBase}`, ]); strategies.push(['deepen HEAD', 'fetch', '--no-tags', '--deepen=1000', 'origin', 'HEAD']); if (gitTrim(cwd, 'rev-parse', '--is-shallow-repository') === 'true') { strategies.push(['unshallow', 'fetch', '--no-tags', '--unshallow', 'origin']); } ```

🟠 警告|🧪 Maya

位置src/lib/gitrepo.js 第 270–291 行

問題描述

新增 pushToken 後,push 行為分成「PAT 直接推送」與「origin 失敗後使用一般 token 重試」,但本次 diff 沒有測試這兩條認證路徑。尚未驗證空字串邊界、PAT 路徑不碰 origin、origin 成功時不建立認證 URL,以及失敗重試使用正確 token;若分支選錯,可能讓 CI 不再觸發或直接無法推送。

修改建議

補單元測試攔截 git 參數:有 pushToken 時只推送一次且使用 PAT URL;沒有或空白 token 時先推 origin;origin 成功不得重試;origin 失敗才以 token 重試。斷言測試輸出與錯誤訊息中不含任何 token 原文。

<!-- ai-code-review --> ### 🟠 警告|🧪 Maya **位置**:`src/lib/gitrepo.js` 第 270–291 行 **問題描述** 新增 `pushToken` 後,push 行為分成「PAT 直接推送」與「origin 失敗後使用一般 token 重試」,但本次 diff 沒有測試這兩條認證路徑。尚未驗證空字串邊界、PAT 路徑不碰 origin、origin 成功時不建立認證 URL,以及失敗重試使用正確 token;若分支選錯,可能讓 CI 不再觸發或直接無法推送。 **修改建議** 補單元測試攔截 git 參數:有 `pushToken` 時只推送一次且使用 PAT URL;沒有或空白 token 時先推 origin;origin 成功不得重試;origin 失敗才以 `token` 重試。斷言測試輸出與錯誤訊息中不含任何 token 原文。

🟠 警告|🗡️ Assassin

位置src/lib/review.js 第 62–70 行

問題描述

攻擊者可在送審 diff 或 AI 回覆中安插會被 CLI 回顯的敏感內容;開啟 ACTIONS_STEP_DEBUG 後,程式便把 stderr/stdout 寫入 CI 日誌。黑名單遮罩無法涵蓋任意格式的 token、JWT、私鑰、PII 或原始碼機密,且截取前 500 字不會降低外洩風險。

修改建議

CI 日誌一律不要輸出 AI CLI 的原始 stderr/stdout,即使在 debug 模式亦然;只記錄退出碼、訊號與預先定義的錯誤分類。若確實需要除錯內容,應寫入權限受控、短期保存的 artifact,並先套用結構化允許清單與平台 secret masking。

建議寫法

// 不將不可信的 CLI 輸出寫入 CI log。
if (verbose) parts.push('已啟用除錯;原始輸出因可能含機密而不寫入日誌');
<!-- ai-code-review --> ### 🟠 警告|🗡️ Assassin **位置**:`src/lib/review.js` 第 62–70 行 **問題描述** 攻擊者可在送審 diff 或 AI 回覆中安插會被 CLI 回顯的敏感內容;開啟 `ACTIONS_STEP_DEBUG` 後,程式便把 stderr/stdout 寫入 CI 日誌。黑名單遮罩無法涵蓋任意格式的 token、JWT、私鑰、PII 或原始碼機密,且截取前 500 字不會降低外洩風險。 **修改建議** CI 日誌一律不要輸出 AI CLI 的原始 stderr/stdout,即使在 debug 模式亦然;只記錄退出碼、訊號與預先定義的錯誤分類。若確實需要除錯內容,應寫入權限受控、短期保存的 artifact,並先套用結構化允許清單與平台 secret masking。 **建議寫法** ``` // 不將不可信的 CLI 輸出寫入 CI log。 if (verbose) parts.push('已啟用除錯;原始輸出因可能含機密而不寫入日誌'); ```

🔵 建議|🎼 Bard

位置action.yml 第 3–3 行

問題描述

檔頭的「更新時間」仍寫成 2026/07/17 18:49:58,與本次變更所示的檔案更新時間 2026/07/20 15:00:23 不一致。手動維護且散落各檔的時間戳已經走調,讀者無法判斷哪個資訊可信。

修改建議

移除容易過期的手動更新時間,改以版本控制紀錄作為唯一依據;若專案規範要求保留,則應由腳本統一產生並同步更新所有位置。

建議寫法

# 用途:定義 AI Code Review Node action 的名稱、輸入參數與 Node.js 24 進入點,供 Gitea / GitHub workflow 以 uses 引用。
<!-- ai-code-review --> ### 🔵 建議|🎼 Bard **位置**:`action.yml` 第 3–3 行 **問題描述** 檔頭的「更新時間」仍寫成 `2026/07/17 18:49:58`,與本次變更所示的檔案更新時間 `2026/07/20 15:00:23` 不一致。手動維護且散落各檔的時間戳已經走調,讀者無法判斷哪個資訊可信。 **修改建議** 移除容易過期的手動更新時間,改以版本控制紀錄作為唯一依據;若專案規範要求保留,則應由腳本統一產生並同步更新所有位置。 **建議寫法** ``` # 用途:定義 AI Code Review Node action 的名稱、輸入參數與 Node.js 24 進入點,供 Gitea / GitHub workflow 以 uses 引用。 ```

🔵 建議|🎼 Bard

位置readme.md 第 3–3 行

問題描述

README 的手動「更新時間」與本次檔案實際更新時間不符,也和 action.ymlsrc/index.js 重複保存同一類易過期資訊。這種散落的版本註記會逐漸形成彼此不押韻的多個真相來源。

修改建議

刪除此手動時間戳,讓 Git 歷史承擔更新追蹤;若讀者確實需要顯示更新日期,請由發布流程自動注入,避免人工同步。

建議寫法

# AI Code Review
<!-- ai-code-review --> ### 🔵 建議|🎼 Bard **位置**:`readme.md` 第 3–3 行 **問題描述** README 的手動「更新時間」與本次檔案實際更新時間不符,也和 `action.yml`、`src/index.js` 重複保存同一類易過期資訊。這種散落的版本註記會逐漸形成彼此不押韻的多個真相來源。 **修改建議** 刪除此手動時間戳,讓 Git 歷史承擔更新追蹤;若讀者確實需要顯示更新日期,請由發布流程自動注入,避免人工同步。 **建議寫法** ``` # AI Code Review ```

🔵 建議|🎼 Bard

位置src/index.js 第 7–7 行

問題描述

啟動橫幅硬編碼的「更新時間」與本次程式實際更新時間不一致,而且每次修改程式都得額外人工校準,既製造視覺噪音,也讓執行日誌呈現失真的版本資訊。

修改建議

移除手動時間戳;若日誌需要辨識執行版本,改顯示由建置或 CI 注入的 commit SHA/版本號,語義會比模糊的更新時間更穩定。

建議寫法

console.log(`Version : ${process.env.GITHUB_SHA || 'unknown'}`);
<!-- ai-code-review --> ### 🔵 建議|🎼 Bard **位置**:`src/index.js` 第 7–7 行 **問題描述** 啟動橫幅硬編碼的「更新時間」與本次程式實際更新時間不一致,而且每次修改程式都得額外人工校準,既製造視覺噪音,也讓執行日誌呈現失真的版本資訊。 **修改建議** 移除手動時間戳;若日誌需要辨識執行版本,改顯示由建置或 CI 注入的 commit SHA/版本號,語義會比模糊的更新時間更穩定。 **建議寫法** ``` console.log(`Version : ${process.env.GITHUB_SHA || 'unknown'}`); ```

🔵 建議|🎼 Bard

位置src/index.js 第 179–231 行

問題描述

main() 內新增兩個帶完整 JSDoc 的閉包函式,再接上一大段「步驟 2:延後執行」說明,使主流程在真正進入步驟 3 前被近六十行細節打斷。審查編排應像總譜般一眼看出段落走向,目前留言路由、issue 建立與流程辯解混在同一層,閱讀節奏顯得沉重。

修改建議

將留言路由與 issue 建立封裝成具語義名稱的輔助物件或模組,例如 createCommentPublisher,讓 main() 只保留流程級呼叫;延後清理的理由則縮成一則貼近實際呼叫點的簡短註解。

建議寫法

const comments = createCommentPublisher({ ctx, gitea });

// 成功產生本回合結果後再清理舊留言,避免失敗時留下空窗。
await comments.post(templates.toolComment({ /* ... */ }));
<!-- ai-code-review --> ### 🔵 建議|🎼 Bard **位置**:`src/index.js` 第 179–231 行 **問題描述** `main()` 內新增兩個帶完整 JSDoc 的閉包函式,再接上一大段「步驟 2:延後執行」說明,使主流程在真正進入步驟 3 前被近六十行細節打斷。審查編排應像總譜般一眼看出段落走向,目前留言路由、issue 建立與流程辯解混在同一層,閱讀節奏顯得沉重。 **修改建議** 將留言路由與 issue 建立封裝成具語義名稱的輔助物件或模組,例如 `createCommentPublisher`,讓 `main()` 只保留流程級呼叫;延後清理的理由則縮成一則貼近實際呼叫點的簡短註解。 **建議寫法** ``` const comments = createCommentPublisher({ ctx, gitea }); // 成功產生本回合結果後再清理舊留言,避免失敗時留下空窗。 await comments.post(templates.toolComment({ /* ... */ })); ```

🔵 建議|🎼 Bard

位置src/lib/gitea.js 第 171–194 行

問題描述

新增的 addLabelsToIssue 在同一份變更中已被主流程明確註明「不再於事後補掛」,卻仍以大篇幅註解保留為未見使用情境的通用能力。這段程式與本次實際流程沒有呼應,讓 API 表面多出一個無聲部可接的樂句,也增加讀者辨識真正入口的負擔。

修改建議

若目前沒有呼叫端,先移除此函式與匯出;待出現實際需求時再連同使用情境加入。若確有外部使用者,則應在文件中明確列出呼叫契約,而非只以「通用能力」籠統交代。

<!-- ai-code-review --> ### 🔵 建議|🎼 Bard **位置**:`src/lib/gitea.js` 第 171–194 行 **問題描述** 新增的 `addLabelsToIssue` 在同一份變更中已被主流程明確註明「不再於事後補掛」,卻仍以大篇幅註解保留為未見使用情境的通用能力。這段程式與本次實際流程沒有呼應,讓 API 表面多出一個無聲部可接的樂句,也增加讀者辨識真正入口的負擔。 **修改建議** 若目前沒有呼叫端,先移除此函式與匯出;待出現實際需求時再連同使用情境加入。若確有外部使用者,則應在文件中明確列出呼叫契約,而非只以「通用能力」籠統交代。
Member

🔧 AI Code Review 問題處理進度

已依嚴重度逐條處理議題 #9(審查 commit b2129e0)的 13 條問題。由於 #9 審的是較早的 commit,多條所指程式碼已在後續 commit 修復,逐一對照目前程式碼確認。

統計: 已解決 3 條、🚫 不採納 2 條、⏭️ 待人工處理 8 條。 exclusions.json 本輪無新增(兩條不採納皆已有等價排除項)。

留言 等級 審查員 位置 處理 說明
#4796 🔴 嚴重 🗡️ Assassin src/lib/gitrepo.js 已解決 帶認證推送已改經 GIT_CONFIG_*/extraheader 以 env 傳入、不進 argv,URL 不含帳密、push 失敗遮蔽錯誤(先前 commit 8349fe8 等)
#4808 🔵 建議 🎼 Bard src/lib/gitea.js 已解決 無呼叫端的 addLabelsToIssue 已移除(先前 commit 0a51aff
#4801 🟠 警告 Rogue src/lib/gitrepo.js 已解決 resolveMergeBase 改為先 deepen base/HEAD、--unshallow 降為最後手段,避免大型 repo 只為找共同祖先就拉全史(本輪修復)
#4798 🟠 警告 🔮 Mage src/index.js 🚫 不採納 resolveOldComments 時序——維護者(議題 #8 留言 #4838)已明確裁示舊留言須刻意在本回合結果前標記過時、不可延後;exclusions.json 已有等價排除
#4803 🟠 警告 🗡️ Assassin src/lib/review.js 🚫 不採納 「debug 模式也完全不輸出 CLI stderr/stdout」——維護者本輪已明確選擇「失敗時預設輸出經 redactSecrets 遮罩的片段」以便診斷(如 claude OAuth 過期即靠此查出),方向相反;exclusions.json 已有等價排除
#4797 🟠 警告 🧰 Leo src/index.js ⏭️ 待人工處理 步驟編號硬編碼於註解/日誌/README/JSDoc,建議改語意階段名稱——跨檔重構+設計取捨
#4799 🟠 警告 🧪 Maya src/lib/gitea.js ⏭️ 待人工處理 相依 API 測試;無測試框架(addLabelsToIssue 已移除,其測試不再需要)
#4800 🟠 警告 🧰 Leo src/lib/gitrepo.js ⏭️ 待人工處理 tryGit 改結構化錯誤(保留 sanitize 後原因)——診斷重構+需測試
#4802 🟠 警告 🧪 Maya src/lib/gitrepo.js ⏭️ 待人工處理 push 認證路徑測試;雙路徑已合併為單一 token 認證推送,測試仍待補(無測試框架)
#4804 🔵 建議 🎼 Bard action.yml ⏭️ 待人工處理 檔頭手動「更新時間」走調——屬 jsc spec-time-log 慣例,交 doc-funcs 統一
#4805 🔵 建議 🎼 Bard readme.md ⏭️ 待人工處理 同上(README 更新時間)
#4806 🔵 建議 🎼 Bard src/index.js ⏭️ 待人工處理 同上(啟動橫幅更新時間)
#4807 🔵 建議 🎼 Bard src/index.js ⏭️ 待人工處理 抽出 comment publisher 精簡 main()——與 #4797 同屬主流程重構

⏭️ 待人工處理已寫回 findings 追蹤

8 條待人工處理已寫成 findings wrapper 檔 .gitea/ai-review/findings/2026-07-20-17:37:54.jsonF001F008)供後續追蹤,不會因關閉議題而遺失。

待人工處理共同原因

多為「無測試框架/devDependencies」(Maya 測試)、「跨檔/主流程重構+設計取捨」(Leo 步驟語意化、tryGit 結構化、Bard publisher 抽離)或「jsc spec-time-log 時間戳慣例(交 doc-funcs 統一)」。皆屬架構/慣例決策,依規範不逕自硬改。

問題已全部分流: 已修復(3)/🚫 不採納(2,維護者裁示、已有等價排除)/⏭️ 待人工處理(8,寫回 findings 追蹤)。依 resolve 流程收尾,關閉本議題。

## 🔧 AI Code Review 問題處理進度 已依嚴重度逐條處理議題 #9(審查 commit `b2129e0`)的 13 條問題。由於 #9 審的是較早的 commit,**多條所指程式碼已在後續 commit 修復**,逐一對照目前程式碼確認。 **統計:✅ 已解決 3 條、🚫 不採納 2 條、⏭️ 待人工處理 8 條。** `exclusions.json` 本輪無新增(兩條不採納皆已有等價排除項)。 | 留言 | 等級 | 審查員 | 位置 | 處理 | 說明 | | --- | --- | --- | --- | --- | --- | | #4796 | 🔴 嚴重 | 🗡️ Assassin | `src/lib/gitrepo.js` | ✅ 已解決 | 帶認證推送已改經 `GIT_CONFIG_*`/extraheader 以 **env 傳入、不進 argv**,URL 不含帳密、push 失敗遮蔽錯誤(先前 commit `8349fe8` 等) | | #4808 | 🔵 建議 | 🎼 Bard | `src/lib/gitea.js` | ✅ 已解決 | 無呼叫端的 `addLabelsToIssue` 已移除(先前 commit `0a51aff`) | | #4801 | 🟠 警告 | ⚡ Rogue | `src/lib/gitrepo.js` | ✅ 已解決 | `resolveMergeBase` 改為**先 deepen base/HEAD、`--unshallow` 降為最後手段**,避免大型 repo 只為找共同祖先就拉全史(本輪修復) | | #4798 | 🟠 警告 | 🔮 Mage | `src/index.js` | 🚫 不採納 | `resolveOldComments` 時序——維護者(議題 #8 留言 #4838)已明確裁示舊留言須刻意在本回合結果前標記過時、不可延後;`exclusions.json` 已有等價排除 | | #4803 | 🟠 警告 | 🗡️ Assassin | `src/lib/review.js` | 🚫 不採納 | 「debug 模式也完全不輸出 CLI stderr/stdout」——維護者本輪已明確選擇「失敗時預設輸出經 `redactSecrets` 遮罩的片段」以便診斷(如 claude OAuth 過期即靠此查出),方向相反;`exclusions.json` 已有等價排除 | | #4797 | 🟠 警告 | 🧰 Leo | `src/index.js` | ⏭️ 待人工處理 | 步驟編號硬編碼於註解/日誌/README/JSDoc,建議改語意階段名稱——跨檔重構+設計取捨 | | #4799 | 🟠 警告 | 🧪 Maya | `src/lib/gitea.js` | ⏭️ 待人工處理 | 相依 API 測試;無測試框架(addLabelsToIssue 已移除,其測試不再需要) | | #4800 | 🟠 警告 | 🧰 Leo | `src/lib/gitrepo.js` | ⏭️ 待人工處理 | `tryGit` 改結構化錯誤(保留 sanitize 後原因)——診斷重構+需測試 | | #4802 | 🟠 警告 | 🧪 Maya | `src/lib/gitrepo.js` | ⏭️ 待人工處理 | push 認證路徑測試;雙路徑已合併為單一 token 認證推送,測試仍待補(無測試框架) | | #4804 | 🔵 建議 | 🎼 Bard | `action.yml` | ⏭️ 待人工處理 | 檔頭手動「更新時間」走調——屬 jsc spec-time-log 慣例,交 doc-funcs 統一 | | #4805 | 🔵 建議 | 🎼 Bard | `readme.md` | ⏭️ 待人工處理 | 同上(README 更新時間) | | #4806 | 🔵 建議 | 🎼 Bard | `src/index.js` | ⏭️ 待人工處理 | 同上(啟動橫幅更新時間) | | #4807 | 🔵 建議 | 🎼 Bard | `src/index.js` | ⏭️ 待人工處理 | 抽出 comment publisher 精簡 main()——與 #4797 同屬主流程重構 | ### ⏭️ 待人工處理已寫回 findings 追蹤 8 條待人工處理已寫成 findings wrapper 檔 ``.gitea/ai-review/findings/2026-07-20-17:37:54.json``(`F001`–`F008`)供後續追蹤,不會因關閉議題而遺失。 ### 待人工處理共同原因 多為「無測試框架/devDependencies」(Maya 測試)、「跨檔/主流程重構+設計取捨」(Leo 步驟語意化、tryGit 結構化、Bard publisher 抽離)或「jsc spec-time-log 時間戳慣例(交 doc-funcs 統一)」。皆屬架構/慣例決策,依規範不逕自硬改。 > 問題已全部分流:✅ 已修復(3)/🚫 不採納(2,維護者裁示、已有等價排除)/⏭️ 待人工處理(8,寫回 findings 追蹤)。依 resolve 流程收尾,關閉本議題。
Sign in to join this conversation.
No labels
2 Participants
Notifications
Due Date
No due date set.
Reference: node-actions/ai-code-review#9