Skip to content

Commit 56e0adf

Browse files
committed
ci/maintainer: merge same tag with different paths, remove Path display from CI comment
1 parent f00b0a4 commit 56e0adf

File tree

1 file changed

+171
-92
lines changed

1 file changed

+171
-92
lines changed

.github/workflows/auto-assign-reviewers.yml

Lines changed: 171 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# 2025-03-14 hydevcode
1010
# 2025-05-10 kurisaW Fixed file existence, cache, and comment time issues
1111
# 2025-05-11 kurisaW Fixed missing unique files creation and cache logic
12+
# 2025-07-14 kurisaW Merge same tag with different paths, remove Path display from CI comment
1213

1314
# Script Function Description: Assign PR reviews based on the MAINTAINERS list.
1415

@@ -54,97 +55,160 @@ jobs:
5455
"https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments" | \
5556
jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64')
5657
57-
echo "=== Changed Files ==="
58-
cat changed_files.txt
59-
echo "====================="
60-
6158
comment_body=""
6259
if [[ ! -z "$existing_comment" ]]; then
63-
comment_body=$(echo "$existing_comment" | head -1 | base64 -d | jq -r .body | sed -nE 's/.*Last Updated: ([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2} UTC).*/\1/p')
64-
comment_time=$(date -d "$comment_body" +%s)
65-
echo "${comment_body}"
60+
comment_body=$(echo "$existing_comment" | head -1 | base64 -d | jq -r .body | sed -nE 's/.*Last Updated: ([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2} CST).*/\1/p')
61+
comment_time=$(TZ='Asia/Shanghai' date -d "$comment_body" +%s)
62+
echo "CACHE_TIMESTAMP=${comment_time}" >> $GITHUB_OUTPUT # 统一使用这个变量名
6663
echo "COMMENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
6764
else
6865
comment_time=""
69-
echo "COMMENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
66+
echo "CACHE_TIMESTAMP=" >> $GITHUB_OUTPUT
67+
echo "COMMENT_TIME=" >> $GITHUB_OUTPUT
7068
fi
71-
echo "COMMENT_TIME=${comment_time}"
69+
echo "Debug - CACHE_TIMESTAMP: $comment_time"
7270
7371
- name: Parse MAINTAINERS file
7472
id: parse_maintainer
7573
run: |
76-
# 使用 AWK 解析 MAINTAINERS 文件格式:
77-
# 提取 tag(标签)、path(路径)和 owners(维护者 GitHub ID)
74+
set -euo pipefail
7875
awk '
76+
BEGIN{ tag=""; paths=""; owners="" }
7977
/^tag:/ {
80-
tag = substr($0, index($0, $2)) # 提取标签内容
78+
tag = substr($0, index($0, $2));
79+
paths=""; owners=""
8180
}
8281
/^path:/ {
83-
# 提取 path 字段并去除前后空格
84-
path = substr($0, index($0, $2))
85-
gsub(/^[ \t]+|[ \t]+$/, "", path) # 清理前后空格和制表符
82+
path = substr($0, index($0, $2))
83+
gsub(/^[ \t]+|[ \t]+$/, "", path)
84+
paths = (paths == "" ? path : paths "|" path)
8685
}
8786
/^owners:/ {
88-
owners = substr($0, index($0, $2)) # 提取维护者信息
89-
split(owners, parts, /[()]/) # 拆分出 GitHub ID(括号内内容)
90-
github_ids = ""
91-
for (i=2; i<=length(parts); i+=2) {
92-
github_ids = github_ids "@" parts[i] " " # 拼接为 @user 格式
93-
}
94-
print tag "|" path "|" github_ids
87+
owners = substr($0, index($0, $2))
88+
n = split(owners, parts, /[()]/)
89+
github_ids=""
90+
for (i=2; i<=n; i+=2) {
91+
id=parts[i]
92+
gsub(/^[ \t@]+|[ \t]+$/, "", id)
93+
if(id != "") github_ids=github_ids "@" id " "
94+
}
95+
print tag "|" paths "|" github_ids
96+
tag=""; paths=""; owners=""
9597
}
9698
' MAINTAINERS > tag_data.csv
9799
98-
- name: Generate reviewers list
100+
- name: Generate reviewers list and tag-file mapping
99101
id: generate_reviewers
100102
run: |
101-
rm -f triggered_reviewers.txt triggered_tags.txt unique_reviewers.txt unique_tags.txt
103+
rm -f triggered_reviewers.txt triggered_tags.txt unique_reviewers.txt unique_tags.txt tag_files_map.json tag_reviewers_map.txt
102104
touch triggered_reviewers.txt triggered_tags.txt unique_reviewers.txt unique_tags.txt
103105
104-
while IFS='|' read -r tag path reviewers; do
105-
# 转义路径中的正则特殊字符
106-
escaped_path=$(sed 's/[.[\*^$]/\\&/g' <<< "$path")
107-
108-
# 使用增强型正则匹配路径及其所有子目录
109-
if grep -qE "^$escaped_path(/.*)*" changed_files.txt; then
110-
echo "$reviewers" | tr -s ' ' '\n' | sed '/^$/d' >> triggered_reviewers.txt
111-
echo "$tag" >> triggered_tags.txt
112-
echo "Matched: $path → $tag"
113-
fi
106+
# 1. 读取 tag_data.csv,建立 tag -> [paths], tag -> reviewers
107+
declare -A tag_paths_map
108+
declare -A tag_reviewers_map
109+
110+
while IFS='|' read -r tag paths reviewers; do
111+
IFS='|' read -ra path_arr <<< "$paths"
112+
for p in "${path_arr[@]}"; do
113+
tag_paths_map["$tag"]+="$p;"
114+
done
115+
# 合并 reviewers,去重,只保留合法格式
116+
existing_reviewers="${tag_reviewers_map["$tag"]}"
117+
all_reviewers="$existing_reviewers $reviewers"
118+
# 只保留 @xxx 格式,去重
119+
all_reviewers=$(echo "$all_reviewers" | grep -o '@[A-Za-z0-9_-]\+' | sort -u | tr '\n' ' ')
120+
tag_reviewers_map["$tag"]="$all_reviewers"
114121
done < tag_data.csv
115122
123+
# 2. 针对每个 tag,找出它所有 path 匹配的变更文件
124+
declare -A tag_changedfiles_map
125+
while IFS= read -r changed; do
126+
for tag in "${!tag_paths_map[@]}"; do
127+
IFS=';' read -ra tpaths <<< "${tag_paths_map[$tag]}"
128+
for tpath in "${tpaths[@]}"; do
129+
[[ -z "$tpath" ]] && continue
130+
if [[ -f "$tpath" ]]; then
131+
# 精确文件名
132+
[[ "$changed" == "$tpath" ]] && tag_changedfiles_map["$tag"]+="$changed;"
133+
else
134+
# 目录前缀
135+
[[ "$changed" == $tpath* ]] && tag_changedfiles_map["$tag"]+="$changed;"
136+
fi
137+
done
138+
done
139+
done < changed_files.txt
140+
141+
# 3. 输出合并后的 tag reviewers、tag、并去重
142+
for tag in "${!tag_changedfiles_map[@]}"; do
143+
reviewers="${tag_reviewers_map[$tag]}"
144+
echo "$reviewers" | tr -s ' ' '\n' | sed '/^$/d' >> triggered_reviewers.txt
145+
echo "$tag" >> triggered_tags.txt
146+
done
147+
116148
# 生成去重的 unique_reviewers.txt 和 unique_tags.txt
117149
sort -u triggered_reviewers.txt > unique_reviewers.txt
118150
sort -u triggered_tags.txt > unique_tags.txt
119151
120-
# 检查是否有匹配的 reviewers
152+
# 4. 输出 tag_files_map.json,格式 { "tag1": ["file1","file2"], ... }
153+
{
154+
echo "{"
155+
first_tag=1
156+
for tag in "${!tag_changedfiles_map[@]}"; do
157+
[[ $first_tag -eq 0 ]] && echo ","
158+
echo -n " \"${tag}\": ["
159+
IFS=';' read -ra files <<< "${tag_changedfiles_map[$tag]}"
160+
file_list=""
161+
for f in "${files[@]}"; do
162+
[[ -z "$f" ]] && continue
163+
[[ -n "$file_list" ]] && file_list+=", "
164+
file_list+="\"$f\""
165+
done
166+
echo -n "$file_list"
167+
echo -n "]"
168+
first_tag=0
169+
done
170+
echo ""
171+
echo "}"
172+
} > tag_files_map.json
173+
174+
# 5. 保存聚合去重后的 reviewers 到 tag_reviewers_map.txt
175+
{
176+
for tag in "${!tag_reviewers_map[@]}"; do
177+
echo "$tag|${tag_reviewers_map[$tag]}"
178+
done
179+
} > tag_reviewers_map.txt
180+
181+
# 6. 标记是否有 reviewer
121182
if [[ -s unique_reviewers.txt ]]; then
122183
echo "HAS_REVIEWERS=true" >> $GITHUB_OUTPUT
123184
else
124185
echo "HAS_REVIEWERS=false" >> $GITHUB_OUTPUT
125186
fi
126187
127-
echo "=== Matched Paths ==="
188+
echo "=== Matched Tags ==="
128189
cat unique_tags.txt
129190
echo "=== Matched Reviewers ==="
130191
cat unique_reviewers.txt
192+
echo "=== Tag-ChangedFiles Map ==="
193+
cat tag_files_map.json
131194
132195
- name: Restore Reviewers Cache
133-
id: reviewers-cache-restore
134-
if: ${{ steps.changed_files.outputs.COMMENT_TIME != '' }}
196+
id: reviewers-cache-restore
197+
if: ${{ steps.changed_files.outputs.CACHE_TIMESTAMP != '' }}
135198
uses: actions/cache/restore@v4
136-
with:
199+
with:
137200
path: |
138201
unique_tags_bak.txt
139202
unique_reviewers_bak.txt
140-
key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.changed_files.outputs.COMMENT_TIME }}
203+
key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.changed_files.outputs.CACHE_TIMESTAMP }}-${{ github.run_id }}
204+
restore-keys: |
205+
${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.changed_files.outputs.CACHE_TIMESTAMP }}-
206+
${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-
141207
142208
- name: Get approval status
143209
id: get_approval
144210
run: |
145-
current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
146-
147-
# 检查 unique_reviewers.txt 是否存在且非空
211+
current_time=$(TZ='Asia/Shanghai' date +"%Y-%m-%d %H:%M CST")
148212
if [[ ! -s unique_reviewers.txt ]]; then
149213
echo "No reviewers found, creating empty unique_reviewers.txt"
150214
touch unique_reviewers.txt
@@ -183,39 +247,35 @@ jobs:
183247
select($mention | inside($reviewers)) | # 过滤有效审查者
184248
"\($mention) \(.created_at)" # 输出审查者和时间
185249
' <<< "$comments" >> approval_data.txt
186-
187250
notified_users=""
188251
if [[ -f unique_reviewers_bak.txt ]]; then
189252
notified_users=$(cat unique_reviewers_bak.txt | xargs)
190253
else
191254
notified_users=""
192255
fi
193-
194256
{
195257
echo "---"
196258
echo "### 📊 Current Review Status (Last Updated: $current_time)"
197259
while read -r reviewer; do
198260
formatted_reviewers=""
199261
for r in $reviewers; do
200262
if [[ " ${notified_users[@]} " =~ " $reviewer " ]]; then
201-
formatted_reviewers+="${reviewer#@}"
263+
formatted_reviewers+="${reviewer#@}"
202264
else
203-
formatted_reviewers+="$reviewer"
265+
formatted_reviewers+="$reviewer"
204266
fi
205267
done
206-
207268
if [[ -n "${approvals[$reviewer]}" ]]; then
208-
timestamp=$(date -d "${approvals[$reviewer]}" -u +"%Y-%m-%d %H:%M UTC")
269+
timestamp=$(TZ='Asia/Shanghai' date -d "${approvals[$reviewer]}" +"%Y-%m-%d %H:%M CST")
209270
echo "- ✅ **$formatted_reviewers** Reviewed On $timestamp"
210271
else
211272
echo "- ⌛ **$formatted_reviewers** Pending Review"
212273
fi
213274
done < unique_reviewers.txt
214275
} > review_status.md
215-
216276
echo "CURRENT_TIME=${current_time}" >> $GITHUB_OUTPUT
217277
218-
- name: Generate review data
278+
- name: Generate review data (tag merge, no path in comment, changed files summary per tag)
219279
id: generate_review
220280
if: steps.generate_reviewers.outputs.HAS_REVIEWERS == 'true'
221281
run: |
@@ -227,49 +287,55 @@ jobs:
227287
if [[ -f unique_tags_bak.txt ]]; then
228288
unique_tags_bak=$(cat unique_tags_bak.txt | xargs)
229289
fi
230-
231-
existing_tags=""
232-
for r in $unique_tags; do
233-
if [[ " ${unique_tags_bak[@]} " =~ " $r " ]]; then
234-
echo "$r 不存在于数组中"
235-
else
236-
existing_tags+="$r "
237-
fi
238-
done
239-
240-
current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
290+
# 读取 tag->files 映射
291+
declare -A tag_files_map
292+
eval "$(jq -r 'to_entries[] | "tag_files_map[\"\(.key)\"]=\"\(.value | join(";"))\"" ' tag_files_map.json)"
293+
# 读取 tag->reviewers(只读聚合去重后的结果)
294+
declare -A tag_reviewers_map
295+
while IFS='|' read -r tag reviewers; do
296+
tag_reviewers_map["$tag"]="$reviewers"
297+
done < tag_reviewers_map.txt
298+
# 获取已通知的 reviewers
299+
notified_users=""
300+
if [[ -f unique_reviewers_bak.txt ]]; then
301+
notified_users=$(cat unique_reviewers_bak.txt | xargs)
302+
fi
303+
current_time=$(TZ='Asia/Shanghai' date +"%Y-%m-%d %H:%M CST")
241304
{
242-
# 生成审查分配信息
243305
echo "## 📌 Code Review Assignment"
244306
echo ""
245-
246-
while IFS='|' read -r tag path reviewers; do
247-
if grep -qE "^$path(/|$)" changed_files.txt; then
248-
echo "### 🏷️ Tag: $tag"
249-
echo "**Path:** \`$path\` "
250-
251-
if [[ " ${existing_tags[@]} " =~ " $tag " ]]; then
252-
echo "**Reviewers:** $reviewers "
307+
for tag in $unique_tags; do
308+
reviewers="${tag_reviewers_map[$tag]}"
309+
# 移除尾部空格并提取有效的@username格式
310+
reviewers=$(echo "$reviewers" | sed 's/[[:space:]]*$//' | grep -o '@[A-Za-z0-9_-]\+' | sort -u | tr '\n' ' ')
311+
312+
# 格式化reviewers显示(仅对已通知用户去掉@)
313+
formatted_reviewers=""
314+
for reviewer in $reviewers; do
315+
if [[ " ${notified_users[@]} " =~ " $reviewer " ]]; then
316+
formatted_reviewers+="${reviewer#@} " # 已通知用户去掉@
253317
else
254-
formatted_reviewers=""
255-
for r in $reviewers; do
256-
formatted_reviewers+="${r#@} "
257-
done
258-
echo "**Reviewers:** $formatted_reviewers "
318+
formatted_reviewers+="$reviewer " # 未通知用户保留@
259319
fi
260-
261-
echo "<details>"
262-
echo "<summary><b>Changed Files</b> (Click to expand)</summary>"
263-
echo ""
264-
grep -E "^$path(/|$)" changed_files.txt | sed 's/^/- /' # 列出匹配的变更文件
265-
echo ""
266-
echo "</details>"
267-
echo ""
268-
fi
269-
done < tag_data.csv
320+
done
321+
322+
echo "### 🏷️ Tag: $tag"
323+
echo ""
324+
echo "**Reviewers:** $formatted_reviewers" # 确保显示Reviewers
325+
echo "<details>"
326+
echo "<summary><b>Changed Files</b> (Click to expand)</summary>"
327+
echo ""
328+
IFS=';' read -ra files <<< "${tag_files_map[$tag]}"
329+
for file in "${files[@]}"; do
330+
[[ -z "$file" ]] && continue
331+
echo "- $file"
332+
done
333+
echo ""
334+
echo "</details>"
335+
echo ""
336+
done
270337
# 插入审查状态
271338
cat review_status.md
272-
273339
echo "---"
274340
echo "### 📝 Review Instructions"
275341
echo ""
@@ -321,17 +387,30 @@ jobs:
321387
"https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments" | \
322388
jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64')
323389
comment_body="${{ steps.get_approval.outputs.CURRENT_TIME }}"
324-
comment_time=$(date -d "$comment_body" +%s)
325-
echo "CURRENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
326-
cp unique_reviewers.txt unique_reviewers_bak.txt
327-
cp unique_tags.txt unique_tags_bak.txt
390+
comment_time=$(TZ='Asia/Shanghai' date -d "$comment_body" +%s)
391+
echo "CACHE_TIMESTAMP=${comment_time}" >> $GITHUB_OUTPUT # 统一使用这个变量名
392+
echo "Debug - Saving cache with timestamp: $comment_time"
393+
394+
mkdir -p $(dirname unique_reviewers_bak.txt)
395+
if [[ -s unique_reviewers.txt ]]; then
396+
cp unique_reviewers.txt unique_reviewers_bak.txt
397+
else
398+
touch unique_reviewers_bak.txt
399+
fi
400+
401+
if [[ -s unique_tags.txt ]]; then
402+
cp unique_tags.txt unique_tags_bak.txt
403+
else
404+
touch unique_tags_bak.txt
405+
fi
328406
329407
- name: Save Reviewers Cache
330408
id: reviewers-cache-save
331409
if: steps.generate_reviewers.outputs.HAS_REVIEWERS == 'true'
410+
continue-on-error: true
332411
uses: actions/cache/save@v4
333412
with:
334413
path: |
335414
unique_tags_bak.txt
336415
unique_reviewers_bak.txt
337-
key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.get_comment_time.outputs.CURRENT_TIME }}
416+
key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.get_comment_time.outputs.CACHE_TIMESTAMP }}-${{ github.run_id }}

0 commit comments

Comments
 (0)