Skip to content

Commit d63b60e

Browse files
committed
feat: CI script assigns PR reviews based on the list of maintainers
1 parent 3436aa6 commit d63b60e

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed
+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
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:/ {
49+
tag = substr($0, index($0, $2))
50+
}
51+
/^path:/ {
52+
path = substr($0, index($0, $2))
53+
}
54+
/^owners:/ {
55+
owners = substr($0, index($0, $2))
56+
split(owners, parts, /[()]/)
57+
github_ids = ""
58+
for (i=2; i<=length(parts); i+=2) {
59+
github_ids = github_ids "@" parts[i] " "
60+
}
61+
print tag "|" path "|" github_ids
62+
}
63+
' MAINTAINERS > tag_data.csv
64+
65+
- name: Generate reviewers list
66+
id: generate_reviewers
67+
run: |
68+
rm -f triggered_reviewers.txt
69+
while IFS='|' read -r tag path reviewers; do
70+
if grep -qE "^$path(/|$)" changed_files.txt; then
71+
echo "$reviewers" | tr ' ' '\n' >> triggered_reviewers.txt
72+
fi
73+
done < tag_data.csv
74+
awk 'NF && !seen[$0]++' triggered_reviewers.txt > unique_reviewers.txt
75+
76+
- name: Get approval status
77+
id: get_approval
78+
run: |
79+
current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
80+
reviewers=$(cat unique_reviewers.txt | tr '\n' '|')
81+
82+
comments=$(curl -s \
83+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
84+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")
85+
86+
echo '#!/bin/bash' > approval_data.sh
87+
echo 'declare -A approvals=()' >> approval_data.sh
88+
89+
jq -r --arg reviewers "$reviewers" '
90+
.[] |
91+
select(.user.login != "github-actions[bot]") |
92+
select(.body | test("^\\s*LGTM\\s*$"; "i")) |
93+
.user.login as $user |
94+
"@\($user)" as $mention |
95+
select($mention | inside($reviewers)) |
96+
"approvals[\"\($mention)\"]=\"\(.created_at)\""
97+
' <<< "$comments" >> approval_data.sh
98+
99+
chmod +x approval_data.sh
100+
source ./approval_data.sh
101+
102+
{
103+
echo "---"
104+
echo "### 📊 Current Review Status (Last Updated: $current_time)"
105+
while read -r reviewer; do
106+
if [[ -n "${approvals[$reviewer]}" ]]; then
107+
timestamp=$(date -d "${approvals[$reviewer]}" -u +"%Y-%m-%d %H:%M UTC")
108+
echo "- ✅ **$reviewer** Reviewed On $timestamp"
109+
else
110+
echo "- ⌛ **$reviewer** Pending Review"
111+
fi
112+
done < unique_reviewers.txt
113+
} > review_status.md
114+
115+
- name: Generate review data
116+
id: generate_review
117+
run: |
118+
current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
119+
{
120+
echo "## 📌 Code Review Assignment"
121+
echo ""
122+
123+
while IFS='|' read -r tag path reviewers; do
124+
if grep -qE "^$path(/|$)" changed_files.txt; then
125+
echo "### 🏷️ Tag: $tag"
126+
echo "**Path:** \`$path\` "
127+
echo "**Reviewers:** $reviewers "
128+
echo "<details>"
129+
echo "<summary><b>Changed Files</b> (Click to expand)</summary>"
130+
echo ""
131+
grep -E "^$path(/|$)" changed_files.txt | sed 's/^/- /'
132+
echo ""
133+
echo "</details>"
134+
echo ""
135+
fi
136+
done < tag_data.csv
137+
138+
cat review_status.md
139+
140+
echo "---"
141+
echo "### 📝 Review Instructions"
142+
echo ""
143+
echo "1. **维护者可以通过单击此处来刷新审查状态:** [🔄 刷新状态](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
144+
echo " **Maintainers can refresh the review status by clicking here:** [🔄 Refresh Status](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
145+
echo ""
146+
echo "2. **确认审核通过后评论 \`LGTM/lgtm\`**"
147+
echo " **Comment \`LGTM/lgtm\` after confirming approval**"
148+
echo ""
149+
echo "3. **PR合并前需至少一位维护者确认**"
150+
echo " **PR must be confirmed by at least one maintainer before merging**"
151+
echo ""
152+
echo "> ℹ️ **刷新CI状态操作需要具备仓库写入权限。**"
153+
echo "> ℹ️ **Refresh CI status operation requires repository Write permission.**"
154+
} > review_data.md
155+
156+
- name: Post/Update comment
157+
id: post_comment
158+
run: |
159+
existing_comment=$(curl -s \
160+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
161+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" | \
162+
jq -r '.[] | select(.user.login == "github-actions[bot]") | {id: .id, body: .body} | @base64')
163+
164+
if [[ -n "$existing_comment" ]]; then
165+
comment_id=$(echo "$existing_comment" | head -1 | base64 -d | jq -r .id)
166+
echo "Updating existing comment $comment_id"
167+
response=$(curl -s -X PATCH \
168+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
169+
-d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
170+
"https://api.github.com/repos/${{ github.repository }}/issues/comments/$comment_id")
171+
else
172+
echo "Creating new comment"
173+
response=$(curl -s -X POST \
174+
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
175+
-d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
176+
"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")
177+
fi

MAINTAINERS

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# List of maintainers of the RT-Thread open-source community
2+
#
3+
# This file manages the maintainers and their associated sections in the repository.
4+
# Developers should update this file according to project needs.
5+
# The following are explanations of each field and guidelines for adding new maintainer entries.
6+
#
7+
# When adding new entries, please follow the format:
8+
#
9+
# 1. **tag** - Assign a unique tag to each entry for identifying the code module or functionality.
10+
# - The tag should be concise and descriptive, such as `workflow`, `libc`...
11+
# - **Rule for Adding**: Use a new tag when adding a new functionality or module to ensure it clearly describes the area of responsibility.
12+
#
13+
# 2. **path** - Specify the directory or file path that the maintainer is responsible for.
14+
# - The path must be relative to the repository's root directory and can refer to either a single file or a folder.
15+
# - If the maintainer is responsible for all files in a directory, use the directory path; if it's for a specific file, provide the full file path.
16+
# - **Rule for Adding**: Ensure that the path correctly points to the relevant code location. Please note that a tag should correspond to only one path. Currently, multiple paths are not supported.
17+
#
18+
# 3. **owners** - List the maintainers responsible for the section, including their GitHub usernames and contact information.
19+
# - The owners should be listed as a comma-separated list if there are multiple maintainers.
20+
# - Format: `Name(GitHub username)<email address>`.
21+
# - **Rule for Adding**: Ensure that the listed GitHub usernames are correct, and the maintainers are aware of their responsibilities and duties.
22+
#
23+
# Example: How to Add a Maintainer Entry
24+
#
25+
# The following is a template for adding new entries in the MAINTAINER file:
26+
#
27+
# tag: <module-name>
28+
# path: <file-or-directory-path>
29+
# owners: <maintainer1>, <maintainer2>, ...
30+
#
31+
# When adding entries, please follow these guidelines:
32+
# - Ensure the `tag` is unique and descriptive.
33+
# - Ensure the `path` points to the correct location in the repository.
34+
# - Ensure the `owners` are accurate and that all new maintainers are aware of their responsibilities.
35+
#
36+
# Example Entry:
37+
# tag: example-module
38+
# path: example/module/path
39+
# owners: John Doe(johndoe)<[email protected]>, Jane Smith(janesmith)<[email protected]>
40+
41+
# Note:
42+
# - Each entry includes a `tag` that identifies the module or functionality, a `path` that points to the relevant code location, and `owners` who are the maintainers for that part of the codebase.
43+
# - If there are multiple entries, each entry should be separated by a blank line. Within a single entry, there is no need to insert blank lines between the tag, path, and owners.
44+
45+
# Below are existing maintainer entries, divided by module:
46+
47+
tag: workflow
48+
path: .github
49+
owners: Li Tao(supperthomas)<[email protected]>
50+
51+
tag: stm32f407-rt-spark
52+
path: bsp/stm32/stm32f407-rt-spark
53+
owners: Zhang Bingru(Rbb666)<[email protected]>, Wang Yuqiang(kurisaW)<[email protected]>
54+
55+
tag: libc
56+
path: components/libc
57+
owners: Man Jiantin(mysterywolf)<[email protected]>

0 commit comments

Comments
 (0)