github-overlord 0.4.3__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {github_overlord-0.4.3 → github_overlord-0.5.0}/PKG-INFO +3 -3
- {github_overlord-0.4.3 → github_overlord-0.5.0}/README.md +1 -1
- {github_overlord-0.4.3 → github_overlord-0.5.0}/github_overlord/__init__.py +45 -25
- github_overlord-0.5.0/github_overlord/stale_commenter.py +97 -0
- {github_overlord-0.4.3 → github_overlord-0.5.0}/pyproject.toml +3 -3
- {github_overlord-0.4.3 → github_overlord-0.5.0}/github_overlord/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: github-overlord
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Automatically merge Dependabot PRs in public repositories
|
|
5
5
|
Home-page: https://github.com/iloveitaly/github-overlord
|
|
6
6
|
Keywords: github,dependabot,automation
|
|
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
13
13
|
Requires-Dist: apscheduler (>=3.10.4,<4.0.0)
|
|
14
14
|
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
15
15
|
Requires-Dist: funcy-pipe (>=0.11.0,<0.12.0)
|
|
16
|
-
Requires-Dist: openai (>=1.
|
|
16
|
+
Requires-Dist: openai (>=1.35.1,<2.0.0)
|
|
17
17
|
Requires-Dist: pygithub (>=2.3.0,<3.0.0)
|
|
18
18
|
Requires-Dist: python-decouple-typed (>=3.11.0,<4.0.0)
|
|
19
19
|
Requires-Dist: structlog (>=24.2.0,<25.0.0)
|
|
@@ -25,7 +25,7 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
|
|
26
26
|
GitHub Overlord is a Python script that automatically merges Dependabot PRs in public repositories that have passed CI checks.
|
|
27
27
|
|
|
28
|
-
This simple project has also given me the chance to iterate on my nixpacks github actions project.
|
|
28
|
+
This simple project has also given me the chance to iterate on my [nixpacks github actions project](https://github.com/iloveitaly/github-action-nixpacks).
|
|
29
29
|
|
|
30
30
|
## Installation
|
|
31
31
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
GitHub Overlord is a Python script that automatically merges Dependabot PRs in public repositories that have passed CI checks.
|
|
6
6
|
|
|
7
|
-
This simple project has also given me the chance to iterate on my nixpacks github actions project.
|
|
7
|
+
This simple project has also given me the chance to iterate on my [nixpacks github actions project](https://github.com/iloveitaly/github-action-nixpacks).
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -7,8 +7,10 @@ import click
|
|
|
7
7
|
import funcy_pipe as fp
|
|
8
8
|
from github import Github
|
|
9
9
|
from github.GithubObject import NotSet
|
|
10
|
+
from github.Notification import Notification
|
|
10
11
|
from github.PullRequest import PullRequest
|
|
11
12
|
|
|
13
|
+
from github_overlord.stale_commenter import check_for_stale_comments, is_stale_comment
|
|
12
14
|
from github_overlord.utils import log
|
|
13
15
|
|
|
14
16
|
AUTOMATIC_MERGE_MESSAGE = "Automatically merged with GitHub overlord"
|
|
@@ -154,6 +156,21 @@ def cli():
|
|
|
154
156
|
pass
|
|
155
157
|
|
|
156
158
|
|
|
159
|
+
def extract_repo_reference_from_github_url(url: str):
|
|
160
|
+
"""
|
|
161
|
+
Extract the owner and repo from a GitHub URL
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
# convert 'https://github.com/iloveitaly/todoist-digest/pulls' to 'iloveitaly/todoist-digest'
|
|
165
|
+
if "github.com" in url:
|
|
166
|
+
match = re.search(r"github\.com/([^/]+)/([^/]+)", url)
|
|
167
|
+
|
|
168
|
+
if match:
|
|
169
|
+
url = f"{match.group(1)}/{match.group(2)}"
|
|
170
|
+
|
|
171
|
+
return url
|
|
172
|
+
|
|
173
|
+
|
|
157
174
|
@click.command()
|
|
158
175
|
@click.option(
|
|
159
176
|
"--token",
|
|
@@ -168,47 +185,50 @@ def dependabot(token, dry_run, repo):
|
|
|
168
185
|
Automatically merge dependabot PRs in public repos that have passed CI checks
|
|
169
186
|
"""
|
|
170
187
|
|
|
171
|
-
|
|
172
|
-
# convert 'https://github.com/iloveitaly/todoist-digest/pulls' to 'iloveitaly/todoist-digest'
|
|
173
|
-
if "github.com" in repo:
|
|
174
|
-
match = re.search(r"github\.com/([^/]+)/([^/]+)", repo)
|
|
175
|
-
|
|
176
|
-
if match:
|
|
177
|
-
repo = f"{match.group(1)}/{match.group(2)}"
|
|
178
|
-
|
|
188
|
+
repo = extract_repo_reference_from_github_url(repo)
|
|
179
189
|
main(token, dry_run, repo)
|
|
180
190
|
|
|
181
191
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
192
|
+
@click.command()
|
|
193
|
+
@click.option(
|
|
194
|
+
"--token",
|
|
195
|
+
help="GitHub token, can also be set via GITHUB_TOKEN",
|
|
196
|
+
default=os.getenv("GITHUB_TOKEN"),
|
|
197
|
+
)
|
|
198
|
+
# TODO move this into the parent command
|
|
199
|
+
@click.option("--dry-run", is_flag=True, help="Run script without merging PRs")
|
|
200
|
+
@click.option("--repo", help="Only process a single repository")
|
|
201
|
+
def keep_alive_prs(token, dry_run, repo):
|
|
202
|
+
repo = extract_repo_reference_from_github_url(repo)
|
|
185
203
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
2. PRs that are not merged, been open for at least 30 days, with no comments from the maintainer.
|
|
189
|
-
"""
|
|
204
|
+
github = Github(token)
|
|
205
|
+
login = github.get_user().login
|
|
190
206
|
|
|
191
|
-
|
|
207
|
+
github.get_repo(repo).get_pulls(state="open") | fp.filter(
|
|
208
|
+
lambda pr: pr.user.login == login
|
|
209
|
+
) | fp.map(check_for_stale_comments) | fp.to_list()
|
|
192
210
|
|
|
193
211
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if
|
|
197
|
-
|
|
212
|
+
def notifications(token, dry_run):
|
|
213
|
+
"""
|
|
214
|
+
Look at notifications and mark them as read if they are dependabot notifications
|
|
215
|
+
"""
|
|
198
216
|
|
|
199
217
|
g = Github(token)
|
|
200
218
|
user = g.get_user()
|
|
201
219
|
notifications = user.get_notifications()
|
|
202
220
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if not read_only:
|
|
206
|
-
notification.mark_as_read()
|
|
221
|
+
def handle_notification(notification: Notification):
|
|
222
|
+
pass
|
|
207
223
|
|
|
208
|
-
|
|
224
|
+
# for notification in notifications:
|
|
225
|
+
# print(f"Notification: {notification.subject['title']}, Reason: {notification.reason}")
|
|
226
|
+
# if not read_only:
|
|
227
|
+
# notification.mark_as_read()
|
|
209
228
|
|
|
210
229
|
|
|
211
230
|
cli.add_command(dependabot)
|
|
231
|
+
cli.add_command(keep_alive_prs)
|
|
212
232
|
|
|
213
233
|
if __name__ == "__main__":
|
|
214
234
|
cli()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from github.IssueComment import IssueComment
|
|
4
|
+
from github.PullRequest import PullRequest
|
|
5
|
+
from openai import OpenAI
|
|
6
|
+
|
|
7
|
+
from github_overlord.utils import log
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def check_for_stale_comments(pr: PullRequest):
|
|
11
|
+
"""
|
|
12
|
+
Look at PRs which you have written:
|
|
13
|
+
|
|
14
|
+
1. There are bots out there which will close the PR if there are is no activity, even if there is no activity from
|
|
15
|
+
the maintainer. This will keep the PR open by adding a comment.
|
|
16
|
+
2. PRs that are not merged, been open for at least 30 days, with no comments from the maintainer.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
log.debug("checking for stale comments", url=pr.html_url)
|
|
21
|
+
|
|
22
|
+
# PR comments are comments on the cod3
|
|
23
|
+
issue = pr.as_issue()
|
|
24
|
+
comments = list(issue.get_comments())
|
|
25
|
+
|
|
26
|
+
if len(comments) == 0:
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
last_comment = comments[-1]
|
|
30
|
+
|
|
31
|
+
# TODO this will need to be changed
|
|
32
|
+
if last_comment.user.login != "github-actions[bot]":
|
|
33
|
+
log.debug("Last comment is not from github-actions[bot]", url=pr.html_url)
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
is_stale, comment = is_stale_comment(last_comment)
|
|
37
|
+
|
|
38
|
+
if not is_stale:
|
|
39
|
+
log.debug("comment does not indicate stale state", url=pr.html_url)
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
log.info(
|
|
43
|
+
"comment indicates stale state, commenting", url=pr.html_url, comment=comment
|
|
44
|
+
)
|
|
45
|
+
# pr.create_issue_comment(comment)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def is_stale_comment(comment: IssueComment):
|
|
49
|
+
"""
|
|
50
|
+
Check if the comment indicates that the PR will be automatically closed if there is no activity
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
prompt = """
|
|
54
|
+
A GitHub pull request comment will be included with the author name. Determine if this comment indicates that if there is no activity
|
|
55
|
+
(more commits, comments, etc) the pull request will be closed. If the comment indicates that the pull
|
|
56
|
+
request will be closed, respond with a JSON object like:
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
"stale": "yes",
|
|
60
|
+
"comment": "Friendly reminder on this pull request! Let me know what else may need to be done here."
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Adjust the comment wording slightly.
|
|
64
|
+
|
|
65
|
+
If the comment does not indicate that the pull request will be closed, respond with:
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
"stale": "no",
|
|
69
|
+
}
|
|
70
|
+
"""
|
|
71
|
+
comment_markdown = """
|
|
72
|
+
Author: {comment.user.login}
|
|
73
|
+
|
|
74
|
+
{comment.body}
|
|
75
|
+
"""
|
|
76
|
+
client = OpenAI()
|
|
77
|
+
|
|
78
|
+
response = client.chat.completions.create(
|
|
79
|
+
messages=[
|
|
80
|
+
{
|
|
81
|
+
"role": "system",
|
|
82
|
+
"content": prompt,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"role": "user",
|
|
86
|
+
"content": comment_markdown,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
model="gpt-3.5-turbo",
|
|
90
|
+
response_format={"type": "json_object"},
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# TODO got to be a helper for this instead
|
|
94
|
+
message = response.choices[0].message
|
|
95
|
+
response_dict = json.loads(message.content)
|
|
96
|
+
|
|
97
|
+
return (response_dict["stale"] == "yes", response_dict.get("comment"))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "github-overlord"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
description = "Automatically merge Dependabot PRs in public repositories"
|
|
5
5
|
authors = ["Michael Bianco <mike@mikebian.co>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -15,7 +15,7 @@ python-decouple-typed = "^3.11.0"
|
|
|
15
15
|
funcy-pipe = "^0.11.0"
|
|
16
16
|
structlog = "^24.2.0"
|
|
17
17
|
apscheduler = "^3.10.4"
|
|
18
|
-
openai = "^1.
|
|
18
|
+
openai = "^1.35.1"
|
|
19
19
|
|
|
20
20
|
[tool.poetry.scripts]
|
|
21
21
|
github-overlord = "github_overlord:cli"
|
|
@@ -29,7 +29,7 @@ float_to_top = true
|
|
|
29
29
|
[tool.poetry.group.dev.dependencies]
|
|
30
30
|
isort = "^5.13.2"
|
|
31
31
|
black = "^24.4.2"
|
|
32
|
-
pyright = "^1.1.
|
|
32
|
+
pyright = "^1.1.368"
|
|
33
33
|
pylint = "^3.2.3"
|
|
34
34
|
|
|
35
35
|
[build-system]
|
|
File without changes
|