diff --git a/.gitea/ai-review/findings.json b/.gitea/ai-review/findings.json index 854d9a5..3b576f3 100644 --- a/.gitea/ai-review/findings.json +++ b/.gitea/ai-review/findings.json @@ -4,63 +4,91 @@ "role": "Maya", "location": "entrypoint.sh", "suggestion": "此 Git Diff 引入了大量新的函數 (`trim`, `url_encode`, `parse_repo_context`, `fetch_package_versions`) 並對現有函數進行了重大重構 (`resolve_keep_count`, `resolve_package_names`, `api_request`, `collect_package_candidates`, `process_candidates`)。然而,程式碼庫中完全沒有為這些關鍵邏輯變更提供任何測試。這導致無法驗證新功能的正確性、邊界條件處理以及重構後的穩定性。請立即為所有新增及修改的函數補齊單元測試和整合測試。", + "is_new": false + }, + { + "level": "critical", + "role": "Maya", + "location": "entrypoint.sh:10-14", + "suggestion": "新增 `trim` 函數的單元測試。此函數為核心工具,被多處使用,應確保其能正確處理各種輸入,例如:空字串、只包含空白的字串、前後有空白的字串、中間有空白的字串、無空白的字串等。", "is_new": true }, { "level": "critical", "role": "Maya", - "location": "entrypoint.sh:13", - "suggestion": "新增的 `trim` 函數是許多其他函數的基礎,其正確性至關重要。請為 `trim` 函數編寫單元測試,涵蓋輸入為空字串、僅包含空白字元、前後有空白字元、以及沒有空白字元的字串等情境。", - "is_new": false + "location": "entrypoint.sh:16-19", + "suggestion": "新增 `url_encode` 函數的單元測試。此函數用於 URL 編碼,應測試包含特殊字元、空格、空字串、已編碼字串等情況,確保其符合 RFC 3986 標準。", + "is_new": true }, { "level": "critical", "role": "Maya", - "location": "entrypoint.sh:20", - "suggestion": "新增的 `url_encode` 函數用於構建 API 路徑,直接影響請求的正確性和安全性。請為 `url_encode` 函數編寫單元測試,涵蓋包含特殊字元、空格、已編碼字元以及一般字元的輸入,確保其符合 RFC 3986 標準。", - "is_new": false + "location": "entrypoint.sh:39-49", + "suggestion": "新增 `resolve_keep_count` 函數的單元測試。應測試以下邊界條件和無效輸入:\n1. 有效的非負整數 (例如 0, 1, 5)。\n2. 包含前後空白的有效整數 (例如 \" 5 \")。\n3. 空字串或只包含空白的字串 (應回退到預設值 2)。\n4. 無效的非數字輸入 (例如 \"abc\", \"-1\", \"1.5\"),確保能正確觸發 `fail`。", + "is_new": true }, { "level": "critical", "role": "Maya", - "location": "entrypoint.sh:44", - "suggestion": "重構後的 `resolve_package_names` 函數邏輯複雜,涉及字串處理、分割和去重。請為其編寫單元測試,涵蓋以下情境:空輸入、僅空白字元、單一套件名稱、多個套件名稱(逗號分隔、換行符分隔、混合分隔)、帶有前後空白字元的套件名稱、以及重複的套件名稱,確保輸出是唯一且正確的列表。", - "is_new": false + "location": "entrypoint.sh:51-75", + "suggestion": "新增 `resolve_package_names` 函數的單元測試。此函數處理使用者輸入的套件名稱,應測試以下情況:\n1. 空字串或只包含空白的字串 (應觸發 `fail`)。\n2. 單一套件名稱。\n3. 多個套件名稱,以逗號或換行符分隔。\n4. 包含前後空白的套件名稱 (應被 `trim` 處理)。\n5. 重複的套件名稱 (應被正確去重)。\n6. 包含空令牌的輸入 (例如 \"pkg1,,pkg2\")。\n7. 包含特殊字元 (例如連字號、點) 的套件名稱。", + "is_new": true }, { "level": "critical", "role": "Maya", - "location": "entrypoint.sh:70", - "suggestion": "重構後的 `parse_repo_context` 函數是解析 Gitea 儲存庫資訊的關鍵。請為其編寫單元測試,涵蓋有效格式(例如 `owner/repo`)、空輸入、僅空白字元、缺少 owner 或 repo、以及無效格式(例如 `owner`、`owner/`、`/repo`、`owner/repo/extra`)等情境,確保其能正確解析或報錯。", - "is_new": false + "location": "entrypoint.sh:77-92", + "suggestion": "新增 `parse_repo_context` 函數的單元測試。此函數解析 Gitea 儲存庫字串,應測試以下邊界條件和無效輸入:\n1. 有效的儲存庫名稱 (例如 \"owner/repo\", \"org/project-name\")。\n2. 包含前後空白的有效儲存庫名稱。\n3. 空字串或無斜線的字串 (應觸發 `fail`)。\n4. 包含多個斜線的字串 (例如 \"owner/repo/sub\"),確保能正確觸發 `fail`。", + "is_new": true }, { "level": "critical", "role": "Maya", - "location": "entrypoint.sh:120", - "suggestion": "重構後的 `fetch_package_versions` 函數負責分頁獲取套件版本,邏輯複雜。請為其編寫整合測試,涵蓋單頁、多頁、空結果、套件不存在(404 回應)、以及 API 錯誤等情境。特別要驗證 `url_encode` 在構建 URL 時的正確性。", - "is_new": false - }, - { - "level": "critical", - "role": "Maya", - "location": "entrypoint.sh:170", - "suggestion": "重構後的 `collect_package_candidates` 函數是決定哪些版本應被刪除的核心邏輯。請為其編寫整合測試,驗證在不同 `keep_count` 值(0、1、等於總版本數、大於總版本數)下,候選版本是否正確選取。同時,確保版本排序邏輯(`created_at` 和 `version`)的正確性。", - "is_new": false + "location": "entrypoint.sh:133-184", + "suggestion": "新增 `fetch_package_versions` 函數的單元測試。此函數是獲取套件版本的核心邏輯,應測試以下情況:\n1. 套件不存在 (應返回空 JSON 陣列,HTTP 404)。\n2. 套件存在但沒有版本。\n3. 套件版本數量少於分頁限制 (PAGE_LIMIT)。\n4. 套件版本數量多於分頁限制,需要多頁獲取。\n5. Gitea API 返回非 2xx/404 錯誤時的處理。\n6. `PAGE_LIMIT` 為無效值 (非數字、零、負數) 時的處理。", + "is_new": true }, { "level": "critical", "role": "Zara", - "location": "entrypoint.sh:L157", - "suggestion": "在 `collect_package_candidates` 函數中,針對每個 `package_name` 獨立呼叫 `fetch_package_versions` 導致了 N+1 API 查詢問題。這會顯著增加 API 請求次數,尤其當 `INPUT_PACKAGE_NAMES` 包含多個套件名稱時,可能導致執行時間過長或觸發 API 速率限制。\n\n建議恢復舊有的策略:先透過單一 API 呼叫(可能需要分頁)取得指定 `owner` 下所有 NuGet 套件的資訊,然後在本地使用 `jq` 進行過濾和分組,以減少對 Gitea API 的總體請求次數。", + "location": "entrypoint.sh:L209", + "suggestion": "在 `collect_package_candidates` 函數中,針對每個 `package_name` 呼叫 `fetch_package_versions` 會導致 N+1 查詢問題。如果 Gitea API 支援一次性查詢多個套件或所有套件的版本(如同舊版程式碼的 `fetch_all_pages` 似乎暗示的),應改為一次性取得所有相關套件的版本資料,然後在本地進行過濾和分組。這將大幅減少 API 請求的總數,顯著提升執行效率並降低 Gitea 伺服器的負載。", + "is_new": true + }, + { + "level": "critical", + "role": "Maya", + "location": "entrypoint.sh:186-231", + "suggestion": "新增 `collect_package_candidates` 函數的單元測試。此函數負責識別要刪除的套件版本,應測試以下情況:\n1. 沒有提供套件名稱或提供的套件名稱不存在。\n2. 套件版本數量少於 `keep_count` (不應有刪除候選)。\n3. 套件版本數量多於 `keep_count` (應正確識別最舊的版本作為候選)。\n4. `keep_count` 為 0 或 1 的邊界情況。\n5. 確保 `sort_by(.created_at, .version)` 排序邏輯的正確性,特別是在 `created_at` 相同時的行為。", + "is_new": true + }, + { + "level": "critical", + "role": "Maya", + "location": "entrypoint.sh:233-278", + "suggestion": "新增 `process_candidates` 函數的單元測試。此函數負責執行刪除操作,應測試以下情況:\n1. 候選文件為空 (不應執行任何刪除)。\n2. 候選文件中包含一個或多個刪除項目。\n3. 模擬 `api_request` 成功刪除的情況。\n4. 模擬 `api_request` 刪除失敗的情況 (例如 404, 500)。\n5. 確保最終的摘要日誌輸出正確反映刪除和錯誤計數。", + "is_new": true + }, + { + "level": "critical", + "role": "Maya", + "location": "entrypoint.sh:280-312", + "suggestion": "新增 `main` 函數的整合測試。此函數是腳本的入口點,應透過設置不同的環境變數 (例如 `GITEA_SERVER_URL`, `GITEA_REPOSITORY`, `RUNNER_TOKEN`, `INPUT_KEEP_COUNT`, `INPUT_PACKAGE_NAMES`) 來模擬真實場景,並驗證整個流程的正確性,包括:\n1. 成功執行並刪除指定套件版本。\n2. 在輸入無效時正確觸發 `fail`。\n3. 在 Gitea API 返回錯誤時的行為。\n4. 確保所有臨時文件在腳本結束時被正確清理。", "is_new": true }, { "level": "warning", - "role": "Zara", - "location": "entrypoint.sh:125, entrypoint.sh:126, entrypoint.sh:240, entrypoint.sh:241", - "suggestion": "在 `fetch_package_versions` 函式中,`page_file` 和 `headers_file` 在 `while` 迴圈的每次迭代中都被建立和刪除。同樣地,在 `process_candidates` 函式中,`body_file` 和 `headers_file` 也在迴圈中重複建立和刪除。這會導致頻繁的 `mktemp` 和 `rm -f` 系統呼叫,增加 I/O 和程序啟動的開銷,尤其是在處理大量分頁或多個待刪除版本時。建議在迴圈外部只建立一次這些暫存檔案,並在迴圈內部重複使用它們,最後在函式結束時統一刪除。", - "is_new": false + "role": "Maya", + "location": "entrypoint.sh:94-131", + "suggestion": "雖然 `api_request` 函數的重構使其更易於測試,但仍缺少針對其行為的單元測試。建議使用 mock 方式模擬 `curl` 命令,以測試以下情況:\n1. 成功的 HTTP 請求 (2xx 狀態碼)。\n2. 各種錯誤的 HTTP 響應 (例如 404, 500)。\n3. `curl` 命令本身失敗的情況。\n4. 響應頭中包含或不包含 `x-gitea-request-id` 或 `x-request-id` 的情況。\n5. 確保臨時文件在所有執行路徑中都被正確清理。", + "is_new": true + }, + { + "level": "warning", + "role": "Leo", + "location": "entrypoint.sh:91", + "suggestion": "在 `api_request` 函式中直接呼叫 `fail` 會導致函式在發生錯誤時直接終止整個腳本。為了提高模組化和錯誤處理的彈性,建議 `api_request` 函式在失敗時返回一個非零的狀態碼或錯誤訊息,讓呼叫者(例如 `fetch_package_versions` 或 `process_candidates`)決定如何處理錯誤,例如是繼續執行還是終止腳本。這將使 `api_request` 成為一個更純粹的 HTTP 請求工具函式。", + "is_new": true }, { "level": "warning", @@ -71,32 +99,53 @@ }, { "level": "warning", - "role": "Maya", - "location": "entrypoint.sh:38-50", - "suggestion": "`resolve_keep_count` 函數現在會修剪輸入並驗證其為非負整數。請為其編寫單元測試,涵蓋以下情境:有效的正整數、零、帶有首尾空白的有效整數、空字串(應使用預設值)、只包含空白字元的字串(應使用預設值)、以及無效的輸入(如負數、浮點數、非數字字串),確保在無效輸入時能正確觸發 `fail`。", - "is_new": true - }, - { - "level": "warning", - "role": "Maya", - "location": "entrypoint.sh:95-129", - "suggestion": "`api_request` 函數現在將 `body_file` 和 `headers_file` 作為參數傳入,並返回 HTTP 狀態碼、狀態文本和請求 ID。這顯著提高了其可測試性。請為其編寫整合測試,透過模擬 `curl` 的行為來驗證:成功的 2xx 響應、不同的 4xx 和 5xx 錯誤響應、網路連線失敗、以及正確解析 `X-Gitea-Request-Id` 或 `X-Request-Id`(包括大小寫不敏感和不存在的情況)。", - "is_new": true - }, - { - "level": "warning", - "role": "Maya", - "location": "entrypoint.sh:232-276", - "suggestion": "`process_candidates` 函數負責執行實際的刪除操作並匯總結果。請為其編寫整合測試,透過模擬 `api_request` (DELETE) 的響應來驗證:所有候選都成功刪除、部分候選刪除失敗、`candidate_file` 為空的情況、以及最終匯總統計數據(已刪除數量、錯誤數量)的準確性。", - "is_new": true + "role": "Zara", + "location": "entrypoint.sh:125, entrypoint.sh:126, entrypoint.sh:240, entrypoint.sh:241", + "suggestion": "在 `fetch_package_versions` 函式中,`page_file` 和 `headers_file` 在 `while` 迴圈的每次迭代中都被建立和刪除。同樣地,在 `process_candidates` 函式中,`body_file` 和 `headers_file` 也在迴圈中重複建立和刪除。這會導致頻繁的 `mktemp` 和 `rm -f` 系統呼叫,增加 I/O 和程序啟動的開銷,尤其是在處理大量分頁或多個待刪除版本時。建議在迴圈外部只建立一次這些暫存檔案,並在迴圈內部重複使用它們,最後在函式結束時統一刪除。", + "is_new": false }, { "level": "warning", "role": "Zara", - "location": "entrypoint.sh:L24", - "suggestion": "在 `url_encode` 函數中,每次呼叫都會啟動一個新的 `jq` 外部程序。雖然 `jq` 執行速度快,但在 `process_candidates` 函數中,如果需要刪除的套件版本數量非常多,重複啟動外部程序可能會累積輕微的效能開銷。\n\n對於簡單的 URL 編碼,可以考慮使用 Bash 內建功能或 `printf %q` 等方式來避免頻繁的外部程序呼叫。然而,考量到 `jq` 的 `@uri` 提供了 RFC 3986 標準編碼的完整性,且 API 呼叫本身是主要瓶頸,此處的效能影響可能較小,但仍值得注意。", + "location": "entrypoint.sh:L171, entrypoint.sh:L342", + "suggestion": "`url_encode` 函數每次執行都會啟動一個 `jq` 外部程序。雖然 `jq` 功能強大,但在迴圈中頻繁呼叫它(例如在 `fetch_package_versions` 和 `process_candidates` 中對每個套件名稱或版本進行編碼)會產生顯著的程序啟動開銷。考慮在 Bash 中實現一個更輕量級的 URL 編碼函數,特別是如果需要編碼的字元集有限且已知,以減少外部程序呼叫的頻率。", "is_new": true }, + { + "level": "info", + "role": "Aria", + "location": "entrypoint.sh:13", + "suggestion": "trim 函數中的參數擴展 (`value=\"${value#\"${value%%[![:space:]]*}\"}\"`) 雖然高效且為純 Bash 實現,但對於不熟悉 Bash 進階語法的人來說可能較難理解。可以考慮添加更詳細的註釋來解釋其工作原理,或在極端追求可讀性的情況下,使用 `sed` 或 `awk` 等工具來實現,儘管這會引入外部依賴。", + "is_new": false + }, + { + "level": "info", + "role": "Leo", + "location": "entrypoint.sh", + "suggestion": "程式碼中多處使用了 `log` 函式。為確保可維護性,建議在腳本開頭或一個專門的工具函式庫中明確定義 `log` 函式,並考慮其輸出格式(例如是否包含時間戳、日誌級別等),以便於日誌分析和問題追蹤。雖然此 diff 未包含 `log` 函式的定義,但其廣泛使用使其成為一個值得關注的點。", + "is_new": false + }, + { + "level": "info", + "role": "Maya", + "location": "entrypoint.sh", + "suggestion": "除了單元測試和整合測試,建議開發端到端(E2E)測試。這些測試應在一個隔離的測試環境中運行 `main` 函數,並與一個模擬的 Gitea 實例或專用的測試 Gitea 實例互動,以驗證整個工作流程的正確性。", + "is_new": false + }, + { + "level": "info", + "role": "Maya", + "location": "entrypoint.sh", + "suggestion": "建議在專案中新增一個 `test/` 目錄,將所有測試腳本放在其中。並將這些測試整合到 CI/CD 流程中,確保每次程式碼變更都能自動執行測試,從而及早發現問題並維持程式碼品質。", + "is_new": false + }, + { + "level": "info", + "role": "Aria", + "location": "entrypoint.sh:148", + "suggestion": "在 `fetch_package_versions` 函數中,`limit=100` 是一個硬編碼的數值。考慮將此值定義為一個具名的變數(例如 `PAGE_LIMIT`),以提高可讀性和未來的可配置性。", + "is_new": false + }, { "level": "info", "role": "Zara", @@ -114,43 +163,8 @@ { "level": "info", "role": "Aria", - "location": "entrypoint.sh:148", - "suggestion": "在 `fetch_package_versions` 函數中,`limit=100` 是一個硬編碼的數值。考慮將此值定義為一個具名的變數(例如 `PAGE_LIMIT`),以提高可讀性和未來的可配置性。", - "is_new": false - }, - { - "level": "info", - "role": "Maya", - "location": "entrypoint.sh", - "suggestion": "考慮引入一個專門的 shell 腳本測試框架,例如 `bats-core` 或 `shunit2`。這些框架能提供更結構化的方式來編寫單元測試和整合測試,包括設置(setup)、拆卸(teardown)、斷言(assertions)和模擬(mocking),從而提高測試的可靠性和可維護性。", - "is_new": true - }, - { - "level": "info", - "role": "Maya", - "location": "entrypoint.sh", - "suggestion": "除了單元測試和整合測試,建議開發端到端(E2E)測試。這些測試應在一個隔離的測試環境中運行 `main` 函數,並與一個模擬的 Gitea 實例或專用的測試 Gitea 實例互動,以驗證整個工作流程的正確性。", - "is_new": false - }, - { - "level": "info", - "role": "Leo", - "location": "Dockerfile:4", - "suggestion": "移除 `--no-check-certificate` 是一個正面的安全強化,表示現在系統預期能正確驗證 SSL 憑證。這有助於避免在生產環境中因繞過安全檢查而引入的潛在風險。請確保執行環境已配置好信任的憑證,以避免未來因憑證問題導致的連線失敗。", - "is_new": true - }, - { - "level": "info", - "role": "Leo", - "location": "entrypoint.sh", - "suggestion": "此 Git Diff 顯示了對程式碼可維護性的重大改進。將全域變數替換為函式參數和回傳值,顯著提升了模組化、降低了函式間的耦合度,並使程式碼更容易理解和測試。此外,新增的輔助函式 (如 `trim`, `url_encode`) 和對錯誤處理、臨時檔案管理的強化,都對長期維護成本有極大的正面影響。這是一個非常出色的重構,值得肯定。", - "is_new": true - }, - { - "level": "info", - "role": "Leo", - "location": "entrypoint.sh", - "suggestion": "程式碼中多處使用了 `log` 函式。為確保可維護性,建議在腳本開頭或一個專門的工具函式庫中明確定義 `log` 函式,並考慮其輸出格式(例如是否包含時間戳、日誌級別等),以便於日誌分析和問題追蹤。雖然此 diff 未包含 `log` 函式的定義,但其廣泛使用使其成為一個值得關注的點。", + "location": "entrypoint.sh:249", + "suggestion": "為了提高 `trap` 命令的穩健性並避免潛在的引用問題,建議將清理邏輯封裝在一個獨立的函數中,然後 `trap` 該函數。例如:\n\n```bash\ncleanup() {\n rm -f -- \"${candidate_file}\"\n}\ntrap cleanup EXIT\n```\n\n目前的寫法 `trap \"rm -f -- '${candidate_file}'\" EXIT` 在 `candidate_file` 包含特殊字元(如單引號)時可能導致非預期的行為,儘管 `mktemp` 生成的檔名通常不會有此問題。", "is_new": true }, { @@ -158,20 +172,13 @@ "role": "Rex", "location": "entrypoint.sh:300", "suggestion": "雖然目前程式碼對 `RESOLVED_GITEA_TOKEN` 的使用已盡量小心,但將敏感資訊(如 API token)以 `export` 方式設定為環境變數,可能會在某些情況下(例如子程序繼承環境變數、或系統日誌意外記錄環境)造成資訊洩漏。建議考慮在 `curl` 命令中直接使用 `-H \"Authorization: token ${token}\"` 而非依賴 `export`,以限制 token 的作用域,或確保所有子程序都不會意外地存取或記錄此變數。", - "is_new": true + "is_new": false }, { "level": "info", - "role": "Aria", - "location": "entrypoint.sh:13", - "suggestion": "trim 函數中的參數擴展 (`value=\"${value#\"${value%%[![:space:]]*}\"}\"`) 雖然高效且為純 Bash 實現,但對於不熟悉 Bash 進階語法的人來說可能較難理解。可以考慮添加更詳細的註釋來解釋其工作原理,或在極端追求可讀性的情況下,使用 `sed` 或 `awk` 等工具來實現,儘管這會引入外部依賴。", - "is_new": true - }, - { - "level": "info", - "role": "Maya", - "location": "entrypoint.sh", - "suggestion": "建議在專案中新增一個 `test/` 目錄,將所有測試腳本放在其中。並將這些測試整合到 CI/CD 流程中,確保每次程式碼變更都能自動執行測試,從而及早發現問題並維持程式碼品質。", + "role": "Rex", + "location": "entrypoint.sh:342-344,354", + "suggestion": "審查日誌中輸出的環境變數和輸入值。雖然這些資訊(Gitea Server URL, Repository, Package Names)通常不包含直接的機密,但若其中包含任何敏感資料或可被利用的資訊,可能會造成洩漏。建議僅在必要時記錄,並考慮對敏感資訊進行遮蔽處理。", "is_new": true } ]