Merge pull request 'feat/ai_code_review' (#7) from feat/ai_code_review into develop

Reviewed-on: #7
This commit is contained in:
2026-05-16 16:03:58 +00:00
14 changed files with 384 additions and 14 deletions
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
When the task is to triage review findings, follow this workflow:
1. Merge all findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1 after sorting.
5. Fix real issues with the smallest safe change.
6. Add false positives to `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
7. Add or update tests when behavior changes.
8. Re-check the issue after each fix.
Use the repo-local `triage-findings` skill for the same workflow when running in Codex.
+29
View File
@@ -0,0 +1,29 @@
---
name: triage-findings
description: Triage findings, fix real issues, and exclude false positives.
---
# Triage Findings
## Use
直接輸入:`triage-findings 問題原始檔(文字或截圖)`
## Workflow
1. Merge all findings.
2. Sort by severity:
- critical
- warning
- info
3. Renumber from 1.
4. Fix real issues.
5. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
6. Add tests when behavior changes.
## Output Rules
- Keep the final list short.
- Keep numbering contiguous.
- Preserve file path, location, and fix.
- When writing exclusions, prefer the original issue text over paraphrased rewrites.
+45
View File
@@ -0,0 +1,45 @@
---
name: triage-findings
description: Merge code-review findings, sort and renumber them by severity, resolve real issues, and move false positives into exclusions.
---
# Triage Findings
## When To Use
Use this skill when you receive multiple review findings, screenshots, comments, or issue lists that need to become one final triaged list.
It is also used when some findings are false positives and should be moved into the exclusions list.
## Workflow
1. Collect all findings into one list.
2. Merge duplicates into a single finding when they describe the same issue.
3. Sort the final list by severity:
- critical
- warning
- info
4. Renumber the sorted list from 1 upward.
5. Rewrite each finding concisely so the final list reads cleanly and consistently.
6. If a finding is a false positive, do not keep it in the final list.
7. Add false positives to the exclusions list using the existing schema in the repo or task context, and preserve the original finding wording as much as possible, including language and semantics.
## Resolution Flow
After the list is merged and ordered, resolve the remaining findings one by one.
1. Start from the highest severity item.
2. Identify the root cause in the relevant file or context.
3. Apply the smallest safe change that fixes the issue.
4. Add or update tests when behavior changes.
5. Re-check the issue after the change.
6. If the item is confirmed false positive, move it to exclusions instead of changing code.
7. Continue until the list is either fixed or explicitly excluded.
## Output Rules
- Keep the final findings list in severity order, then by any stable secondary order needed to make it readable.
- Keep numbering contiguous after filtering and merging.
- Preserve useful details like file path, location, and suggested fix.
- Keep exclusions entries minimal and consistent with the project schema.
- When writing exclusions, prefer the original issue text and language; only paraphrase if needed to fit the schema.
- If the source already provides a severity or title, keep it unless it conflicts with the final ordering.
@@ -0,0 +1,4 @@
interface:
display_name: "Triage Findings"
short_description: "Triage, sort, fix, and exclude review findings"
default_prompt: "Use $triage-findings to merge review findings, sort and renumber them by severity, resolve real issues one by one, and add false positives to exclusions."
+29
View File
@@ -0,0 +1,29 @@
---
name: triage-findings
description: Triage findings, fix real issues, and exclude false positives.
---
# Triage Findings
## Use
直接輸入:`triage-findings 問題原始檔(文字或截圖)`
## Workflow
1. Merge all findings.
2. Sort by severity:
- critical
- warning
- info
3. Renumber from 1.
4. Fix real issues.
5. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
6. Add tests when behavior changes.
## Output Rules
- Keep the final list short.
- Keep numbering contiguous.
- Preserve file path, location, and fix.
- When writing exclusions, prefer the original issue text over paraphrased rewrites.
+149
View File
@@ -0,0 +1,149 @@
[
{
"level": "critical",
"role": "Rex",
"location": "entrypoint.sh:40 (舊版)",
"suggestion": "舊版程式碼中的 `zip -r \"$(basename \"$PWD\").$RELEASE_VERSION.zip\" $RELEASE_FOLDER/*` 存在嚴重的命令注入風險。`$RELEASE_FOLDER/*` 未被引用 (unquoted),若 `$RELEASE_FOLDER` 包含惡意檔案名稱(例如含有特殊字元、命令替換),或檔案數量過多導致參數列表溢出,可能導致任意命令執行或服務阻斷。新版程式碼透過 `(cd \"$RELEASE_FOLDER\"; zip -qr \"$archive_path\" .)` 的方式,安全地切換目錄並壓縮當前目錄內容,有效避免了此類風險,這是非常重要的安全改進。",
"reason": "false positive"
},
{
"level": "warning",
"role": "Zara",
"location": "b/entrypoint.sh:66",
"suggestion": "新增的 `file_count=\"$(find \"$RELEASE_FOLDER\" -type f | wc -l | tr -d ' ')\"` 指令會對 `$RELEASE_FOLDER` 進行一次完整的檔案系統遍歷以計算檔案數量。對於包含大量檔案或深層目錄的資料夾,這會增加額外的 I/O 開銷和執行時間,可能在壓縮前造成明顯的延遲。請評估此檔案計數是否為必要資訊,若僅用於日誌記錄,可考慮移除或採用更輕量級的估算方式,以避免不必要的效能損耗。",
"reason": "false positive"
},
{
"level": "warning",
"role": "Rex",
"location": "entrypoint.sh:1 (舊版)",
"suggestion": "舊版程式碼缺少 `set -euo pipefail`。`set -e` 確保指令失敗時腳本立即退出,防止在錯誤狀態下繼續執行;`set -u` 確保使用未定義變數時腳本報錯,避免潛在的意外行為或注入;`set -o pipefail` 確保管道命令的錯誤能被正確捕獲。新版程式碼已加入這些設定,顯著提升了腳本的安全性與穩定性,降低了因未預期錯誤導致的安全風險。",
"reason": "false positive"
},
{
"level": "warning",
"role": "Rex",
"location": "entrypoint.sh:34 (新版)",
"suggestion": "舊版程式碼在壓縮前未檢查 `$RELEASE_FOLDER` 是否為一個有效的目錄。若 `$RELEASE_FOLDER` 不存在或指向非目錄路徑,可能導致腳本執行失敗或產生非預期的行為,甚至在某些情況下可能被利用。新版程式碼已新增 `if [[ ! -d \"$RELEASE_FOLDER\" ]]` 檢查,提升了腳本的健壯性與安全性。",
"reason": "false positive"
},
{
"level": "info",
"role": "Leo",
"location": "entrypoint.sh:10",
"suggestion": "為新定義的函式 `log_section` 添加簡短的註解,說明其用途(例如:用於輸出帶有邊框和分隔線的區塊標題)。這有助於未來維護者快速理解函式功能。",
"reason": "false positive"
},
{
"level": "info",
"role": "Leo",
"location": "entrypoint.sh:14",
"suggestion": "為新定義的函式 `log_kv` 添加簡短的註解,說明其用途(例如:用於輸出鍵值對資訊,並確保對齊)。這有助於未來維護者快速理解函式功能。",
"reason": "false positive"
},
{
"level": "info",
"role": "Leo",
"location": "entrypoint.sh:18",
"suggestion": "為新定義的函式 `fail` 添加簡短的註解,說明其用途(例如:用於輸出錯誤訊息並以非零狀態碼退出腳本)。這有助於未來維護者快速理解函式功能。",
"reason": "false positive"
},
{
"level": "info",
"role": "Leo",
"location": "entrypoint.sh:22",
"suggestion": "為新定義的函式 `require_value` 添加簡短的註解,說明其用途(例如:用於檢查指定變數是否為空或 'null',若否則呼叫 `fail` 函式退出)。這有助於未來維護者快速理解函式功能。",
"reason": "false positive"
},
{
"level": "info",
"role": "Rex",
"location": "entrypoint.sh:38 (新版)",
"suggestion": "舊版程式碼未檢查 `zip` 命令是否存在。若執行環境中缺少 `zip` 工具,腳本將會失敗,影響可靠性。新版程式碼已加入 `command -v zip` 檢查,確保必要工具的存在,提升了腳本的可靠性,這是一個良好的安全實踐。",
"reason": "false positive"
},
{
"level": "info",
"role": "Aria",
"location": "entrypoint.sh:13",
"suggestion": "在 `log_kv` 函數中,`printf` 的格式字串 `%-16s` 硬編碼了鍵的寬度為 16。若鍵的字串長度超過此值,對齊效果將會失效。建議考慮動態計算最大鍵寬度,或使用更具彈性的對齊方式,以提高日誌輸出的健壯性。",
"reason": "false positive"
},
{
"level": "info",
"role": "Aria",
"location": "entrypoint.sh:44",
"suggestion": "建議將 `archive_name` 宣告為 `readonly`,以明確其為常數且不應被修改,增加程式碼的清晰度與維護性。",
"reason": "false positive"
},
{
"level": "info",
"role": "Aria",
"location": "entrypoint.sh:45",
"suggestion": "建議將 `archive_path` 宣告為 `readonly`,以明確其為常數且不應被修改,增加程式碼的清晰度與維護性。",
"reason": "false positive"
},
{
"level": "info",
"role": "Aria",
"location": "entrypoint.sh:46",
"suggestion": "建議將 `file_count` 宣告為 `readonly`,以明確其為常數且不應被修改,增加程式碼的清晰度與維護性。",
"reason": "false positive"
},
{
"level": "critical",
"role": "Maya",
"location": "entrypoint.sh",
"suggestion": "此 `entrypoint.sh` 腳本是應用程式的進入點,其穩定性至關重要。然而,此 diff 中並未看到任何相關的測試程式碼(例如單元測試或整合測試)。應為此腳本建立一套完整的測試,涵蓋所有功能路徑、錯誤處理和邊界條件,以確保其可靠性。",
"reason": "false positive"
},
{
"level": "critical",
"role": "Maya",
"location": "entrypoint.sh:37-44",
"suggestion": "腳本新增了兩項重要的前置檢查:驗證 `RELEASE_FOLDER` 是否存在,以及 `zip` 指令是否可用。應新增測試案例,驗證當 `RELEASE_FOLDER` 不存在時,腳本會以錯誤碼退出並輸出正確的錯誤訊息。同時,也應新增測試案例,驗證當 `zip` 指令不存在時,腳本會以錯誤碼退出並輸出正確的錯誤訊息。",
"reason": "false positive"
},
{
"level": "critical",
"role": "Maya",
"location": "entrypoint.sh:55-58",
"suggestion": "壓縮邏輯已從 `zip -r ... $RELEASE_FOLDER/*` 變更為 `(cd \"$RELEASE_FOLDER\"; zip -qr \"$archive_path\" .)`。這會顯著改變壓縮檔內部的檔案結構(例如,不再包含頂層的 `RELEASE_FOLDER` 名稱)。應新增測試案例,驗證壓縮後的 `.zip` 檔案內容結構是否符合預期,確認檔案路徑是相對於 `RELEASE_FOLDER` 的根目錄。",
"reason": "false positive"
},
{
"level": "warning",
"role": "Maya",
"location": "entrypoint.sh:55-58",
"suggestion": "應新增測試案例,驗證當 `RELEASE_FOLDER` 存在但為空時,腳本是否能正確執行並產生一個空的(或只包含目錄結構的)壓縮檔,且不會產生錯誤或非預期的行為。",
"reason": "false positive"
},
{
"level": "warning",
"role": "Maya",
"location": "entrypoint.sh:55-58",
"suggestion": "應新增測試案例,驗證當 `RELEASE_FOLDER` 內包含檔名或目錄名含有特殊字元(例如空格、括號、UTF-8 字元等)的檔案時,壓縮和解壓縮是否能正常運作,確保檔案完整性。",
"reason": "false positive"
},
{
"level": "info",
"role": "Maya",
"location": "entrypoint.sh",
"suggestion": "考慮引入一個輕量級的 shell 腳本測試框架,例如 `bats-core` 或 `shunit2`,以結構化和自動化測試流程。這將有助於提高測試的可維護性、可讀性,並更容易地擴展測試覆蓋率。",
"reason": "false positive"
},
{
"level": "critical",
"role": "Zara",
"location": "entrypoint.sh",
"suggestion": "舊版程式碼中的 `zip -r \"$(basename \"$PWD\").$RELEASE_VERSION.zip\" $RELEASE_FOLDER/*` 命令,在 `$RELEASE_FOLDER` 包含大量檔案或子目錄時,可能因 shell 的參數列表過長限制 (ARG_MAX) 而導致指令失敗。新版程式碼透過 `(cd \"$RELEASE_FOLDER\"; zip -qr \"$archive_path\" .)` 的方式,在子 shell 中切換目錄並壓縮當前目錄 (`.`),有效避免了此參數限制問題,顯著提升了壓縮操作的穩定性與對大型資料集的處理能力。此關鍵效能與穩定性問題已獲得良好解決。",
"reason": "false positive"
},
{
"level": "warning",
"role": "Zara",
"location": "entrypoint.sh:44",
"suggestion": "`file_count=\"$(find \"$RELEASE_FOLDER\" -type f | wc -l | tr -d ' ')\"` 這行程式碼在執行壓縮前會完整遍歷一次 `$RELEASE_FOLDER` 以計算檔案數量。對於包含極大量檔案的目錄,這會增加額外的 I/O 操作和處理時間。如果檔案數量資訊並非每次執行都絕對必要,或可接受在壓縮完成後再計算,則可考慮移除此步驟以減少預處理開銷,進一步提升效率。",
"reason": "false positive"
}
]
+1
View File
@@ -0,0 +1 @@
[]
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: 發布專案 - name: 發布專案
uses: akkuman/gitea-release-action@${{ vars.ACTION_RELEASE_VERSION }} uses: akkuman/gitea-release-action@${{ vars.ACTION_RELEASE_VERSION }}
with: with:
tag_name: "v${{ needs.version.outputs.version }}" tag_name: v${{ needs.version.outputs.version }}
- name: 清理成品 - name: 清理成品
uses: https://gitea.jsc.idv.tw/actions/cleanup-release@${{ vars.ACTION_CLEANUP_RELEASE_VERSION }} uses: https://gitea.jsc.idv.tw/actions/cleanup-release@${{ vars.ACTION_CLEANUP_RELEASE_VERSION }}
with: with:
+4
View File
@@ -1,6 +1,8 @@
name: AI name: AI
on: on:
pull_request: pull_request:
branches-ignore:
- master
types: [opened, synchronize] types: [opened, synchronize]
jobs: jobs:
code-review: code-review:
@@ -10,6 +12,8 @@ jobs:
- name: AI Code Review - name: AI Code Review
uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }} uses: https://gitea.jsc.idv.tw/actions/code-review@${{ vars.ACTION_CODE_REVIEW_VERSION }}
with: 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_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_BASE_URL: https://generativelanguage.googleapis.com/v1beta
GEMINI_MODEL: ${{ vars.GEMINI_MODEL }} GEMINI_MODEL: ${{ vars.GEMINI_MODEL }}
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
Use the triage-finding workflow for review issue lists:
1. Merge findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1.
5. Fix real issues with the smallest safe change.
6. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
7. Add or update tests when behavior changes.
8. Re-check after each fix.
The full reusable skill lives in `.claude/skills/triage-findings/SKILL.md`.
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
Use the triage-finding workflow for review issue lists:
1. Merge findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1.
5. Fix real issues with the smallest safe change.
6. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
7. Add or update tests when behavior changes.
8. Re-check after each fix.
The reusable skill lives in `.gemini/skills/triage-findings/SKILL.md`.
+16
View File
@@ -0,0 +1,16 @@
# Triage Findings
When the task is to triage review findings, follow this workflow:
1. Merge all findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1 after sorting.
5. Fix real issues with the smallest safe change.
6. Add false positives to `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
7. Add or update tests when behavior changes.
8. Re-check the issue after each fix.
Use the repo-local `triage-findings` skill for the same workflow when running in Codex.
Trigger it with `/triage-findings`.
+14
View File
@@ -0,0 +1,14 @@
# Triage Findings
Use the triage-finding workflow for review issue lists:
1. Merge findings into one list.
2. Remove duplicates.
3. Sort by severity: `critical` -> `warning` -> `info`.
4. Renumber from 1.
5. Fix real issues with the smallest safe change.
6. Put false positives into `.gitea/ai-review/exclusions.json`, preserving the original wording, language, and semantics as much as possible.
7. Add or update tests when behavior changes.
8. Re-check after each fix.
The reusable skill lives in `.gemini/skills/triage-findings/SKILL.md`.
+50 -13
View File
@@ -1,23 +1,60 @@
#!/bin/bash #!/usr/bin/env bash
echo "==================================================" set -euo pipefail
echo "參數檢查" readonly BORDER='=================================================='
readonly DIVIDER='--------------------------------------------------'
echo "--------------------------------------------------" log_section() {
printf '%s\n%s\n%s\n' "$BORDER" "$1" "$DIVIDER"
}
# 顯示 RELEASE_VERSION 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出 log_kv() {
echo "RELEASE_VERSION=$RELEASE_VERSION" && ([ -z "$RELEASE_VERSION" ] || [ "$RELEASE_VERSION" = "null" ]) && exit 1 printf '%-16s %s\n' "$1" "$2"
}
# 顯示 RELEASE_FOLDER 參數,並檢查是否為空或 "null",如果是則輸出錯誤訊息並退出 fail() {
echo "RELEASE_FOLDER=$RELEASE_FOLDER" && ([ -z "$RELEASE_FOLDER" ] || [ "$RELEASE_FOLDER" = "null" ]) && exit 1 printf '錯誤: %s\n' "$1" >&2
exit 1
}
echo "==================================================" require_value() {
local name="$1"
local value="${2:-}"
echo "壓縮檔案" log_kv "$name" "$value"
echo "--------------------------------------------------" if [[ -z "$value" || "$value" == "null" ]]; then
fail "$name 不能為空"
fi
}
zip -r "$(basename "$PWD").$RELEASE_VERSION.zip" $RELEASE_FOLDER/* log_section '參數檢查'
require_value 'RELEASE_VERSION' "${RELEASE_VERSION:-}"
require_value 'RELEASE_FOLDER' "${RELEASE_FOLDER:-}"
echo "==================================================" if [[ ! -d "$RELEASE_FOLDER" ]]; then
fail "找不到資料夾: $RELEASE_FOLDER"
fi
if ! command -v zip >/dev/null 2>&1; then
fail '找不到 zip 指令,請確認映像檔已安裝 zip'
fi
archive_name="$(basename "$PWD").${RELEASE_VERSION}.zip"
archive_path="$PWD/$archive_name"
file_count="$(find "$RELEASE_FOLDER" -type f | wc -l | tr -d ' ')"
log_section '壓縮檔案'
log_kv '來源資料夾' "$RELEASE_FOLDER"
log_kv '檔案數量' "$file_count"
log_kv '輸出檔案' "$archive_name"
(
cd "$RELEASE_FOLDER"
zip -qr "$archive_path" .
)
log_kv '狀態' '壓縮完成'
log_kv '檔案位置' "$archive_path"
printf '%s\n' "$BORDER"