Files
review/tools/merge-findings.sh
T
jiantw83 a0bd488ac8 feat(tools): 新增發現合併與變更註解兩支腳本
- 合併、去重、排序原本寫在兩支技能的步驟文字裡,各寫一份就會各自演化。改由腳本執行,並定下六欄 TSV 的回報格式。格式不對就擋下並指出是第幾列,不猜、不放行、不自行補欄。
- 另一支腳本列出本次改到的註解行,清理範圍不再靠肉眼判讀。
- 註解樣式與非程式碼副檔名清單沿用註解範圍腳本的同一份,不另立第二套判準。markdown 的標題行開頭就是井字號,判準不一致就會把整份文件當成註解。
2026-08-31 11:07:05 +08:00

111 lines
4.0 KiB
Bash
Executable File
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.
#!/usr/bin/env sh
# merge-findings.sh — 合併多組審查發現,依位置去重,再依嚴重度排序。
# 用法:merge-findings.sh [發現檔…]
# 給檔名就讀那幾個檔,不給檔名就讀 stdin。code-review 六組與 api-doc 兩個面向
# 共用這一支,兩支技能都不再自己描述合併與排序規則。
#
# 輸入格式(TSV,一列一筆發現,六欄,欄位之間一個 tab):
# 1 file 檔案路徑,相對專案根目錄,不得留空
# 2 line 行號,正整數
# 3 severity 嚴重度,只收 高、中、低
# 4 group 組別或面向代號,例如 2 或 1 狀態碼
# 5 item 發現名稱,壞味道名稱或稽核項目名稱
# 6 detail 一句證據加上建議修法
# 空白列、開頭是 # 的列、第一欄為「無發現」的列一律略過。
#
# 輸出(TSV,同樣六欄,一個位置一列,印到 stdout):
# file 與 line 相同的發現合併成一列。
# severity 取該位置最高的一級。group、item、detail 依輸入順序以「|」相連。
# 排序:嚴重度 高 → 中 → 低;同一級再依 file 字典序、line 數值遞增。
#
# 結束碼:
# 0 合併成功,至少印出一列
# 1 沒有任何發現(輸入沒有可用的資料列),呼叫端回報「無發現」
# 2 參數或環境錯誤(找不到輸入檔,或缺 awk、sort、cut)
# 3 輸入格式錯誤(欄數不是 6、行號不是正整數,或嚴重度不在 高、中、低)
#
# 護欄:
# 格式錯誤一律 exit 3 並在 stderr 指出是第幾個檔的第幾列,不猜、不放行、不自行補欄。
# 錯誤訊息一律印繁中到 stderr,正常輸出只走 stdout。
set -u
for cmd in awk sort cut; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "錯誤:環境缺 $cmd,無法合併發現。" >&2
exit 2
}
done
for f in "$@"; do
case "$f" in
-*) echo "用法:merge-findings.sh [發現檔…](不給檔名時讀 stdin)" >&2; exit 2 ;;
esac
[ -f "$f" ] || { echo "錯誤:找不到輸入檔 $f。請確認路徑後重試。" >&2; exit 2; }
done
TAB=$(printf '\t')
TMP="${TMPDIR:-/tmp}/jsc-merge-findings.$$"
trap 'rm -f "$TMP" "$TMP.sorted"' EXIT INT TERM
# 第一欄先放排序用的嚴重度序位,排完再切掉。
awk -F"$TAB" '
BEGIN { OFS = "\t"; n = 0; bad = 0 }
{
sub(/\r$/, "", $0)
if ($0 ~ /^[ \t]*$/) next
if ($0 ~ /^#/) next
if ($1 == "無發現") next
where = (FILENAME == "" || FILENAME == "-") ? "stdin" : FILENAME
if (NF != 6) {
printf("錯誤:%s 第 %d 列有 %d 欄,應為 6 欄。\n", where, FNR, NF) > "/dev/stderr"
bad = 1; next
}
if ($1 == "") {
printf("錯誤:%s 第 %d 列的檔案路徑是空的。\n", where, FNR) > "/dev/stderr"
bad = 1; next
}
if ($2 !~ /^[0-9]+$/ || $2 + 0 < 1) {
printf("錯誤:%s 第 %d 列的行號「%s」不是正整數。\n", where, FNR, $2) > "/dev/stderr"
bad = 1; next
}
if ($3 != "高" && $3 != "中" && $3 != "低") {
printf("錯誤:%s 第 %d 列的嚴重度「%s」不在 高、中、低。\n", where, FNR, $3) > "/dev/stderr"
bad = 1; next
}
rank = ($3 == "高") ? 1 : (($3 == "中") ? 2 : 3)
key = $1 SUBSEP $2
if (!(key in seen)) {
seen[key] = 1
order[++n] = key
kfile[key] = $1; kline[key] = $2; krank[key] = rank
kgroup[key] = $4; kitem[key] = $5; kdetail[key] = $6
} else {
if (rank < krank[key]) krank[key] = rank
kgroup[key] = kgroup[key] "|" $4
kitem[key] = kitem[key] "|" $5
kdetail[key] = kdetail[key] "|" $6
}
}
END {
if (bad) exit 3
if (n == 0) exit 1
for (i = 1; i <= n; i++) {
k = order[i]
sev = (krank[k] == 1) ? "高" : ((krank[k] == 2) ? "中" : "低")
print krank[k], kfile[k], kline[k], sev, kgroup[k], kitem[k], kdetail[k]
}
}
' "$@" > "$TMP"
rc=$?
[ "$rc" -eq 0 ] || exit "$rc"
LC_ALL=C sort -t"$TAB" -k1,1n -k2,2 -k3,3n "$TMP" > "$TMP.sorted" || {
echo "錯誤:排序失敗。" >&2
exit 2
}
cut -f2- "$TMP.sorted" || {
echo "錯誤:輸出失敗。" >&2
exit 2
}
exit 0