1+ #
2+ # Copyright (c) 2006-2025, RT-Thread Development Team
3+ #
4+ # SPDX-License-Identifier: Apache-2.0
5+ #
6+ # Change Logs:
7+ # Date Author Notes
8+ # 2025-01-21 kurisaW Initial version
9+ #
10+
11+ # Script Function Description: Assign PR reviews based on the MAINTAINERS list.
12+
13+ name : Auto Review Assistant
14+
15+ on :
16+ pull_request :
17+ types : [opened, synchronize, reopened]
18+ workflow_dispatch :
19+ issue_comment :
20+ types : [created]
21+
22+ jobs :
23+ assign-reviewers :
24+ runs-on : ubuntu-22.04
25+ if : github.repository_owner == 'RT-Thread'
26+ permissions :
27+ issues : write
28+ pull-requests : write
29+ contents : read
30+ steps :
31+ - name : Checkout code
32+ uses : actions/checkout@v3
33+
34+ - name : Get changed files
35+ id : changed_files
36+ run : |
37+ changed_files=$(curl -s \
38+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
39+ "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" | \
40+ jq -r '.[].filename')
41+
42+ echo "$changed_files" | grep -v '^MAINTAINERS$' > changed_files.txt
43+
44+ - name : Parse MAINTAINERS file
45+ id : parse_maintainer
46+ run : |
47+ awk '
48+ /^tag:/ { tag=$2 }
49+ /^path:/ { path=$2 }
50+ /^owners:/ {
51+ split($0, parts, /[()]/)
52+ github_ids = ""
53+ for (i=2; i<=length(parts); i+=2) {
54+ github_ids = github_ids "@" parts[i] " "
55+ }
56+ print tag "|" path "|" github_ids
57+ }
58+ ' MAINTAINERS > tag_data.csv
59+
60+ - name : Generate reviewers list
61+ id : generate_reviewers
62+ run : |
63+ rm -f triggered_reviewers.txt
64+ while IFS='|' read -r tag path reviewers; do
65+ if grep -qE "^$path(/|$)" changed_files.txt; then
66+ echo "$reviewers" | tr ' ' '\n' >> triggered_reviewers.txt
67+ fi
68+ done < tag_data.csv
69+ awk 'NF && !seen[$0]++' triggered_reviewers.txt > unique_reviewers.txt
70+
71+ - name : Get approval status
72+ id : get_approval
73+ run : |
74+ current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
75+ reviewers=$(cat unique_reviewers.txt | tr '\n' '|')
76+
77+ comments=$(curl -s \
78+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
79+ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")
80+
81+ echo '#!/bin/bash' > approval_data.sh
82+ echo 'declare -A approvals=()' >> approval_data.sh
83+
84+ jq -r --arg reviewers "$reviewers" '
85+ .[] |
86+ select(.user.login != "github-actions[bot]") |
87+ select(.body | test("^\\s*LGTM\\s*$"; "i")) |
88+ .user.login as $user |
89+ "@\($user)" as $mention |
90+ select($mention | inside($reviewers)) |
91+ "approvals[\"\($mention)\"]=\"\(.created_at)\""
92+ ' <<< "$comments" >> approval_data.sh
93+
94+ chmod +x approval_data.sh
95+ source ./approval_data.sh
96+
97+ {
98+ echo "---"
99+ echo "### 📊 Current Review Status (Last Updated: $current_time)"
100+ while read -r reviewer; do
101+ if [[ -n "${approvals[$reviewer]}" ]]; then
102+ timestamp=$(date -d "${approvals[$reviewer]}" -u +"%Y-%m-%d %H:%M UTC")
103+ echo "- ✅ **$reviewer** Reviewed On $timestamp"
104+ else
105+ echo "- ⌛ **$reviewer** Pending Review"
106+ fi
107+ done < unique_reviewers.txt
108+ } > review_status.md
109+
110+ - name : Generate review data
111+ id : generate_review
112+ run : |
113+ current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
114+ {
115+ echo "## 📌 Code Review Assignment"
116+ echo ""
117+
118+ while IFS='|' read -r tag path reviewers; do
119+ if grep -qE "^$path(/|$)" changed_files.txt; then
120+ echo "### 🏷️ Tag: $tag"
121+ echo "**Path:** \`$path\` "
122+ echo "**Reviewers:** $reviewers "
123+ echo "<details>"
124+ echo "<summary><b>Changed Files</b> (Click to expand)</summary>"
125+ echo ""
126+ grep -E "^$path(/|$)" changed_files.txt | sed 's/^/- /'
127+ echo ""
128+ echo "</details>"
129+ echo ""
130+ fi
131+ done < tag_data.csv
132+
133+ cat review_status.md
134+
135+ echo "---"
136+ echo "### 📝 Review Instructions"
137+ echo ""
138+ echo "1. **维护者可以通过单击此处来刷新审查状态:** [🔄 刷新状态](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/rerun"
139+ echo " **Maintainers can refresh the review status by clicking here:** [🔄 Refresh Status](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/rerun"
140+ echo ""
141+ echo "2. **确认审核通过后评论 \`LGTM/lgtm\`**"
142+ echo " **Comment \`LGTM/lgtm\` after confirming approval**"
143+ echo ""
144+ echo "3. **PR合并前需至少一位维护者确认**"
145+ echo " **PR must be confirmed by at least one maintainer before merging**"
146+ echo ""
147+ echo "> ℹ️ **刷新CI状态操作需要具备仓库写入权限。**"
148+ echo "> ℹ️ **Refresh CI status operation requires repository Write permission.**"
149+ } > review_data.md
150+
151+ - name : Post/Update comment
152+ id : post_comment
153+ run : |
154+ existing_comment=$(curl -s \
155+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
156+ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" | \
157+ jq -r '.[] | select(.user.login == "github-actions[bot]") | {id: .id, body: .body} | @base64')
158+
159+ if [[ -n "$existing_comment" ]]; then
160+ comment_id=$(echo "$existing_comment" | head -1 | base64 -d | jq -r .id)
161+ echo "Updating existing comment $comment_id"
162+ response=$(curl -s -X PATCH \
163+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
164+ -d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
165+ "https://api.github.com/repos/${{ github.repository }}/issues/comments/$comment_id")
166+ else
167+ echo "Creating new comment"
168+ response=$(curl -s -X POST \
169+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
170+ -d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
171+ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")
172+ fi
0 commit comments