Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 70 additions & 50 deletions .github/scripts/label_community_user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import time

import requests

Expand All @@ -14,57 +13,72 @@
}


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_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:
Expand All @@ -78,6 +92,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")
Expand All @@ -87,19 +103,23 @@ 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(
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.")
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/label_community_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: Label Community PR
on:
pull_request:
types: [opened]
permissions:
pull-requests: write

jobs:
label_pr:
Expand All @@ -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
Loading