github-overlord 0.6.9__tar.gz → 0.7.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: github-overlord
3
- Version: 0.6.9
3
+ Version: 0.7.1
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
@@ -12,11 +12,11 @@ Classifier: Programming Language :: Python :: 3.11
12
12
  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
- Requires-Dist: funcy-pipe (>=0.11.0,<0.12.0)
16
- Requires-Dist: openai (>=1.35.3,<2.0.0)
15
+ Requires-Dist: funcy-pipe (>=0.11.1,<0.12.0)
16
+ Requires-Dist: openai (>=1.41.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
- Requires-Dist: structlog (>=24.2.0,<25.0.0)
19
+ Requires-Dist: structlog (>=24.4.0,<25.0.0)
20
20
  Description-Content-Type: text/markdown
21
21
 
22
22
  [![Release Notes](https://img.shields.io/github/release/iloveitaly/github-overlord)](https://github.com/iloveitaly/github-overlord/releases) [![Downloads](https://static.pepy.tech/badge/github-overlord/month)](https://pepy.tech/project/github-overlord) [![Python Versions](https://img.shields.io/pypi/pyversions/github-overlord)](https://pypi.org/project/github-overlord) ![GitHub CI Status](https://github.com/iloveitaly/github-overlord/actions/workflows/build_and_publish.yml/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -10,12 +10,10 @@ from github.GithubObject import NotSet
10
10
  from github.Notification import Notification
11
11
  from github.PullRequest import PullRequest
12
12
 
13
- from github_overlord.stale_commenter import (
14
- check_for_stale_comments,
15
- inspect_repo_for_stale_prs,
16
- is_stale_comment,
17
- )
18
- from github_overlord.utils import log
13
+ import github_overlord.patch as _
14
+
15
+ from .stale_commenter import inspect_repo_for_stale_prs
16
+ from .utils import log
19
17
 
20
18
  AUTOMATIC_MERGE_MESSAGE = "Automatically merged with [github-overlord](https://github.com/iloveitaly/github-overlord)"
21
19
 
@@ -244,26 +242,86 @@ def keep_alive_prs(token, dry_run, repo):
244
242
  log.info("stale PR check complete")
245
243
 
246
244
 
247
- def notifications(token, dry_run):
245
+ @click.command()
246
+ @click.option(
247
+ "--token",
248
+ help="GitHub token, can also be set via GITHUB_TOKEN",
249
+ default=os.getenv("GITHUB_TOKEN"),
250
+ )
251
+ # TODO move this into the parent command
252
+ @click.option("--dry-run", is_flag=True, help="Run script without merging PRs")
253
+ @click.option(
254
+ "--only-unread", is_flag=True, help="Only process a single repository", default=True
255
+ )
256
+ def notifications(token, dry_run, only_unread):
248
257
  """
249
- Look at notifications and mark them as read if they are dependabot notifications
258
+ Look at notifications and mark them as read if they are:
259
+
260
+ * Dependabot notifications
261
+ * Releases on repos I own
262
+ * Closed (merged, closed) pull requests on repos I own
263
+ * Closed pull requests that I authored
264
+
265
+ Helpful if you work across a lot of repos and want to keep your notifications clean.
250
266
  """
251
267
 
252
- g = Github(token)
253
- user = g.get_user()
254
- notifications = user.get_notifications()
268
+ github = Github(token)
269
+ user = github.get_user()
270
+ login = user.login
271
+
272
+ # all includes read notifications AND done notifications :/
273
+ # there is no way to determine if a notification is marked as done
274
+ notifications = list(user.get_notifications(all=not only_unread))
275
+
276
+ # TODO fix funcy_pipe here
277
+ released_on_owned_repos = (
278
+ notifications
279
+ | fp.filter(
280
+ lambda n: n.subject.type == "Release"
281
+ and n.repository.owner.login == login
282
+ # TODO I think there is a way to convert the instance method to a standard method so it could be mapped
283
+ # patchy had some code for this
284
+ )
285
+ | fp.lmap(Notification.mark_as_done)
286
+ )
287
+
288
+ log.info("marked releases as done", count=len(released_on_owned_repos))
255
289
 
256
- def handle_notification(notification: Notification):
257
- pass
290
+ def is_dependabot_notification(notification: Notification) -> bool:
291
+ return notification.get_pull_request().user.login == "dependabot[bot]"
292
+
293
+ def is_pull_request(notification: Notification) -> bool:
294
+ return notification.subject.type == "PullRequest"
295
+
296
+ def is_pull_request_open(notification: Notification) -> bool:
297
+ return notification.get_pull_request().state == "open"
298
+
299
+ # TODO github digest has some logic to detect bots, maybev we can use that
300
+ pull_requests_by_dependabot = (
301
+ notifications
302
+ | fp.filter(is_pull_request)
303
+ | fp.filter(is_dependabot_notification)
304
+ | fp.lmap(Notification.mark_as_done)
305
+ )
306
+
307
+ log.info("marked dependabot PRs as done", count=len(pull_requests_by_dependabot))
308
+
309
+ # Closed (merged, closed) pull requests that I authored
310
+ owned_closed_pull_requests = (
311
+ notifications
312
+ # PRs that I did not author may still be interesting
313
+ | fp.where_attr(reason="author")
314
+ | fp.filter(is_pull_request)
315
+ | fp.filter(fp.complement(is_pull_request_open))
316
+ | fp.lmap(Notification.mark_as_done)
317
+ )
258
318
 
259
- # for notification in notifications:
260
- # print(f"Notification: {notification.subject['title']}, Reason: {notification.reason}")
261
- # if not read_only:
262
- # notification.mark_as_read()
319
+ log.info("marked owned closed PRs as done", count=len(owned_closed_pull_requests))
263
320
 
264
321
 
265
322
  cli.add_command(dependabot)
266
323
  cli.add_command(keep_alive_prs)
324
+ cli.add_command(notifications)
267
325
 
268
326
  if __name__ == "__main__":
269
327
  cli()
@@ -0,0 +1,14 @@
1
+ from github.Notification import Notification
2
+
3
+
4
+ def mark_as_done(self) -> None:
5
+ """
6
+ :calls: `PATCH /notifications/threads/{id} <https://docs.github.com/en/rest/activity/notifications?apiVersion=2022-11-28#mark-a-thread-as-done>`_
7
+ """
8
+ headers, data = self._requester.requestJsonAndCheck(
9
+ "DELETE",
10
+ self.url,
11
+ )
12
+
13
+
14
+ Notification.mark_as_done = mark_as_done
@@ -9,8 +9,23 @@ from pathlib import Path
9
9
  import structlog
10
10
  from decouple import config
11
11
 
12
+ root: Path
13
+
14
+ # must type manually, unfortunately :/
15
+ # https://www.structlog.org/en/21.3.0/types.html
16
+ log: structlog.stdlib.BoundLogger
17
+
12
18
 
13
19
  def configure_logger():
20
+ global log
21
+
22
+ # context manager to auto-clear context
23
+ log.context = structlog.contextvars.bound_contextvars # type: ignore
24
+ # set thread-local context
25
+ log.local = structlog.contextvars.bind_contextvars # type: ignore
26
+ # clear thread-local context
27
+ log.clear = structlog.contextvars.clear_contextvars # type: ignore
28
+
14
29
  logger_factory = structlog.PrintLoggerFactory()
15
30
 
16
31
  # allow user to specify a log in case they want to do something meaningful with the stdout
@@ -38,30 +53,15 @@ def configure_logger():
38
53
  )
39
54
 
40
55
 
41
- # must type manually, unfortunately :/
42
- # https://www.structlog.org/en/21.3.0/types.html
43
- log: structlog.stdlib.BoundLogger
44
-
45
-
46
56
  def setup():
47
57
  if hasattr(setup, "complete") and setup.complete:
48
58
  return
49
59
 
50
- global root, logger, log
60
+ global root, log
51
61
 
52
62
  root = Path(__file__).parent.parent
53
63
 
54
64
  log = structlog.get_logger()
55
-
56
- # context manager to auto-clear context
57
- log.context = structlog.contextvars.bound_contextvars # type: ignore
58
- # set thread-local context
59
- log.local = structlog.contextvars.bind_contextvars # type: ignore
60
- # clear thread-local context
61
- log.clear = structlog.contextvars.clear_contextvars # type: ignore
62
-
63
- logger = log
64
-
65
65
  configure_logger()
66
66
 
67
67
  log.debug("application setup")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "github-overlord"
3
- version = "0.6.9"
3
+ version = "0.7.1"
4
4
  description = "Automatically merge Dependabot PRs in public repositories"
5
5
  authors = ["Michael Bianco <mike@mikebian.co>"]
6
6
  readme = "README.md"
@@ -12,10 +12,10 @@ python = "^3.11"
12
12
  pygithub = "^2.3.0"
13
13
  click = "^8.1.7"
14
14
  python-decouple-typed = "^3.11.0"
15
- funcy-pipe = "^0.11.0"
16
- structlog = "^24.2.0"
15
+ funcy-pipe = "^0.11.1"
16
+ structlog = "^24.4.0"
17
17
  apscheduler = "^3.10.4"
18
- openai = "^1.35.3"
18
+ openai = "^1.41.1"
19
19
 
20
20
  [tool.poetry.scripts]
21
21
  github-overlord = "github_overlord:cli"
@@ -28,9 +28,9 @@ float_to_top = true
28
28
 
29
29
  [tool.poetry.group.dev.dependencies]
30
30
  isort = "^5.13.2"
31
- black = "^24.4.2"
32
- pyright = "^1.1.368"
33
- pylint = "^3.2.3"
31
+ black = "^24.8.0"
32
+ pyright = "^1.1.376"
33
+ pylint = "^3.2.6"
34
34
 
35
35
  [build-system]
36
36
  requires = ["poetry-core"]