|
| 1 | +import logging |
| 2 | +from typing import Literal |
| 3 | + |
| 4 | +from github import Github |
| 5 | +from github.PullRequestReview import PullRequestReview |
| 6 | +from pydantic import BaseModel, SecretStr |
| 7 | +from pydantic_settings import BaseSettings |
| 8 | + |
| 9 | + |
| 10 | +class LabelSettings(BaseModel): |
| 11 | + await_label: str | None = None |
| 12 | + number: int |
| 13 | + |
| 14 | + |
| 15 | +default_config = {"approved-2": LabelSettings(await_label="awaiting-review", number=2)} |
| 16 | + |
| 17 | + |
| 18 | +class Settings(BaseSettings): |
| 19 | + github_repository: str |
| 20 | + token: SecretStr |
| 21 | + debug: bool | None = False |
| 22 | + config: dict[str, LabelSettings] | Literal[""] = default_config |
| 23 | + |
| 24 | + |
| 25 | +settings = Settings() |
| 26 | +if settings.debug: |
| 27 | + logging.basicConfig(level=logging.DEBUG) |
| 28 | +else: |
| 29 | + logging.basicConfig(level=logging.INFO) |
| 30 | +logging.debug(f"Using config: {settings.json()}") |
| 31 | +g = Github(settings.token.get_secret_value()) |
| 32 | +repo = g.get_repo(settings.github_repository) |
| 33 | +for pr in repo.get_pulls(state="open"): |
| 34 | + logging.info(f"Checking PR: #{pr.number}") |
| 35 | + pr_labels = list(pr.get_labels()) |
| 36 | + pr_label_by_name = {label.name: label for label in pr_labels} |
| 37 | + reviews = list(pr.get_reviews()) |
| 38 | + review_by_user: dict[str, PullRequestReview] = {} |
| 39 | + for review in reviews: |
| 40 | + if review.user.login in review_by_user: |
| 41 | + stored_review = review_by_user[review.user.login] |
| 42 | + if review.submitted_at >= stored_review.submitted_at: |
| 43 | + review_by_user[review.user.login] = review |
| 44 | + else: |
| 45 | + review_by_user[review.user.login] = review |
| 46 | + approved_reviews = [ |
| 47 | + review for review in review_by_user.values() if review.state == "APPROVED" |
| 48 | + ] |
| 49 | + config = settings.config or default_config |
| 50 | + for approved_label, conf in config.items(): |
| 51 | + logging.debug(f"Processing config: {conf.json()}") |
| 52 | + if conf.await_label is None or (conf.await_label in pr_label_by_name): |
| 53 | + logging.debug(f"Processable PR: {pr.number}") |
| 54 | + if len(approved_reviews) >= conf.number: |
| 55 | + logging.info(f"Adding label to PR: {pr.number}") |
| 56 | + pr.add_to_labels(approved_label) |
| 57 | + if conf.await_label: |
| 58 | + logging.info(f"Removing label from PR: {pr.number}") |
| 59 | + pr.remove_from_labels(conf.await_label) |
| 60 | +logging.info("Finished") |
0 commit comments