From 49ac5d4ec43e78fe0b8a1cecaf8e363597b1ea87 Mon Sep 17 00:00:00 2001 From: AI Review Bot Date: Fri, 26 Jun 2026 03:05:47 +0000 Subject: [PATCH] chore: update ai-review findings [ai-review-bot][failure] --- .gitea/ai-review/findings.json | 116 ++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 44 deletions(-) diff --git a/.gitea/ai-review/findings.json b/.gitea/ai-review/findings.json index 093fe99..35c67fa 100644 --- a/.gitea/ai-review/findings.json +++ b/.gitea/ai-review/findings.json @@ -1,65 +1,93 @@ [ + { + "level": "critical", + "role": "Bard", + "location": "app/gitea-client.js:59", + "problem": "fetchAllPages 採展開運算子處理分頁資料,面對龐大項目數量可能導致 Maximum call stack size exceeded 錯誤,並伴隨無窮迴圈風險 (缺少 MAX_PAGES 限制)。", + "suggestion": "改用簡單的 for...of 迴圈逐一 push 項目以規避堆疊風險,並務必加入 MAX_PAGES 常數作為安全斷點,防止 API 異常導致無限迴圈與資源耗盡。" + }, + { + "level": "critical", + "role": "Maya", + "location": "app/index.js:46", + "problem": "缺乏核心清理流程的整合測試,且 cleanup 步驟之間的 API 狀態一致性未受保障,可能導致正在使用的 tag 被錯誤刪除。", + "suggestion": "於 app/test/ 新增整合測試(mock GiteaClient),驗證 cleanup 流程串接,並在兩個 cleanup 步驟之間確保 API 狀態一致,或於刪除 tag 前再次確認其未被現存 release 使用。" + }, + { + "level": "critical", + "role": "Mage", + "location": "app/releases.js:38", + "problem": "清理流程對網路請求依賴強,若 API 呼叫失敗,整個 main 流程中斷,導致容器無法確保後續清理的一致性與部分成功重試。", + "suggestion": "引入更細緻的錯誤處理(如錯誤閾值機制)或部分清理成功後的重試策略,確保清理任務具備健壯性。" + }, { "level": "warning", "role": "Mage", "location": "app/releases.js:46", - "problem": "在 `cleanupReleases` 迴圈中執行 DELETE 請求時,未針對網路不穩定或暫時性服務錯誤(如 502, 503, 504)實作重試機制。若刪除過程中發生瞬間網路中斷,該 release 將不會被刪除。", - "suggestion": "對於特定的 HTTP 狀態碼(502, 503, 504),建議引入簡單的指數退避重試機制(Exponential Backoff)。", - "status": "deferred", - "defer_reason": "屬功能性增強與設計取捨。此清理 Action 通常以排程執行,單次失敗可於下次執行補刪;是否引入重試/退避涉及重試次數、間隔與冪等性等設計決策,保留待人工評估。" - }, - { - "level": "warning", - "role": "Mage", - "location": "app/tags.js:56", - "problem": "`cleanupReleases` 與 `cleanupOrphanTags` 非同步先後執行,若兩者之間 Gitea 上有新的 release 被建立,releaseTagNames 快照會過時,可能導致正在使用的 tag 被錯誤刪除。", - "suggestion": "在兩個 cleanup 步驟之間確保 API 狀態一致性,或在刪除 tag 前再次檢查該 tag 是否仍未被任何現存 release 使用。", - "status": "deferred", - "defer_reason": "現行程式已在 cleanupOrphanTags 開頭重新抓取最新 release 清單作為主要緩解;殘餘競態窗極小且屬排程任務可接受範圍。是否再加每筆刪除前複查屬一致性/成本的設計取捨,保留待人工評估。" - }, - { - "level": "warning", - "role": "Rogue", - "location": "app/releases.js:43", - "problem": "刪除舊成品時使用序列化的 `for...of` + `await`,刪除請求逐一排隊等待 API 回應。", - "suggestion": "改用 `Promise.all` 搭配 `map` 將刪除請求並行化以縮短總執行時間。", - "status": "deferred", - "defer_reason": "刻意保留序列化:可避免對 Gitea API 造成併發壓力與觸發速率限制,並維持可預期的記錄輸出順序,且與原 bash 版本行為一致。無上限並行化非等價變更,保留待人工評估(可日後改為有上限的並行)。" - }, - { - "level": "warning", - "role": "Rogue", - "location": "app/tags.js:46", - "problem": "刪除孤立 tag 時同樣使用序列化迴圈,造成總執行時間拉長。", - "suggestion": "改用 `Promise.all` 將刪除請求並行化。", - "status": "deferred", - "defer_reason": "與 app/releases.js:43 同理,刻意保留序列化以避免併發壓力與速率限制並維持記錄順序,屬設計取捨,保留待人工評估。" + "problem": "刪除邏輯未針對網路不穩定或特定 HTTP 狀態碼 (502, 503, 504) 實作重試機制,且在遇到失敗時未停止後續請求,導致大量無意義錯誤。", + "suggestion": "針對特定 HTTP 狀態碼實作指數退避重試機制,並引入錯誤閾值,當失敗次數過高時立即中斷流程。" }, { "level": "warning", "role": "Leo", "location": "app/releases.js:37", - "problem": "`cleanupReleases` 同時負責取得資料、判斷邏輯與執行刪除副作用,違反單一職責原則,未來不易單獨測試刪除邏輯。", - "suggestion": "將取得/過濾的純邏輯與執行刪除的副作用層拆開(目前 `selectReleasesToDelete` 已做了一部分)。", - "status": "deferred", - "defer_reason": "純邏輯(selectReleasesToDelete)已抽離且函式短小、已具失敗路徑測試覆蓋;進一步拆出刪除迴圈為設計偏好,效益有限且增加表面積,保留待人工評估。" + "problem": "cleanupReleases 違反單一職責原則,同時處理資料獲取、邏輯判斷與副作用執行,不易測試。", + "suggestion": "拆分邏輯與執行層,將刪除副作用抽象化為獨立函式,以利單獨測試。" }, { "level": "warning", + "role": "Rogue", + "location": "app/releases.js:43", + "problem": "清理成品與刪除 tag 使用序列化迴圈,導致 API 請求逐一排隊,整體執行時間拉長。", + "suggestion": "改用 Promise.all 搭配 map 將刪除請求並行化以縮短執行時間。" + }, + { + "level": "warning", + "role": "Assassin", + "location": "app/releases.js:56", + "problem": "直接將 API 回傳的 id 與 tag 名稱拼接到 URL 中進行 DELETE 操作,未經驗證,存在路徑穿越或 SSRF 風險。", + "suggestion": "在使用 id 或 tag 名稱構建 URL 前,必須嚴格驗證其字元組成(如僅允許特定格式或編碼處理)。" + }, + { + "level": "warning", + "role": "Maya", + "location": "app/config.js:36", + "problem": "缺乏對 GITEA_TOKEN 長度或格式的邊界測試,以及對 KEEP_COUNT 格式異常的檢查。", + "suggestion": "在測試檔中增加針對 Token 與 KEEP_COUNT 的邊界測試,並在 loadConfig 內加強格式轉換檢查。" + }, + { + "level": "warning", + "role": "Maya", + "location": "app/gitea-client.js:33", + "problem": "fetchAllPages 使用了逾時訊號,但測試套件未驗證網路逾時情境,且總耗時未受限制。", + "suggestion": "在測試中模擬 AbortError,並考慮對整個 fetchAllPages 流程引入總執行時間限制。" + }, + { + "level": "warning", + "role": "Bard", + "location": "app/logger.js:5", + "problem": "分隔線字串為魔術字串,硬編碼在模組頂層,不易維護與調整。", + "suggestion": "將分隔線管理集中化,並考慮提供動態產生方法。" + }, + { + "level": "info", "role": "Leo", - "location": "app/tags.js:46", - "problem": "`cleanupOrphanTags` 直接在主流程遍歷並刪除,未來若需更複雜的失敗處理(重試、批次)會難以維護。", - "suggestion": "參考 cleanupReleases 結構,將分類過濾與執行刪除進一步解耦。", - "status": "deferred", - "defer_reason": "分類邏輯(categorizeTags)已抽為純函式並具測試;是否進一步拆出刪除執行層為設計偏好,與 app/releases.js:37 一併保留待人工評估。" + "location": "app/validate.js:47", + "problem": "驗證規則寫死在函式內,易產生重複程式碼,且缺乏擴充性。", + "suggestion": "將驗證規則提取為設定物件或共用常數,或考慮使用 Zod 等 Schema 套件進行驗證與型別轉換。" }, { "level": "info", "role": "Rogue", "location": "app/gitea-client.js:33", - "problem": "`fetchAllPages` 採線性逐頁請求,資料量龐大時總請求時間較長。", - "suggestion": "若 API 支援,先取得總頁數再並行發出請求。", - "status": "deferred", - "defer_reason": "Gitea 分頁未可靠提供總頁數,需額外解析 Link 標頭且各端點支援度不一;線性逐頁搭配逾時已足夠穩健且簡單,並行化屬最佳化取捨,保留待人工評估。" + "problem": "fetchAllPages 採線性逐頁請求,資料龐大時效能不佳,且回應處理透過 .text() 再轉 JSON 造成重複記憶體開銷。", + "suggestion": "若 API 支援,先取得總頁數後並行請求,並直接處理 Response 的 ReadableStream 以提升效能。" + }, + { + "level": "info", + "role": "Leo", + "location": "app/index.js:34", + "problem": "錯誤報告邏輯與 logger.js 重疊,職責不清晰。", + "suggestion": "統一透過 logger.js 的 fail 函式處理錯誤輸出,將邏輯收斂。" } ]