From 294d6208310b8e43271af361eadf1d5a4e9aa257 Mon Sep 17 00:00:00 2001
From: kurisaw <2053731441@qq.com>
Date: Sat, 8 Feb 2025 15:27:56 +0800
Subject: [PATCH] feat: CI script assigns PR reviews based on the list of
maintainers
---
.github/workflows/auto-assign-reviewers.yml | 190 ++++++++++++++++++++
MAINTAINERS | 57 ++++++
2 files changed, 247 insertions(+)
create mode 100644 .github/workflows/auto-assign-reviewers.yml
create mode 100644 MAINTAINERS
diff --git a/.github/workflows/auto-assign-reviewers.yml b/.github/workflows/auto-assign-reviewers.yml
new file mode 100644
index 00000000000..8678f326e7f
--- /dev/null
+++ b/.github/workflows/auto-assign-reviewers.yml
@@ -0,0 +1,190 @@
+#
+# Copyright (c) 2006-2025, RT-Thread Development Team
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Change Logs:
+# Date Author Notes
+# 2025-01-21 kurisaW Initial version
+#
+
+# Script Function Description: Assign PR reviews based on the MAINTAINERS list.
+
+name: Auto Review Assistant
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened]
+ workflow_dispatch:
+ issue_comment:
+ types: [created]
+
+jobs:
+ assign-reviewers:
+ runs-on: ubuntu-22.04
+ if: github.repository_owner == 'RT-Thread'
+ permissions:
+ issues: write
+ pull-requests: write
+ contents: read
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Get changed files
+ id: changed_files
+ run: |
+ # 通过 GitHub API 获取 PR 的变更文件列表
+ changed_files=$(curl -s \
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
+ "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" | \
+ jq -r '.[].filename') # 使用 jq 提取文件名
+
+ echo "$changed_files" | grep -v '^MAINTAINERS$' > changed_files.txt
+
+ - name: Parse MAINTAINERS file
+ id: parse_maintainer
+ run: |
+ # 使用 AWK 解析 MAINTAINERS 文件格式:
+ # 提取 tag(标签)、path(路径)和 owners(维护者 GitHub ID)
+ awk '
+ /^tag:/ {
+ tag = substr($0, index($0, $2)) # 提取标签内容
+ }
+ /^path:/ {
+ path = substr($0, index($0, $2)) # 提取路径内容
+ }
+ /^owners:/ {
+ owners = substr($0, index($0, $2)) # 提取维护者信息
+ split(owners, parts, /[()]/) # 拆分出 GitHub ID(括号内内容)
+ github_ids = ""
+ for (i=2; i<=length(parts); i+=2) {
+ github_ids = github_ids "@" parts[i] " " # 拼接为 @user 格式
+ }
+ print tag "|" path "|" github_ids
+ }
+ ' MAINTAINERS > tag_data.csv
+
+ - name: Generate reviewers list
+ id: generate_reviewers
+ run: |
+ # 根据变更文件路径匹配维护者规则
+ rm -f triggered_reviewers.txt
+ while IFS='|' read -r tag path reviewers; do
+ # 使用正则匹配路径(支持子目录)
+ if grep -qE "^$path(/|$)" changed_files.txt; then
+ echo "$reviewers" | tr ' ' '\n' >> triggered_reviewers.txt
+ fi
+ done < tag_data.csv
+ # 去重处理
+ awk 'NF && !seen[$0]++' triggered_reviewers.txt > unique_reviewers.txt
+
+ - name: Get approval status
+ id: get_approval
+ run: |
+ current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
+ reviewers=$(cat unique_reviewers.txt | tr '\n' '|')
+
+ # 获取 PR 的所有评论
+ comments=$(curl -s \
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
+ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")
+
+ echo '#!/bin/bash' > approval_data.sh
+ echo 'declare -A approvals=()' >> approval_data.sh
+
+ # 使用 jq 解析包含 LGTM 的有效评论
+ jq -r --arg reviewers "$reviewers" '
+ .[] |
+ select(.user.login != "github-actions[bot]") | # 排除 bot 的评论
+ select(.body | test("^\\s*LGTM\\s*$"; "i")) | # 匹配 LGTM 评论(不区分大小写)
+ .user.login as $user |
+ "@\($user)" as $mention |
+ select($mention | inside($reviewers)) | # 过滤有效审查者
+ "approvals[\"\($mention)\"]=\"\(.created_at)\"" # 记录审批时间
+ ' <<< "$comments" >> approval_data.sh
+
+ # 加载审查数据并生成状态报告
+ chmod +x approval_data.sh
+ source ./approval_data.sh
+
+ {
+ echo "---"
+ echo "### 📊 Current Review Status (Last Updated: $current_time)"
+ while read -r reviewer; do
+ if [[ -n "${approvals[$reviewer]}" ]]; then
+ timestamp=$(date -d "${approvals[$reviewer]}" -u +"%Y-%m-%d %H:%M UTC")
+ echo "- ✅ **$reviewer** Reviewed On $timestamp"
+ else
+ echo "- ⌛ **$reviewer** Pending Review"
+ fi
+ done < unique_reviewers.txt
+ } > review_status.md
+
+ - name: Generate review data
+ id: generate_review
+ run: |
+ current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
+ {
+ # 生成审查分配信息
+ echo "## 📌 Code Review Assignment"
+ echo ""
+
+ while IFS='|' read -r tag path reviewers; do
+ if grep -qE "^$path(/|$)" changed_files.txt; then
+ echo "### 🏷️ Tag: $tag"
+ echo "**Path:** \`$path\` "
+ echo "**Reviewers:** $reviewers "
+ echo ""
+ echo "Changed Files (Click to expand)
"
+ echo ""
+ grep -E "^$path(/|$)" changed_files.txt | sed 's/^/- /' # 列出匹配的变更文件
+ echo ""
+ echo " "
+ echo ""
+ fi
+ done < tag_data.csv
+ # 插入审查状态
+ cat review_status.md
+
+ echo "---"
+ echo "### 📝 Review Instructions"
+ echo ""
+ echo "1. **维护者可以通过单击此处来刷新审查状态:** [🔄 刷新状态](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
+ echo " **Maintainers can refresh the review status by clicking here:** [🔄 Refresh Status](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
+ echo ""
+ echo "2. **确认审核通过后评论 \`LGTM/lgtm\`**"
+ echo " **Comment \`LGTM/lgtm\` after confirming approval**"
+ echo ""
+ echo "3. **PR合并前需至少一位维护者确认**"
+ echo " **PR must be confirmed by at least one maintainer before merging**"
+ echo ""
+ echo "> ℹ️ **刷新CI状态操作需要具备仓库写入权限。**"
+ echo "> ℹ️ **Refresh CI status operation requires repository Write permission.**"
+ } > review_data.md
+
+ - name: Post/Update comment
+ id: post_comment
+ run: |
+ # 查找现有的 bot 评论
+ existing_comment=$(curl -s \
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
+ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" | \
+ jq -r '.[] | select(.user.login == "github-actions[bot]") | {id: .id, body: .body} | @base64')
+
+ if [[ -n "$existing_comment" ]]; then
+ # 更新现有评论
+ comment_id=$(echo "$existing_comment" | head -1 | base64 -d | jq -r .id)
+ echo "Updating existing comment $comment_id"
+ response=$(curl -s -X PATCH \
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
+ -d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
+ "https://api.github.com/repos/${{ github.repository }}/issues/comments/$comment_id")
+ else
+ # 创建新评论
+ echo "Creating new comment"
+ response=$(curl -s -X POST \
+ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN_AUTO_REVIEW }}" \
+ -d "$(jq -n --arg body "$(cat review_data.md)" '{body: $body}')" \
+ "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments")
+ fi
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 00000000000..d71b73fcf7d
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,57 @@
+# List of maintainers of the RT-Thread open-source community
+#
+# This file manages the maintainers and their associated sections in the repository.
+# Developers should update this file according to project needs.
+# The following are explanations of each field and guidelines for adding new maintainer entries.
+#
+# When adding new entries, please follow the format:
+#
+# 1. **tag** - Assign a unique tag to each entry for identifying the code module or functionality.
+# - The tag should be concise and descriptive, such as `workflow`, `libc`...
+# - **Rule for Adding**: Use a new tag when adding a new functionality or module to ensure it clearly describes the area of responsibility.
+#
+# 2. **path** - Specify the directory or file path that the maintainer is responsible for.
+# - The path must be relative to the repository's root directory and can refer to either a single file or a folder.
+# - 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.
+# - **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.
+#
+# 3. **owners** - List the maintainers responsible for the section, including their GitHub usernames and contact information.
+# - The owners should be listed as a comma-separated list if there are multiple maintainers.
+# - Format: `Name(GitHub username)`.
+# - **Rule for Adding**: Ensure that the listed GitHub usernames are correct, and the maintainers are aware of their responsibilities and duties.
+#
+# Example: How to Add a Maintainer Entry
+#
+# The following is a template for adding new entries in the MAINTAINER file:
+#
+# tag:
+# path:
+# owners: , , ...
+#
+# When adding entries, please follow these guidelines:
+# - Ensure the `tag` is unique and descriptive.
+# - Ensure the `path` points to the correct location in the repository.
+# - Ensure the `owners` are accurate and that all new maintainers are aware of their responsibilities.
+#
+# Example Entry:
+# tag: example-module
+# path: example/module/path
+# owners: John Doe(johndoe), Jane Smith(janesmith)
+
+# Note:
+# - 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.
+# - 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.
+
+# Below are existing maintainer entries, divided by module:
+
+tag: workflow
+path: .github
+owners: Li Tao(supperthomas)<78900636@qq.com>
+
+tag: stm32f407-rt-spark
+path: bsp/stm32/stm32f407-rt-spark
+owners: Zhang Bingru(Rbb666)<751061401@qq.com>, Wang Yuqiang(kurisaW)<2053731441@qq.com>
+
+tag: libc
+path: components/libc
+owners: Man Jiantin(mysterywolf)<920369182@qq.com>