-
Notifications
You must be signed in to change notification settings - Fork 0
/
dependabot_reviews.py
128 lines (108 loc) · 3.78 KB
/
dependabot_reviews.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "rich",
# "furl",
# ]
# ///
import subprocess
import json
from rich.console import Console
from furl import furl
from time import sleep
class PullRequest:
def __init__(self, repositry: str, title: str, url: str, labels: list[str]):
self.repositry: str = repositry
self.title: str = title
self._url: furl = furl(url)
self.labels_str: str = self._format_labels(labels)
self.checks_str: str = self._get_and_format_checks()
def __repr__(self) -> str:
return f"PullRequest(repositry='{self.repositry}', title='{self.title}', url='{self.url}', labels='{self.labels}')"
@property
def url(self) -> str:
return self._url.url
def _format_labels(self, labels: list) -> str:
if labels == []:
return ""
return str("[" + ",".join([label["name"] for label in labels]) + "]")
def _get_and_format_checks(self) -> str:
status_checks = execute_gh_command(
f"pr view {self.url} --json statusCheckRollup"
)["statusCheckRollup"]
if status_checks == []:
return "[yellow] Checks not found[/yellow]"
for check in status_checks:
if check["conclusion"] != "SUCCESS":
return "[red] Checks not passed[/red]"
else:
return "[green] Checks passed[/green]"
def execute_gh_command(command: str, return_response: bool = True) -> dict:
"""
Execute a Github CLI command and return the JSON output.
"""
try:
result = subprocess.run(
f"gh {command}",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True,
)
if return_response:
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
error_message = f"Command failed with exit code {e.returncode}\n"
error_message += f"stdout: {e.stdout}\n"
error_message += f"stderr: {e.stderr}"
raise RuntimeError(error_message) from e
except json.JSONDecodeError as e:
raise ValueError(
f"Failed to parse JSON output: {e}\nOutput: {result.stdout}"
) from e
def get_pending_dependabot_prs() -> list[PullRequest]:
"""
Return a list of pending dependabot PRs.
"""
response = execute_gh_command(
"search prs --state open --author 'dependabot[bot]' --review-requested @me --json repository,title,url,labels"
)
return [
PullRequest(
repositry=pr["repository"]["nameWithOwner"],
title=pr["title"],
url=pr["url"],
labels=pr["labels"],
)
for pr in response
]
def approve_pr(pr: str) -> None:
"""
Approves a PR with the message "@dependabot merge".
"""
execute_gh_command(
f"pr review {pr.url} --approve --body '@dependabot merge'",
return_response=False,
)
def format_message(pr: PullRequest):
return f"[bold]{pr.repositry}[/bold]:{pr.labels_str} '{pr.title}' {pr.url}{pr.checks_str}"
def main() -> None:
terminal = Console()
with terminal.status("Fetching PRs..."):
prs = get_pending_dependabot_prs()
terminal.print(f"Found [red]{len(prs)}[/red] pending dependabot PRs to review")
for pr in prs:
terminal.print(format_message(pr))
terminal.print("Approve? (y/N) ", end="")
if terminal.input().lower().strip() in ["y", "yes"]:
with terminal.status("Approving..."):
approve_pr(pr)
terminal.print("[green][bold]Approved[/]")
else:
terminal.print("[yellow]Skipped[/yellow]")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
exit(0)