Skip to content

Commit 6022ac0

Browse files
authored
✨ Add first-class support for PRs, including reviews, review comments (#20)
1 parent 38b626d commit 6022ac0

File tree

2 files changed

+37
-21
lines changed

2 files changed

+37
-21
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
3838
Then, you can answer an issue or PR and add the label from the config, in this case, `answered`.
3939

40-
After 10 days, if no one has added a new comment, the GitHub action will write:
40+
After 10 days, if no one has added a new comment (or in the case of PRs, a new review or commit), the GitHub action will write:
4141

4242
```markdown
4343
Assuming the original need was handled, this will be automatically closed now.
@@ -140,7 +140,7 @@ And finally, if:
140140

141141
* a PR has a label `needs-tests`
142142
* the label was added _after_ the last comment
143-
* the last comment was addded more than `691200` seconds (8 days) ago
143+
* the last comment was added more than `691200` seconds (8 days) ago
144144

145145
...the GitHub action would close the PR with:
146146

@@ -336,7 +336,7 @@ on:
336336
* The `issues` option with a type of `label` will run it with each specific issue when you add a label.
337337
* This way you can add a label to an issue that was answered long ago, and if the configured delay since the last comment is enough the GitHub action will close the issue right away.
338338
* The `pull_request_target` option with a type of `label` will run it with each specific Pull Request made to your repo when you add a label.
339-
* This way you can add a label to a PR that was answered long ago, or that was waiting for more comments from the author, etc. And if the configured delay since the last comment is enough the GitHub action will close the issue right away.
339+
* This way you can add a label to a PR that was answered long ago, or that was waiting for more comments from the author, reviews, commits, etc. And if the configured delay since the last comment is enough the GitHub action will close the issue right away.
340340
* The `workflow_dispatch` option allows you to run the action manually from the GitHub Actions tab for your repo.
341341

342342
## Motivation

app/main.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
from datetime import datetime, timedelta, timezone
21
import logging
2+
from datetime import datetime, timedelta, timezone
33
from pathlib import Path
44
from typing import Dict, List, Optional, Set
55

66
from github import Github
77
from github.Issue import Issue
8-
from github.IssueComment import IssueComment
98
from github.IssueEvent import IssueEvent
109
from pydantic import BaseModel, SecretStr, validator
1110
from pydantic_settings import BaseSettings
1211

1312

1413
class KeywordMeta(BaseModel):
1514
delay: timedelta = timedelta(days=10)
16-
message: str = "Assuming the original need was handled, this will be automatically closed now."
15+
message: str = (
16+
"Assuming the original need was handled, this will be automatically closed now."
17+
)
1718
remove_label_on_comment: bool = True
1819
remove_label_on_close: bool = False
1920

@@ -41,15 +42,31 @@ class PartialGitHubEvent(BaseModel):
4142
pull_request: Optional[PartialGitHubEventIssue] = None
4243

4344

44-
def get_last_comment(issue: Issue) -> Optional[IssueComment]:
45-
last_comment: Optional[IssueComment] = None
46-
comment: IssueComment
47-
for comment in issue.get_comments():
48-
if not last_comment:
49-
last_comment = comment
50-
elif comment.created_at > last_comment.created_at:
51-
last_comment = comment
52-
return last_comment
45+
def get_last_interaction_date(issue: Issue) -> Optional[datetime]:
46+
last_date: Optional[datetime] = None
47+
comments = list(issue.get_comments())
48+
if issue.pull_request:
49+
pr = issue.as_pull_request()
50+
commits = list(pr.get_commits())
51+
reviews = list(pr.get_reviews())
52+
pr_comments = list(pr.get_comments())
53+
interactions = comments + pr_comments
54+
interaction_dates = [
55+
interaction.created_at for interaction in interactions
56+
]
57+
interaction_dates.extend(
58+
[commit.commit.author.date for commit in commits]
59+
)
60+
interaction_dates.extend([review.submitted_at for review in reviews])
61+
else:
62+
interactions = comments
63+
interaction_dates = [interaction.created_at for interaction in interactions]
64+
for item_date in interaction_dates:
65+
if not last_date:
66+
last_date = item_date
67+
elif item_date > last_date:
68+
last_date = item_date
69+
return last_date
5370

5471

5572
def get_labeled_events(events: List[IssueEvent]) -> List[IssueEvent]:
@@ -92,13 +109,12 @@ def process_issue(*, issue: Issue, settings: Settings) -> None:
92109
label_strs = set([label.name for label in issue.get_labels()])
93110
events = list(issue.get_events())
94111
labeled_events = get_labeled_events(events)
95-
last_comment = get_last_comment(issue)
112+
last_date = get_last_interaction_date(issue)
96113
now = datetime.now(timezone.utc)
97114
for keyword, keyword_meta in settings.input_config.items():
98115
# Check closable delay, if enough time passed and the issue could be closed
99116
closable_delay = (
100-
last_comment is None
101-
or (now - keyword_meta.delay) > last_comment.created_at
117+
last_date is None or (now - keyword_meta.delay) > last_date
102118
)
103119
# Check label, optionally removing it if there's a comment after adding it
104120
if keyword in label_strs:
@@ -107,9 +123,9 @@ def process_issue(*, issue: Issue, settings: Settings) -> None:
107123
labeled_events=labeled_events, label=keyword
108124
)
109125
if (
110-
last_comment
126+
last_date
111127
and keyword_event
112-
and last_comment.created_at > keyword_event.created_at
128+
and last_date > keyword_event.created_at
113129
):
114130
logging.info(
115131
f"Not closing as the last comment was written after adding the "
@@ -141,7 +157,7 @@ def process_issue(*, issue: Issue, settings: Settings) -> None:
141157
github_event: Optional[PartialGitHubEvent] = None
142158
if settings.github_event_path.is_file():
143159
contents = settings.github_event_path.read_text()
144-
github_event = PartialGitHubEvent.parse_raw(contents)
160+
github_event = PartialGitHubEvent.model_validate_json(contents)
145161
if (
146162
settings.github_event_name == "issues"
147163
or settings.github_event_name == "pull_request_target"

0 commit comments

Comments
 (0)