diff --git a/.github/workflows/pr-approve-status.yml b/.github/workflows/pr-approve-status.yml index ccb418ebb72e9d..fe0a0161607cff 100644 --- a/.github/workflows/pr-approve-status.yml +++ b/.github/workflows/pr-approve-status.yml @@ -19,42 +19,25 @@ name: Need_2_Approval on: pull_request_review: - types: [submitted] + types: [ submitted ] + jobs: Need_2_Approval: runs-on: ubuntu-latest - timeout-minutes: 3 + timeout-minutes: 5 steps: - uses: actions/checkout@v3 - - run: | - pr_num=${{ github.event.pull_request.number }} - echo $pr_num - if [ -z "$pr_num" ]; then - echo "PR number is not set" - exit 1 - fi - response=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }} " "https://api.github.com/repos/apache/doris/pulls/${pr_num}/reviews?per_page=100") - # shellcheck disable=SC2207 - reviewers=($(echo $response | jq -r '.[] | .user.login')) - # shellcheck disable=SC2207 - statuses=($(echo $response | jq -r '.[] | .state')) - echo "${reviewers[@]}" - echo "${statuses[@]}" - approves=() - reviewers_unique=() - for ((i=${#reviewers[@]}-1;i>=0;i--)); do - if ! echo "${reviewers_unique[@]}" | grep -q -w "${reviewers[$i]}" && [ "${statuses[$i]}" != "COMMENTED" ]; then - reviewers_unique+=( "${reviewers[$i]}" ) - if [ "${statuses[$i]}" == "APPROVED" ]; then - approves+=( "${reviewers[$i]}" ) - fi - fi - done - echo "${approves[@]}" - if [ ${#approves[@]} -lt 2 ]; then - echo "PR ${pr_num} has not been approved by at least 2 reviewers" - # shellcheck disable=SC2242 - exit 1 - fi - echo "Thanks for your contribution to Doris." - exit 0 + - name: Install Python dependencies + uses: actions/setup-python@v5 + with: + python-version: '3.10' # Adjust if needed + - name: Install match library + run: | + pip install --upgrade pip + pip install match + pip install requests + - name: Run Python script + run: | + python tools/maintainers/check_review.py ${{ github.event.pull_request.number }} ${{secrets.GITHUB_TOKEN}} +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/tools/maintainers/check_review.py b/tools/maintainers/check_review.py new file mode 100644 index 00000000000000..755b6de1c59128 --- /dev/null +++ b/tools/maintainers/check_review.py @@ -0,0 +1,96 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import os +import requests +import json +import match +import sys + +def check_review_pass(pr_num, maintainers_file, token): + """ + Checks if all necessary files have been reviewed by maintainers. + + Args: + pr_num (int): PR number. + maintainers_file (str): Path to maintainers.json. + token (str): GitHub token. + + Returns: + bool: True if all files are reviewed, False otherwise. + """ + headers = {'Authorization': f'{token}'} + + # Get PR review information and extract reviewers and statuses + response = requests.get(f"https://api.github.com/repos/apache/doris/pulls/{pr_num}/reviews?per_page=100", headers=headers) + reviews = response.json() + reviewers = [review['user']['login'] for review in reviews] + statuses = [review['state'] for review in reviews] + print(reviewers) + print(statuses) + # Create a dictionary with the latest status for each reviewer + latest_statuses = {reviewer: status for reviewer, status in zip(reviewers, statuses)} + + # Create a list of reviewers who have approved + approves = [reviewer for reviewer, status in latest_statuses.items() if status == 'APPROVED'] + + # Get list of changed files + response = requests.get(f"https://api.github.com/repos/apache/doris/pulls/{pr_num}/files", headers=headers) + file_changes = response.json() + file_change_names = [file['filename'] for file in file_changes] + + # Read maintainers.json + with open(maintainers_file) as f: + data = json.load(f) + need_maintainers_review_path = [item['path'] for item in data['paths']] + + # Check if each path's files have been reviewed by a maintainer + has_maintainer_review = True + for file in file_change_names: + path_found = False + for path_item in data['paths']: + path = path_item['path'] + maintainers = path_item['maintainers'] + + if match.match(file, path): + path_found = True + if maintainers: + if not any(maintainer in approves for maintainer in maintainers): + has_maintainer_review = False + break + else: + continue + + if not path_found: + continue + print(approves) + if len(approves) < 2: + print("PR has not been approved by at least 2 reviewers") + exit(1) + + return has_maintainer_review + +if __name__ == "__main__": + + pr_num = sys.argv[1] + token = sys.argv[2] + maintainers_file = 'tools/maintainers/maintainers.json' # Adjust path if needed + + if check_review_pass(pr_num, maintainers_file, token): + print("Thanks for your contribution to Doris.") + else: + print("PR has file changes that need to be reviewed by maintainers.") + exit(1) diff --git a/tools/maintainers/maintainers.json b/tools/maintainers/maintainers.json new file mode 100644 index 00000000000000..981b63021cd54b --- /dev/null +++ b/tools/maintainers/maintainers.json @@ -0,0 +1,12 @@ +{ + "paths":[ + { + "path":"be/src/io/*", + "maintainers": [ + "platoneko", + "gavinchou", + "dataroaring" + ] + } + ] +}