pytorch/.github/scripts/label_utils.py
Ning Xu 2c76838d7f Issue-88098: extract utils from check labels (#94597)
Fixes #88098

This is a mirror of the same PR (https://github.com/Goldspear/pytorch/pull/2) that has been reviewed in my fork (due to it's a stacked PR).

======================
## Context

This is the 2nd of the 3 PRs to address issue-88098.

## What Changed
1. Extract comment related utils from trymerge.py to github_utils.py
2. Extract label related utils from trymerge.py and check_labels.py to label_utils.py

## Tests
* pytorch-dummy repo [trymerge run ](https://github.com/Goldspear/pytorch-dummy/actions/runs/4118944174)merged the test PR [OK](https://github.com/Goldspear/pytorch-dummy/pull/2).

## Note to Reviewers
Due to higher degree of complexity involved to extract GitHubPR class, it's worth having a separate issue to handle that part of refactoring. This issue only focusing on refactoring where necessary to ship the functional diff.

* 1st PR: https://github.com/pytorch/pytorch/pull/94179
* 2nd PR: this one
* 3rd PR: https://github.com/Goldspear/pytorch/pull/3

Pull Request resolved: https://github.com/pytorch/pytorch/pull/94597
Approved by: https://github.com/ZainRizvi
2023-02-12 12:18:53 +00:00

94 lines
3.4 KiB
Python

"""GitHub Label Utilities."""
import json
from functools import lru_cache
from typing import List, Any, Tuple, TYPE_CHECKING, Union
from urllib.request import urlopen, Request
from github_utils import (
GitHubComment,
gh_fetch_json,
)
# TODO: this is a temp workaround to avoid circular dependencies,
# and should be removed once GitHubPR is refactored out of trymerge script.
if TYPE_CHECKING:
from trymerge import GitHubPR
BOT_AUTHORS = ["github-actions", "pytorchmergebot", "pytorch-bot"]
LABEL_ERR_MSG_TITLE = "This PR needs a label"
LABEL_ERR_MSG = f"""# {LABEL_ERR_MSG_TITLE}
If your changes are user facing and intended to be a part of release notes, please use a label starting with `release notes:`.
If not, please add the `topic: not user facing` label.
For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.
"""
# Modified from https://github.com/pytorch/pytorch/blob/b00206d4737d1f1e7a442c9f8a1cadccd272a386/torch/hub.py#L129
def _read_url(url: Request) -> Tuple[Any, Any]:
with urlopen(url) as r:
return r.headers, r.read().decode(r.headers.get_content_charset('utf-8'))
def request_for_labels(url: str) -> Tuple[Any, Any]:
headers = {'Accept': 'application/vnd.github.v3+json'}
return _read_url(Request(url, headers=headers))
def update_labels(labels: List[str], info: str) -> None:
labels_json = json.loads(info)
labels.extend([x["name"] for x in labels_json])
def get_last_page_num_from_header(header: Any) -> int:
# Link info looks like: <https://api.github.com/repositories/65600975/labels?per_page=100&page=2>;
# rel="next", <https://api.github.com/repositories/65600975/labels?per_page=100&page=3>; rel="last"
link_info = header['link']
prefix = "&page="
suffix = ">;"
return int(link_info[link_info.rindex(prefix) + len(prefix):link_info.rindex(suffix)])
@lru_cache()
def gh_get_labels(org: str, repo: str) -> List[str]:
prefix = f"https://api.github.com/repos/{org}/{repo}/labels?per_page=100"
header, info = request_for_labels(prefix + "&page=1")
labels: List[str] = []
update_labels(labels, info)
last_page = get_last_page_num_from_header(header)
assert last_page > 0, "Error reading header info to determine total number of pages of labels"
for page_number in range(2, last_page + 1): # skip page 1
_, info = request_for_labels(prefix + f"&page={page_number}")
update_labels(labels, info)
return labels
def gh_add_labels(org: str, repo: str, pr_num: int, labels: Union[str, List[str]]) -> None:
gh_fetch_json(
f'https://api.github.com/repos/{org}/{repo}/issues/{pr_num}/labels',
data={"labels": labels},
)
def get_release_notes_labels(org: str, repo: str) -> List[str]:
return [label for label in gh_get_labels(org, repo) if label.lstrip().startswith("release notes:")]
def has_required_labels(pr: "GitHubPR") -> bool:
pr_labels = pr.get_labels()
# Check if PR is not user facing
is_not_user_facing_pr = any(label.strip() == "topic: not user facing" for label in pr_labels)
return (
is_not_user_facing_pr or
any(label.strip() in get_release_notes_labels(pr.org, pr.project) for label in pr_labels)
)
def is_label_err_comment(comment: GitHubComment) -> bool:
return comment.body_text.lstrip(" #").startswith(LABEL_ERR_MSG_TITLE) and comment.author_login in BOT_AUTHORS