From 3f4db188ee5c23fa207ef456c84ed8b1bc516e79 Mon Sep 17 00:00:00 2001 From: "Po-Wei Wang (Vincent)" Date: Mon, 2 Jun 2025 21:17:01 -0700 Subject: [PATCH 1/2] Fix the labeling logic Signed-off-by: Po-Wei Wang (Vincent) --- .github/scripts/label_community_user.py | 17 ++++++++++++++--- .github/workflows/label_community_pr.yml | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.github/scripts/label_community_user.py b/.github/scripts/label_community_user.py index d95f1bf372d7..000679c09d8a 100644 --- a/.github/scripts/label_community_user.py +++ b/.github/scripts/label_community_user.py @@ -59,12 +59,21 @@ def get_nvidia_members() -> list[str]: return members -def add_label_to_pr(repo_name: str, pr_number: str, label: str): +def add_label_to_pr(repo_owner: str, repo_name: str, pr_number: str, + label: str): """Adds a label to a pull request.""" - url = f"{GITHUB_API_URL}/repos/NVIDIA/{repo_name}/issues/{pr_number}/labels" + url = f"{GITHUB_API_URL}/repos/{repo_owner}/{repo_name}/issues/{pr_number}/labels" payload = {"labels": [label]} + print(f"Attempting to add label. URL: {url}, Payload: {payload}") try: response = requests.post(url, headers=HEADERS, json=payload) + print(f"API Response Status Code: {response.status_code}") + try: + response_json = response.json() + print(f"API Response JSON: {response_json}") + except requests.exceptions.JSONDecodeError: + print(f"API Response Text (not JSON): {response.text}") + response.raise_for_status() print(f"Successfully added label '{label}' to PR #{pr_number}.") except requests.exceptions.RequestException as e: @@ -78,6 +87,8 @@ def main(): assert pr_author, "PR_AUTHOR environment variable not set" pr_number = os.environ.get("PR_NUMBER") assert pr_number, "PR_NUMBER environment variable not set" + repo_owner = os.environ.get("REPO_OWNER") + assert repo_owner, "REPO_OWNER environment variable not set" repo_name = os.environ.get("REPO_NAME") assert repo_name, "REPO_NAME environment variable not set" community_label = os.environ.get("COMMUNITY_LABEL") @@ -99,7 +110,7 @@ def main(): print( f"User '{pr_author}' is a community user. Adding label '{community_label}'." ) - add_label_to_pr(repo_name, pr_number, community_label) + add_label_to_pr(repo_owner, repo_name, pr_number, community_label) else: print( f"User '{pr_author}' is an NVIDIA member. No label will be added.") diff --git a/.github/workflows/label_community_pr.yml b/.github/workflows/label_community_pr.yml index 6ae9322c4bf4..dd2fa3c30390 100644 --- a/.github/workflows/label_community_pr.yml +++ b/.github/workflows/label_community_pr.yml @@ -3,6 +3,8 @@ name: Label Community PR on: pull_request: types: [opened] +permissions: + pull-requests: write jobs: label_pr: @@ -24,6 +26,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_AUTHOR: ${{ github.event.pull_request.user.login }} PR_NUMBER: ${{ github.event.pull_request.number }} + REPO_OWNER: ${{ github.event.repository.owner.login }} REPO_NAME: ${{ github.event.repository.name }} COMMUNITY_LABEL: "Community want to contribute" run: python .github/scripts/label_community_user.py From d5a86d02f66b6708d7870a292d9c702e94a7fa49 Mon Sep 17 00:00:00 2001 From: "Po-Wei Wang (Vincent)" Date: Tue, 3 Jun 2025 16:45:38 -0700 Subject: [PATCH 2/2] update script to use better api endpoint Signed-off-by: Po-Wei Wang (Vincent) --- .github/scripts/label_community_user.py | 103 +++++++++++++----------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/.github/scripts/label_community_user.py b/.github/scripts/label_community_user.py index 000679c09d8a..c3952b27d6dd 100644 --- a/.github/scripts/label_community_user.py +++ b/.github/scripts/label_community_user.py @@ -1,5 +1,4 @@ import os -import time import requests @@ -14,49 +13,55 @@ } -def get_nvidia_members() -> list[str]: - """Fetches all NVIDIA organization members.""" - members = [] - page = 1 - per_page = 100 - - while True: - url = f"{GITHUB_API_URL}/orgs/NVIDIA/members?per_page={per_page}&page={page}" - try: - time.sleep(0.5) - response = requests.get(url, headers=HEADERS) - - if response.status_code == 404: - raise RuntimeError( - f"Organization 'NVIDIA' not found (404). Cannot fetch members." - ) - elif response.status_code == 403: +def check_user_membership(org: str, username: str) -> bool: + """Checks if a user is a member of an organization using a direct API call.""" + url = f"{GITHUB_API_URL}/orgs/{org}/members/{username}" + try: + response = requests.get(url, + headers=HEADERS, + timeout=10, + allow_redirects=False) + + if response.status_code == 204 or response.status_code == 302: + print( + f"Membership check for '{username}' in '{org}': Positive (Status {response.status_code})." + ) + return True + elif response.status_code == 404: + print( + f"Membership check for '{username}' in '{org}': Negative (Status {response.status_code})." + ) + return False + elif response.status_code == 403: + error_message = "Details not parsable from JSON." + try: error_message = response.json().get( - "message", "") if response.content else "" - raise RuntimeError( - f"Forbidden (403) when fetching members for 'NVIDIA'. " - f"This may be due to insufficient token permissions or rate limits. Details: {error_message}. Cannot fetch members." - ) - + "message", "No specific message from API.") + except requests.exceptions.JSONDecodeError: + if response.text: + error_message = response.text + detail = ( + f"Forbidden (403) checking membership for '{username}' in '{org}'. " + f"Token permissions (e.g., 'read:org' scope) or org restrictions likely. API msg: {error_message}" + ) + print(detail) + raise RuntimeError(detail) + else: + print( + f"Unexpected status {response.status_code} checking membership for '{username}' in '{org}'. Response: {response.text[:200]}" + ) response.raise_for_status() - page_data = response.json() - - if not page_data: - break - - for member_data in page_data: - if isinstance(member_data, dict) and "login" in member_data: - members.append(member_data["login"].lower()) - - if len(page_data) < per_page: - break - page += 1 - except Exception as e: - print(f"Error fetching NVIDIA members: {e}") - return [] - - print(f"Successfully fetched {len(members)} members for 'NVIDIA'.") - return members + return False + except requests.exceptions.Timeout: + print( + f"Timeout checking membership for '{username}' in '{org}'. Assuming not a member." + ) + return False + except requests.exceptions.RequestException as e: + print( + f"RequestException checking membership for '{username}' in '{org}': {e}. Assuming not a member." + ) + return False def add_label_to_pr(repo_owner: str, repo_name: str, pr_number: str, @@ -98,13 +103,17 @@ def main(): f"Starting NVIDIA membership check for PR author '{pr_author}' on PR #{pr_number}." ) - nvidia_members = get_nvidia_members() - if not nvidia_members: - print("Could not retrieve NVIDIA members list. Exiting.") + try: + is_member = check_user_membership("NVIDIA", pr_author) + except RuntimeError as e: + print( + f"Critical error during NVIDIA membership check for '{pr_author}': {e}" + ) + print("Halting script due to inability to determine membership status.") return - is_member = pr_author.lower() in nvidia_members - print(f"User '{pr_author}' is a member of NVIDIA: {is_member}") + print( + f"User '{pr_author}' is determined to be an NVIDIA member: {is_member}") if not is_member: print(