Skip to content

Commit 982c969

Browse files
authored
Merge pull request #4 from hydevcode/auto-reviewer
auto-assign-reviewers
2 parents c6e91bd + 11b3d9a commit 982c969

File tree

2 files changed

+150
-74
lines changed

2 files changed

+150
-74
lines changed

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

Lines changed: 150 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,68 @@
66
# Change Logs:
77
# Date Author Notes
88
# 2025-01-21 kurisaW Initial version
9-
#
9+
# 2025-03-14 hydevcode
1010

1111
# Script Function Description: Assign PR reviews based on the MAINTAINERS list.
1212

1313
name: Auto Review Assistant
1414

1515
on:
16-
pull_request:
16+
pull_request_target:
17+
branches: [ master ]
1718
types: [opened, synchronize, reopened]
18-
workflow_dispatch:
19-
issue_comment:
20-
types: [created]
2119

2220
jobs:
2321
assign-reviewers:
2422
runs-on: ubuntu-22.04
25-
# if: github.repository_owner == 'RT-Thread'
23+
if: github.repository_owner == 'RT-Thread'
2624
permissions:
27-
issues: write
28-
pull-requests: read
25+
issues: read
26+
pull-requests: write
2927
contents: read
3028
steps:
29+
- name: Extract PR number
30+
id: extract-pr
31+
run: |
32+
PR_NUMBER=${{ github.event.pull_request.number }}
33+
echo "PR_NUMBER=${PR_NUMBER}" >> $GITHUB_OUTPUT
3134
- name: Checkout code
32-
uses: actions/checkout@v3
35+
uses: actions/checkout@v4
3336
with:
37+
ref: master
38+
sparse-checkout: MAINTAINERS
3439
persist-credentials: false
3540
- name: Get changed files
3641
id: changed_files
3742
run: |
3843
# 通过 GitHub API 获取 PR 的变更文件列表
3944
changed_files=$(curl -s \
40-
-H "Authorization: Bearer ${{ github.token }}" \
41-
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" | \
45+
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ steps.extract-pr.outputs.PR_NUMBER }}/files" | \
4246
jq -r '.[].filename') # 使用 jq 提取文件名
4347
echo "$changed_files" | grep -v '^MAINTAINERS$' > changed_files.txt
48+
49+
existing_comment=$(curl -s \
50+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments" | \
51+
jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64')
52+
53+
comment_body=""
54+
if [[ ! -z "$existing_comment" ]]; then
55+
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')
56+
57+
comment_time=$(date -d "$comment_body" +%s)
58+
59+
echo "${comment_body}"
60+
echo "COMMENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
61+
else
62+
comment_time=""
63+
echo "COMMENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
64+
fi
65+
echo "COMMENT_TIME=${comment_time}"
4466
- name: Parse MAINTAINERS file
4567
id: parse_maintainer
4668
run: |
4769
# 使用 AWK 解析 MAINTAINERS 文件格式:
48-
# 提取 tag(标签)、path(路径)和 owners维护者 GitHub ID
70+
# 提取 tag(标签)、path(路径)和 owners(维护者 GitHub ID)
4971
awk '
5072
/^tag:/ {
5173
tag = substr($0, index($0, $2)) # 提取标签内容
@@ -63,21 +85,30 @@ jobs:
6385
print tag "|" path "|" github_ids
6486
}
6587
' MAINTAINERS > tag_data.csv
66-
6788
- name: Generate reviewers list
6889
id: generate_reviewers
6990
run: |
7091
# 根据变更文件路径匹配维护者规则
7192
rm -f triggered_reviewers.txt
93+
rm -f triggered_tags.txt
7294
while IFS='|' read -r tag path reviewers; do
7395
# 使用正则匹配路径(支持子目录)
7496
if grep -qE "^$path(/|$)" changed_files.txt; then
7597
echo "$reviewers" | tr ' ' '\n' >> triggered_reviewers.txt
98+
echo "$tag" | tr ' ' '\n' >> triggered_tags.txt
7699
fi
77100
done < tag_data.csv
78-
# 去重处理
79101
awk 'NF && !seen[$0]++' triggered_reviewers.txt > unique_reviewers.txt
80-
102+
awk 'NF && !seen[$0]++' triggered_tags.txt > unique_tags.txt
103+
- name: Restore Reviewers Cache
104+
id: reviewers-cache-restore
105+
if: ${{ steps.changed_files.outputs.COMMENT_TIME != '' }}
106+
uses: actions/cache/restore@v4
107+
with:
108+
path: |
109+
unique_tags_bak.txt
110+
unique_reviewers_bak.txt
111+
key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.changed_files.outputs.COMMENT_TIME }}
81112
- name: Get approval status
82113
id: get_approval
83114
run: |
@@ -86,8 +117,7 @@ jobs:
86117
87118
# 获取 PR 的所有评论
88119
comments=$(curl -s \
89-
-H "Authorization: Bearer ${{ github.token }}" \
90-
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")
120+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments")
91121
92122
echo '#!/bin/bash' > approval_data.sh
93123
echo 'declare -A approvals=()' >> approval_data.sh
@@ -107,24 +137,69 @@ jobs:
107137
chmod +x approval_data.sh
108138
source ./approval_data.sh
109139
140+
jq -r --arg reviewers "$reviewers" '
141+
.[] |
142+
select(.user.login != "github-actions[bot]") | # 排除 bot 的评论
143+
select(.body | test("^\\s*LGTM\\s*$"; "i")) | # 匹配 LGTM 评论(不区分大小写)
144+
.user.login as $user |
145+
"@\($user)" as $mention |
146+
select($mention | inside($reviewers)) | # 过滤有效审查者
147+
"\($mention) \(.created_at)" # 输出审查者和时间
148+
' <<< "$comments" >> approval_data.txt
149+
150+
notified_users=""
151+
if [[ -f unique_reviewers_bak.txt ]]; then
152+
notified_users=$(cat unique_reviewers_bak.txt | xargs)
153+
else
154+
notified_users=""
155+
fi
156+
110157
{
111158
echo "---"
112159
echo "### 📊 Current Review Status (Last Updated: $current_time)"
113160
while read -r reviewer; do
161+
formatted_reviewers=""
162+
for r in $reviewers; do
163+
if [[ " ${notified_users[@]} " =~ " $reviewer " ]]; then
164+
formatted_reviewers+="${reviewer#@}"
165+
else
166+
formatted_reviewers+="$reviewer"
167+
fi
168+
done
169+
114170
if [[ -n "${approvals[$reviewer]}" ]]; then
115171
timestamp=$(date -d "${approvals[$reviewer]}" -u +"%Y-%m-%d %H:%M UTC")
116-
echo "- ✅ **$reviewer** Reviewed On $timestamp"
172+
173+
echo "- ✅ **$formatted_reviewers** Reviewed On $timestamp"
117174
else
118-
echo "- ⌛ **$reviewer** Pending Review"
175+
echo "- ⌛ **$formatted_reviewers** Pending Review"
119176
fi
120177
done < unique_reviewers.txt
121178
} > review_status.md
122-
179+
180+
echo "CURRENT_TIME=${current_time}" >> $GITHUB_OUTPUT
123181
- name: Generate review data
124182
id: generate_review
125183
run: |
184+
unique_tags=""
185+
unique_tags=$(cat unique_tags.txt | xargs)
186+
unique_tags_bak=""
187+
if [[ -f unique_tags_bak.txt ]]; then
188+
unique_tags_bak=$(cat unique_tags_bak.txt | xargs)
189+
fi
190+
191+
existing_tags=""
192+
for r in $unique_tags; do
193+
if [[ " ${unique_tags_bak[@]} " =~ " $r " ]]; then
194+
echo "$r 不存在于数组中"
195+
else
196+
existing_tags+="$r "
197+
fi
198+
done
199+
126200
current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
127201
{
202+
128203
# 生成审查分配信息
129204
echo "## 📌 Code Review Assignment"
130205
echo ""
@@ -133,7 +208,17 @@ jobs:
133208
if grep -qE "^$path(/|$)" changed_files.txt; then
134209
echo "### 🏷️ Tag: $tag"
135210
echo "**Path:** \`$path\` "
136-
echo "**Reviewers:** $reviewers "
211+
212+
if [[ " ${existing_tags[@]} " =~ " $tag " ]]; then
213+
echo "**Reviewers:** $reviewers "
214+
else
215+
formatted_reviewers=""
216+
for r in $reviewers; do
217+
formatted_reviewers+="${r#@} "
218+
done
219+
echo "**Reviewers:** $formatted_reviewers "
220+
fi
221+
137222
echo "<details>"
138223
echo "<summary><b>Changed Files</b> (Click to expand)</summary>"
139224
echo ""
@@ -144,6 +229,8 @@ jobs:
144229
fi
145230
done < tag_data.csv
146231
# 插入审查状态
232+
cat review_status.md
233+
147234
echo "---"
148235
echo "### 📝 Review Instructions"
149236
echo ""
@@ -159,12 +246,47 @@ jobs:
159246
echo "> ℹ️ **刷新CI状态操作需要具备仓库写入权限。**"
160247
echo "> ℹ️ **Refresh CI status operation requires repository Write permission.**"
161248
} > review_data.md
162-
- name: Save PR number
249+
- name: Post/Update comment
250+
id: post_comment
251+
run: |
252+
# 查找现有的 bot 评论
253+
existing_comment=$(curl -s \
254+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
255+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments" | \
256+
jq -r '.[] | select(.user.login == "github-actions[bot]") | {id: .id, body: .body} | @base64')
257+
258+
if [[ -n "$existing_comment" ]]; then
259+
# 更新现有评论
260+
comment_id=$(echo "$existing_comment" | head -1 | base64 -d | jq -r .id)
261+
echo "Updating existing comment $comment_id"
262+
response=$(curl -s -X PATCH \
263+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
264+
-d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
265+
"https://api.github.com/repos/${{ github.repository }}/issues/comments/$comment_id")
266+
else
267+
# 创建新评论
268+
echo "Creating new comment"
269+
response=$(curl -s -X POST \
270+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
271+
-d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
272+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments")
273+
fi
274+
- name: Get Comment Time
275+
id: get_comment_time
163276
run: |
164-
mkdir -p ./pr
165-
echo ${{ github.event.number }} > ./pr/NR
166-
mv /home/runner/work/rt-thread/rt-thread/review_data.md ./pr/
167-
- uses: actions/upload-artifact@v4
277+
existing_comment=$(curl -s \
278+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments" | \
279+
jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64')
280+
comment_body="${{ steps.get_approval.outputs.CURRENT_TIME }}"
281+
comment_time=$(date -d "$comment_body" +%s)
282+
echo "CURRENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
283+
cp unique_reviewers.txt unique_reviewers_bak.txt
284+
cp unique_tags.txt unique_tags_bak.txt
285+
- name: Restore Reviewers Save
286+
id: reviewers-cache-save
287+
uses: actions/cache/save@v4
168288
with:
169-
name: pr
170-
path: pr
289+
path: |
290+
unique_tags_bak.txt
291+
unique_reviewers_bak.txt
292+
key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.get_comment_time.outputs.CURRENT_TIME }}

.github/workflows/auto_comment_pr.yml

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)