Просмотр исходного кода

ci/maintainer: merge same tag with different paths, remove Path display from CI comment

kurisaw 6 месяцев назад
Родитель
Сommit
156259b499
2 измененных файлов с 240 добавлено и 101 удалено
  1. 228 101
      .github/workflows/auto-assign-reviewers.yml
  2. 12 0
      .github/workflows/bsp_buildings.yml

+ 228 - 101
.github/workflows/auto-assign-reviewers.yml

@@ -9,6 +9,7 @@
 # 2025-03-14     hydevcode
 # 2025-05-10     kurisaW      Fixed file existence, cache, and comment time issues
 # 2025-05-11     kurisaW      Fixed missing unique files creation and cache logic
+# 2025-07-14     kurisaW      Merge same tag with different paths, remove Path display from CI comment
 
 # Script Function Description: Assign PR reviews based on the MAINTAINERS list.
 
@@ -44,107 +45,209 @@ jobs:
       - name: Get changed files
         id: changed_files
         run: |
-          # 通过 GitHub API 获取 PR 的变更文件列表
-          changed_files=$(curl -s \
-            "https://api.github.com/repos/${{ github.repository }}/pulls/${{ steps.extract-pr.outputs.PR_NUMBER }}/files" | \
-            jq -r '.[].filename')  # 使用 jq 提取文件名
+          # 通过 GitHub API 获取 PR 的变更文件列表(带重试机制和错误处理)
+          max_retries=3
+          retry_count=0
+          changed_files=""
+          api_response=""
+          
+          echo "Fetching changed files for PR #${{ steps.extract-pr.outputs.PR_NUMBER }}..."
+          
+          while [ $retry_count -lt $max_retries ]; do
+            api_response=$(curl -s \
+              -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
+              -H "Accept: application/vnd.github.v3+json" \
+              "https://api.github.com/repos/${{ github.repository }}/pulls/${{ steps.extract-pr.outputs.PR_NUMBER }}/files")
+            
+            # 验证响应是否为有效JSON且包含文件数组
+            if jq -e 'if type=="array" then .[0].filename else empty end' <<<"$api_response" >/dev/null 2>&1; then
+              changed_files=$(jq -r '.[].filename' <<<"$api_response")
+              break
+            else
+              echo "Retry $((retry_count+1)): API response not ready or invalid format"
+              echo "API Response: $api_response"
+              sleep 5
+              ((retry_count++))
+            fi
+          done
+          
+          if [ -z "$changed_files" ]; then
+            echo "Error: Failed to get changed files after $max_retries attempts"
+            echo "Final API Response: $api_response"
+            exit 1
+          fi
+          
           echo "$changed_files" > changed_files.txt
+          echo "Successfully fetched $(wc -l < changed_files.txt) changed files"
           
+          # 以下是原有的评论处理逻辑(保持不变)
           existing_comment=$(curl -s \
-            "https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments" | \
-            jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64')
-
-          echo "=== Changed Files ==="
-          cat changed_files.txt
-          echo "====================="
-
+            -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
+            "https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments")
+            
+          # Check if response is valid JSON
+          if jq -e . >/dev/null 2>&1 <<<"$existing_comment"; then
+            existing_comment=$(jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64' <<< "$existing_comment")
+          else
+            existing_comment=""
+            echo "Warning: Invalid JSON response from GitHub API for comments"
+            echo "Response: $existing_comment"
+          fi
+ 
           comment_body=""
           if [[ ! -z "$existing_comment" ]]; then
-            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')
-            comment_time=$(date -d "$comment_body" +%s)
-            echo "${comment_body}"
+            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')
+            comment_time=$(TZ='Asia/Shanghai' date -d "$comment_body" +%s)
+            echo "CACHE_TIMESTAMP=${comment_time}" >> $GITHUB_OUTPUT  # 统一使用这个变量名
             echo "COMMENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
           else
             comment_time=""
-            echo "COMMENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
+            echo "CACHE_TIMESTAMP=" >> $GITHUB_OUTPUT
+            echo "COMMENT_TIME=" >> $GITHUB_OUTPUT
           fi
-          echo "COMMENT_TIME=${comment_time}"
+          echo "Debug - CACHE_TIMESTAMP: $comment_time"
 
       - name: Parse MAINTAINERS file
         id: parse_maintainer
         run: |
-          # 使用 AWK 解析 MAINTAINERS 文件格式:
-          # 提取 tag(标签)、path(路径)和 owners(维护者 GitHub ID)
+          set -euo pipefail
           awk '
+            BEGIN{ tag=""; paths=""; owners="" }
             /^tag:/ { 
-              tag = substr($0, index($0, $2))  # 提取标签内容
+                tag = substr($0, index($0, $2)); 
+                paths=""; owners=""
             }
             /^path:/ {
-              # 提取 path 字段并去除前后空格
-              path = substr($0, index($0, $2))
-              gsub(/^[ \t]+|[ \t]+$/, "", path)  # 清理前后空格和制表符
+                path = substr($0, index($0, $2))
+                gsub(/^[ \t]+|[ \t]+$/, "", path)
+                paths = (paths == "" ? path : paths "|" path)
             }
             /^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
+                owners = substr($0, index($0, $2))
+                n = split(owners, parts, /[()]/)
+                github_ids=""
+                for (i=2; i<=n; i+=2) {
+                  id=parts[i]
+                  gsub(/^[ \t@]+|[ \t]+$/, "", id)
+                  if(id != "") github_ids=github_ids "@" id " "
+                }
+                print tag "|" paths "|" github_ids
+                tag=""; paths=""; owners=""
             }
           ' MAINTAINERS > tag_data.csv
 
-      - name: Generate reviewers list
+      - name: Generate reviewers list and tag-file mapping
         id: generate_reviewers
         run: |
-          rm -f triggered_reviewers.txt triggered_tags.txt unique_reviewers.txt unique_tags.txt
+          rm -f triggered_reviewers.txt triggered_tags.txt unique_reviewers.txt unique_tags.txt tag_files_map.json tag_reviewers_map.txt
           touch triggered_reviewers.txt triggered_tags.txt unique_reviewers.txt unique_tags.txt
 
-          while IFS='|' read -r tag path reviewers; do
-            # 转义路径中的正则特殊字符
-            escaped_path=$(sed 's/[.[\*^$]/\\&/g' <<< "$path")
-            
-            # 使用增强型正则匹配路径及其所有子目录
-            if grep -qE "^$escaped_path(/.*)*" changed_files.txt; then
-              echo "$reviewers" | tr -s ' ' '\n' | sed '/^$/d' >> triggered_reviewers.txt
-              echo "$tag" >> triggered_tags.txt
-              echo "Matched: $path → $tag"
-            fi
+          # 1. 读取 tag_data.csv,建立 tag -> [paths], tag -> reviewers
+          declare -A tag_paths_map
+          declare -A tag_reviewers_map
+
+          while IFS='|' read -r tag paths reviewers; do
+            IFS='|' read -ra path_arr <<< "$paths"
+            for p in "${path_arr[@]}"; do
+              tag_paths_map["$tag"]+="$p;"
+            done
+            # 合并 reviewers,去重,只保留合法格式
+            existing_reviewers="${tag_reviewers_map["$tag"]}"
+            all_reviewers="$existing_reviewers $reviewers"
+            # 只保留 @xxx 格式,去重
+            all_reviewers=$(echo "$all_reviewers" | grep -o '@[A-Za-z0-9_-]\+' | sort -u | tr '\n' ' ')
+            tag_reviewers_map["$tag"]="$all_reviewers"
           done < tag_data.csv
 
+          # 2. 针对每个 tag,找出它所有 path 匹配的变更文件
+          declare -A tag_changedfiles_map
+          while IFS= read -r changed; do
+            for tag in "${!tag_paths_map[@]}"; do
+              IFS=';' read -ra tpaths <<< "${tag_paths_map[$tag]}"
+              for tpath in "${tpaths[@]}"; do
+                [[ -z "$tpath" ]] && continue
+                if [[ -f "$tpath" ]]; then
+                  # 精确文件名
+                  [[ "$changed" == "$tpath" ]] && tag_changedfiles_map["$tag"]+="$changed;"
+                else
+                  # 目录前缀
+                  [[ "$changed" == $tpath* ]] && tag_changedfiles_map["$tag"]+="$changed;"
+                fi
+              done
+            done
+          done < changed_files.txt
+
+          # 3. 输出合并后的 tag reviewers、tag、并去重
+          for tag in "${!tag_changedfiles_map[@]}"; do
+            reviewers="${tag_reviewers_map[$tag]}"
+            echo "$reviewers" | tr -s ' ' '\n' | sed '/^$/d' >> triggered_reviewers.txt
+            echo "$tag" >> triggered_tags.txt
+          done
+
           # 生成去重的 unique_reviewers.txt 和 unique_tags.txt
           sort -u triggered_reviewers.txt > unique_reviewers.txt
           sort -u triggered_tags.txt > unique_tags.txt
 
-          # 检查是否有匹配的 reviewers
+          # 4. 输出 tag_files_map.json,格式 { "tag1": ["file1","file2"], ... }
+          {
+            echo "{"
+            first_tag=1
+            for tag in "${!tag_changedfiles_map[@]}"; do
+              [[ $first_tag -eq 0 ]] && echo ","
+              echo -n "  \"${tag}\": ["
+              IFS=';' read -ra files <<< "${tag_changedfiles_map[$tag]}"
+              file_list=""
+              for f in "${files[@]}"; do
+                [[ -z "$f" ]] && continue
+                [[ -n "$file_list" ]] && file_list+=", "
+                file_list+="\"$f\""
+              done
+              echo -n "$file_list"
+              echo -n "]"
+              first_tag=0
+            done
+            echo ""
+            echo "}"
+          } > tag_files_map.json
+
+          # 5. 保存聚合去重后的 reviewers 到 tag_reviewers_map.txt
+          {
+            for tag in "${!tag_reviewers_map[@]}"; do
+              echo "$tag|${tag_reviewers_map[$tag]}"
+            done
+          } > tag_reviewers_map.txt
+
+          # 6. 标记是否有 reviewer
           if [[ -s unique_reviewers.txt ]]; then
             echo "HAS_REVIEWERS=true" >> $GITHUB_OUTPUT
           else
             echo "HAS_REVIEWERS=false" >> $GITHUB_OUTPUT
           fi
 
-          echo "=== Matched Paths ==="
+          echo "=== Matched Tags ==="
           cat unique_tags.txt
           echo "=== Matched Reviewers ==="
           cat unique_reviewers.txt
+          echo "=== Tag-ChangedFiles Map ==="
+          cat tag_files_map.json
 
       - name: Restore Reviewers Cache
-        id: reviewers-cache-restore 
-        if: ${{ steps.changed_files.outputs.COMMENT_TIME != '' }}
+        id: reviewers-cache-restore
+        if: ${{ steps.changed_files.outputs.CACHE_TIMESTAMP != '' }}
         uses: actions/cache/restore@v4
-        with: 
+        with:
           path: |
             unique_tags_bak.txt
             unique_reviewers_bak.txt
-          key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.changed_files.outputs.COMMENT_TIME }}
+          key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.changed_files.outputs.CACHE_TIMESTAMP }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.changed_files.outputs.CACHE_TIMESTAMP }}-
+            ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-
 
       - name: Get approval status
         id: get_approval
         run: |
-          current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
-          
-          # 检查 unique_reviewers.txt 是否存在且非空
+          current_time=$(TZ='Asia/Shanghai' date +"%Y-%m-%d %H:%M CST")
           if [[ ! -s unique_reviewers.txt ]]; then
             echo "No reviewers found, creating empty unique_reviewers.txt"
             touch unique_reviewers.txt
@@ -183,14 +286,12 @@ jobs:
             select($mention | inside($reviewers)) |         # 过滤有效审查者
             "\($mention) \(.created_at)"                   # 输出审查者和时间
           ' <<< "$comments" >> approval_data.txt
-
           notified_users=""
           if [[ -f unique_reviewers_bak.txt ]]; then
             notified_users=$(cat unique_reviewers_bak.txt | xargs)
           else
             notified_users=""
           fi
-
           {
             echo "---"
             echo "### 📊 Current Review Status (Last Updated: $current_time)"
@@ -198,24 +299,22 @@ jobs:
               formatted_reviewers=""
               for r in $reviewers; do
                 if [[ " ${notified_users[@]} " =~ " $reviewer " ]]; then
-                    formatted_reviewers+="${reviewer#@}"
+                  formatted_reviewers+="${reviewer#@}"
                 else
-                    formatted_reviewers+="$reviewer"
+                  formatted_reviewers+="$reviewer"
                 fi
               done
-            
               if [[ -n "${approvals[$reviewer]}" ]]; then
-                timestamp=$(date -d "${approvals[$reviewer]}" -u +"%Y-%m-%d %H:%M UTC")
+                timestamp=$(TZ='Asia/Shanghai' date -d "${approvals[$reviewer]}" +"%Y-%m-%d %H:%M CST")
                 echo "- ✅ **$formatted_reviewers** Reviewed On $timestamp"
               else
                 echo "- ⌛ **$formatted_reviewers** Pending Review"
               fi
             done < unique_reviewers.txt
           } > review_status.md
-          
           echo "CURRENT_TIME=${current_time}" >> $GITHUB_OUTPUT
 
-      - name: Generate review data
+      - name: Generate review data (tag merge, no path in comment, changed files summary per tag)
         id: generate_review
         if: steps.generate_reviewers.outputs.HAS_REVIEWERS == 'true'
         run: |
@@ -227,49 +326,55 @@ jobs:
           if [[ -f unique_tags_bak.txt ]]; then
             unique_tags_bak=$(cat unique_tags_bak.txt | xargs)
           fi
-
-          existing_tags=""
-          for r in $unique_tags; do
-            if [[ " ${unique_tags_bak[@]} " =~ " $r " ]]; then
-              echo "$r 不存在于数组中"
-            else
-              existing_tags+="$r "
-            fi
-          done
-
-          current_time=$(date -u +"%Y-%m-%d %H:%M UTC")
+          # 读取 tag->files 映射
+          declare -A tag_files_map
+          eval "$(jq -r 'to_entries[] | "tag_files_map[\"\(.key)\"]=\"\(.value | join(";"))\"" ' tag_files_map.json)"
+          # 读取 tag->reviewers(只读聚合去重后的结果)
+          declare -A tag_reviewers_map
+          while IFS='|' read -r tag reviewers; do
+            tag_reviewers_map["$tag"]="$reviewers"
+          done < tag_reviewers_map.txt
+          # 获取已通知的 reviewers
+          notified_users=""
+          if [[ -f unique_reviewers_bak.txt ]]; then
+            notified_users=$(cat unique_reviewers_bak.txt | xargs)
+          fi
+          current_time=$(TZ='Asia/Shanghai' date +"%Y-%m-%d %H:%M CST")
           {
-            # 生成审查分配信息
             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\`  "
-
-                if [[ " ${existing_tags[@]} " =~ " $tag " ]]; then
-                  echo "**Reviewers:** $reviewers  "
+            for tag in $unique_tags; do
+              reviewers="${tag_reviewers_map[$tag]}"
+              # 移除尾部空格并提取有效的@username格式
+              reviewers=$(echo "$reviewers" | sed 's/[[:space:]]*$//' | grep -o '@[A-Za-z0-9_-]\+' | sort -u | tr '\n' ' ')
+              
+              # 格式化reviewers显示(仅对已通知用户去掉@)
+              formatted_reviewers=""
+              for reviewer in $reviewers; do
+                if [[ " ${notified_users[@]} " =~ " $reviewer " ]]; then
+                  formatted_reviewers+="${reviewer#@} "  # 已通知用户去掉@
                 else
-                  formatted_reviewers=""
-                  for r in $reviewers; do
-                    formatted_reviewers+="${r#@} "
-                  done
-                  echo "**Reviewers:** $formatted_reviewers  "
+                  formatted_reviewers+="$reviewer "      # 未通知用户保留@
                 fi
-
-                echo "<details>"
-                echo "<summary><b>Changed Files</b> (Click to expand)</summary>"
-                echo ""
-                grep -E "^$path(/|$)" changed_files.txt | sed 's/^/- /'  # 列出匹配的变更文件
-                echo ""
-                echo "</details>"
-                echo ""
-              fi
-            done < tag_data.csv
+              done
+              
+              echo "### 🏷️ Tag: $tag"
+              echo ""
+              echo "**Reviewers:** $formatted_reviewers"  # 确保显示Reviewers
+              echo "<details>"
+              echo "<summary><b>Changed Files</b> (Click to expand)</summary>"
+              echo ""
+              IFS=';' read -ra files <<< "${tag_files_map[$tag]}"
+              for file in "${files[@]}"; do
+                [[ -z "$file" ]] && continue
+                echo "- $file"
+              done
+              echo ""
+              echo "</details>"
+              echo ""
+            done
             # 插入审查状态
             cat review_status.md
-
             echo "---"
             echo "### 📝 Review Instructions"
             echo ""
@@ -318,20 +423,42 @@ jobs:
         if: steps.generate_reviewers.outputs.HAS_REVIEWERS == 'true'
         run: |
           existing_comment=$(curl -s \
-            "https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments" | \
-            jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64')
+            -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
+            "https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.extract-pr.outputs.PR_NUMBER }}/comments")
+            
+          # Check if response is valid JSON
+          if jq -e . >/dev/null 2>&1 <<<"$existing_comment"; then
+            existing_comment=$(jq -r '.[] | select(.user.login == "github-actions[bot]") | {body: .body} | @base64' <<< "$existing_comment")
+          else
+            existing_comment=""
+            echo "Warning: Invalid JSON response from GitHub API"
+            echo "Response: $existing_comment"
+          fi
           comment_body="${{ steps.get_approval.outputs.CURRENT_TIME }}"
-          comment_time=$(date -d "$comment_body" +%s)
-          echo "CURRENT_TIME=${comment_time}" >> $GITHUB_OUTPUT
-          cp unique_reviewers.txt unique_reviewers_bak.txt
-          cp unique_tags.txt unique_tags_bak.txt
+          comment_time=$(TZ='Asia/Shanghai' date -d "$comment_body" +%s)
+          echo "CACHE_TIMESTAMP=${comment_time}" >> $GITHUB_OUTPUT  # 统一使用这个变量名
+          echo "Debug - Saving cache with timestamp: $comment_time"
+          
+          mkdir -p $(dirname unique_reviewers_bak.txt)
+          if [[ -s unique_reviewers.txt ]]; then
+            cp unique_reviewers.txt unique_reviewers_bak.txt
+          else
+            touch unique_reviewers_bak.txt
+          fi
+          
+          if [[ -s unique_tags.txt ]]; then
+            cp unique_tags.txt unique_tags_bak.txt
+          else
+            touch unique_tags_bak.txt
+          fi
 
       - name: Save Reviewers Cache
         id: reviewers-cache-save
         if: steps.generate_reviewers.outputs.HAS_REVIEWERS == 'true'
+        continue-on-error: true
         uses: actions/cache/save@v4
         with:
           path: |
             unique_tags_bak.txt
             unique_reviewers_bak.txt
-          key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.get_comment_time.outputs.CURRENT_TIME }}
+          key: ${{ runner.os }}-auto-assign-reviewers-${{ steps.extract-pr.outputs.PR_NUMBER }}-${{ steps.get_comment_time.outputs.CACHE_TIMESTAMP }}-${{ github.run_id }}

+ 12 - 0
.github/workflows/bsp_buildings.yml

@@ -20,16 +20,28 @@ on:
     branches:
       - master
     paths-ignore:
+      - .github/**
+      - .gitee/**
+      - .hook/**
       - documentation/**
+      - examples/**
       - '**/README.md'
       - '**/README_zh.md'
+      - ChangeLog.md
+      - MAINTAINERS
   pull_request:
     branches:
       - master
     paths-ignore:
+      - .github/**
+      - .gitee/**
+      - .hook/**
       - documentation/**
+      - examples/**
       - '**/README.md'
       - '**/README_zh.md'
+      - ChangeLog.md
+      - MAINTAINERS
   repository_dispatch:
     types:
       - online-pkgs-static-building-trigger-event