Skip to content

Commit 4e71f70

Browse files
#129 - PR added to warning category despite having a registered label (#157)
* #129 - PR added to warning category despite having a registered label - Reported error cannot be re-simulated. Origin PR was closed without merge. - Fixed presence of pr among returned issues list from github API. - Fixed skipped closed PR during filtration. - Now PRs are in Release Notes visible as PR not an Issues. * - Fixed checks. Could fix several AI review news already. * - Fixed AI Review notes. * - Fixed Black.
1 parent 8c275eb commit 4e71f70

File tree

10 files changed

+71
-34
lines changed

10 files changed

+71
-34
lines changed

main.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ def run() -> None:
5656
)
5757

5858
generator = ReleaseNotesGenerator(py_github, custom_chapters)
59-
filterer = FilterByRelease()
60-
rls_notes = generator.generate(filterer)
59+
rls_notes = generator.generate()
6160
logger.debug("Generated release notes: \n%s", rls_notes)
6261

6362
# Set the output for the GitHub Action

release_notes_generator/filter.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,15 @@ def filter(self, data: MinedData) -> MinedData:
7474
logger.debug("Count of issues reduced from %d to %d", len(data.issues), len(issues_list))
7575

7676
# filter out merged PRs and commits before the date
77-
pulls_list = list(
78-
filter(lambda pull: pull.merged_at is not None and pull.merged_at >= data.since, data.pull_requests)
79-
)
77+
pulls_seen: set[int] = set()
78+
pulls_list: list = []
79+
for pull in data.pull_requests:
80+
if (pull.merged_at is not None and pull.merged_at >= data.since) or (
81+
pull.closed_at is not None and pull.closed_at >= data.since
82+
):
83+
if pull.number not in pulls_seen:
84+
pulls_seen.add(pull.number)
85+
pulls_list.append(pull)
8086
logger.debug("Count of pulls reduced from %d to %d", len(data.pull_requests), len(pulls_list))
8187

8288
commits_list = list(filter(lambda commit: commit.commit.author.date > data.since, data.commits))

release_notes_generator/generator.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from github import Github
2727

28-
from release_notes_generator.filter import Filter
28+
from release_notes_generator.filter import FilterByRelease
2929
from release_notes_generator.miner import DataMiner
3030
from release_notes_generator.action_inputs import ActionInputs
3131
from release_notes_generator.builder import ReleaseNotesBuilder
@@ -65,7 +65,7 @@ def rate_limiter(self) -> GithubRateLimiter:
6565
"""Getter for the GithubRateLimiter instance."""
6666
return self._rate_limiter
6767

68-
def generate(self, filterer: Filter) -> Optional[str]:
68+
def generate(self) -> Optional[str]:
6969
"""
7070
Generates the Release Notes for a given repository.
7171
@@ -79,16 +79,19 @@ def generate(self, filterer: Filter) -> Optional[str]:
7979
if data.is_empty():
8080
return None
8181

82-
filtered_data = filterer.filter(data=data)
82+
filterer = FilterByRelease()
83+
data_filtered_by_release = filterer.filter(data=data)
8384

8485
changelog_url: str = get_change_url(
85-
tag_name=ActionInputs.get_tag_name(), repository=filtered_data.repository, git_release=filtered_data.release
86+
tag_name=ActionInputs.get_tag_name(),
87+
repository=data_filtered_by_release.repository,
88+
git_release=data_filtered_by_release.release,
8689
)
8790

88-
assert filtered_data.repository is not None, "Repository must not be None"
91+
assert data_filtered_by_release.repository is not None, "Repository must not be None"
8992

9093
rls_notes_records: dict[int | str, Record] = RecordFactory.generate(
91-
github=self._github_instance, data=filtered_data
94+
github=self._github_instance, data=data_filtered_by_release
9295
)
9396

9497
release_notes_builder = ReleaseNotesBuilder(

release_notes_generator/miner.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,12 @@ def mine_data(self) -> MinedData:
6868
data.commits = list(self._safe_call(data.repository.get_commits)())
6969

7070
logger.info("Data mining from GitHub completed.")
71-
return data
71+
72+
logger.info("Filtering duplicated issues from the list of issues...")
73+
de_duplicated_data = self.__filter_duplicated_issues(data)
74+
logger.info("Filtering duplicated issues from the list of issues finished.")
75+
76+
return de_duplicated_data
7277

7378
def get_latest_release(self, repository: Repository) -> Optional[GitRelease]:
7479
"""
@@ -154,3 +159,23 @@ def __get_latest_semantic_release(self, releases) -> Optional[GitRelease]:
154159
rls = release
155160

156161
return rls
162+
163+
def __filter_duplicated_issues(self, data: MinedData) -> "MinedData":
164+
"""
165+
Filters out duplicated issues from the list of issues.
166+
This method address problem in output of GitHub API where issues list contains PR values.
167+
168+
Parameters:
169+
- data (MinedData): The mined data containing issues and pull requests.
170+
171+
Returns:
172+
- MinedData: The mined data with duplicated issues removed.
173+
"""
174+
pr_numbers = {pr.number for pr in data.pull_requests}
175+
filtered_issues = [issue for issue in data.issues if issue.number not in pr_numbers]
176+
177+
logger.debug("Duplicated issues removed: %s", len(data.issues) - len(filtered_issues))
178+
179+
data.issues = filtered_issues
180+
181+
return data

release_notes_generator/record/record_factory.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ def generate(github: Github, data: MinedData) -> dict[int | str, Record]:
5858

5959
def register_pull_request(pull: PullRequest, skip_rec: bool) -> None:
6060
detected_issues = extract_issue_numbers_from_body(pull)
61-
detected_issues.extend(safe_call(get_issues_for_pr)(pull_number=pull.number))
61+
logger.debug(f"Detected issues - from body: {detected_issues}")
62+
detected_issues.update(safe_call(get_issues_for_pr)(pull_number=pull.number))
63+
logger.debug(f"Detected issues - final: {detected_issues}")
6264

6365
for parent_issue_number in detected_issues:
6466
# create an issue record if not present for PR parent
@@ -89,12 +91,10 @@ def register_pull_request(pull: PullRequest, skip_rec: bool) -> None:
8991
records: dict[int | str, Record] = {}
9092
rate_limiter = GithubRateLimiter(github)
9193
safe_call = safe_call_decorator(rate_limiter)
92-
pull_numbers = [pull.number for pull in data.pull_requests]
9394

9495
logger.debug("Registering issues to records...")
9596
for issue in data.issues:
96-
if issue.number not in pull_numbers:
97-
RecordFactory._create_record_for_issue(records, issue)
97+
RecordFactory._create_record_for_issue(records, issue)
9898

9999
logger.debug("Registering pull requests to records...")
100100
for pull in data.pull_requests:
@@ -105,6 +105,7 @@ def register_pull_request(pull: PullRequest, skip_rec: bool) -> None:
105105
records[pull.number] = PullRequestRecord(pull, skip=skip_record)
106106
logger.debug("Created record for PR %d: %s", pull.number, pull.title)
107107
else:
108+
logger.debug(f"Registering pull number: {pull.number}, title : {pull.title}")
108109
register_pull_request(pull, skip_record)
109110

110111
logger.debug("Registering commits to records...")

release_notes_generator/utils/pull_request_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030

3131
@lru_cache(maxsize=None)
32-
def extract_issue_numbers_from_body(pr: PullRequest) -> list[int]:
32+
def extract_issue_numbers_from_body(pr: PullRequest) -> set[int]:
3333
"""
3434
Extracts the numbers of the issues mentioned in the body of the pull request.
3535
@@ -43,7 +43,7 @@ def extract_issue_numbers_from_body(pr: PullRequest) -> list[int]:
4343
issue_matches = regex_pattern.findall(pr.body if pr.body else "")
4444

4545
# Extract the issue numbers from the matches
46-
issue_numbers = [int(match[-1]) for match in issue_matches]
46+
issue_numbers = {int(match[-1]) for match in issue_matches}
4747

4848
return issue_numbers
4949

tests/release_notes/test_record_factory.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ def test_generate_with_no_commits_with_wrong_issue_number_in_pull_body_mention(m
299299
data = MinedData()
300300
# pylint: disable=unused-variable
301301
issue1, issue2, pr1, pr2, commit1, commit2 = setup_issues_pulls_commits(mocker)
302-
pr1.body = "Closes #100"
302+
pr1.body = "Closes #2"
303303
data.issues = [issue1]
304304
data.pull_requests = [pr1] # PR linked to a non-fetched issues (due to since condition)
305305

@@ -320,8 +320,8 @@ def test_generate_with_no_commits_with_wrong_issue_number_in_pull_body_mention(m
320320
assert isinstance(records[1], IssueRecord)
321321
assert isinstance(records[2], IssueRecord)
322322

323-
rec_issue1 = cast(IssueRecord,records[1])
324-
rec_issue2 = cast(IssueRecord,records[2])
323+
rec_issue1 = cast(IssueRecord, records[1])
324+
rec_issue2 = cast(IssueRecord, records[2])
325325

326326
# Verify that PRs are registered
327327
assert 0 == rec_issue1.pull_requests_count()

tests/test_filter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ def test_filter_with_release(mocker):
6565
MagicMock(closed_at=datetime(2022, 12, 31)),
6666
]
6767
data.pull_requests = [
68-
MagicMock(merged_at=datetime(2023, 2, 3)),
69-
MagicMock(merged_at=datetime(2022, 12, 30)),
68+
MagicMock(merged_at=datetime(2023, 2, 3), closed_at=datetime(2022, 12, 31)),
69+
MagicMock(merged_at=datetime(2022, 12, 30), closed_at=datetime(2022, 12, 31)),
7070
]
7171
data.commits = [
7272
MagicMock(commit=MagicMock(author=MagicMock(date=datetime(2024, 1, 4)))),

tests/test_release_notes_generator.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def test_generate_release_notes_repository_not_found(mocker):
3939

4040
custom_chapters = CustomChapters(print_empty_chapters=True)
4141

42-
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate(filterer=FilterByRelease())
42+
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate()
4343

4444
assert release_notes is None
4545

@@ -74,7 +74,7 @@ def test_generate_release_notes_latest_release_not_found(
7474

7575
custom_chapters = CustomChapters(print_empty_chapters=True)
7676

77-
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate(filterer=FilterByRelease())
77+
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate()
7878

7979
assert release_notes is not None
8080
assert "- #121 _Fix the bug_" in release_notes
@@ -105,14 +105,15 @@ def test_generate_release_notes_latest_release_found_by_created_at(
105105
mock_issue_closed_i1_bug.created_at = mock_repo.created_at + timedelta(days=7)
106106
mock_issue_closed_i1_bug.closed_at = mock_repo.created_at + timedelta(days=6)
107107
mock_pull_closed_with_rls_notes_101.merged_at = mock_repo.created_at + timedelta(days=2)
108+
mock_pull_closed_with_rls_notes_101.closed_at = mock_repo.created_at + timedelta(days=2)
108109
mock_pull_closed_with_rls_notes_102.merged_at = mock_repo.created_at + timedelta(days=7)
110+
mock_pull_closed_with_rls_notes_102.closed_at = mock_repo.created_at + timedelta(days=7)
109111

110112
mock_git_release.created_at = mock_repo.created_at + timedelta(days=5)
111113
mock_git_release.published_at = mock_repo.created_at + timedelta(days=5)
112114
mocker.patch("release_notes_generator.miner.DataMiner.get_latest_release", return_value=mock_git_release)
113115
mocker.patch("release_notes_generator.record.record_factory.get_issues_for_pr", return_value=[])
114116

115-
116117
mock_rate_limit = mocker.Mock()
117118
mock_rate_limit.core.remaining = 1000
118119
github_mock.get_rate_limit.return_value = mock_rate_limit
@@ -124,7 +125,7 @@ def test_generate_release_notes_latest_release_found_by_created_at(
124125

125126
custom_chapters = CustomChapters(print_empty_chapters=True)
126127

127-
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate(filterer=FilterByRelease())
128+
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate()
128129

129130
assert release_notes is not None
130131
assert "- #122 _I1+bug_" in release_notes
@@ -155,7 +156,9 @@ def test_generate_release_notes_latest_release_found_by_published_at(
155156
mock_issue_closed_i1_bug.created_at = mock_repo.created_at + timedelta(days=7)
156157
mock_issue_closed_i1_bug.closed_at = mock_repo.created_at + timedelta(days=8)
157158
mock_pull_closed_with_rls_notes_101.merged_at = mock_repo.created_at + timedelta(days=2)
159+
mock_pull_closed_with_rls_notes_101.closed_at = mock_repo.created_at + timedelta(days=2)
158160
mock_pull_closed_with_rls_notes_102.merged_at = mock_repo.created_at + timedelta(days=7)
161+
mock_pull_closed_with_rls_notes_102.closed_at = mock_repo.created_at + timedelta(days=7)
159162

160163
github_mock.get_repo().get_latest_release.return_value = mock_git_release
161164
mock_git_release.created_at = mock_repo.created_at + timedelta(days=5)
@@ -170,7 +173,7 @@ def test_generate_release_notes_latest_release_found_by_published_at(
170173

171174
custom_chapters = CustomChapters(print_empty_chapters=True)
172175

173-
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate(filterer=FilterByRelease())
176+
release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate()
174177

175178
assert release_notes is not None
176179
assert "- #122 _I1+bug_" in release_notes

tests/utils/test_pull_reuqest_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,35 +48,35 @@ def test_extract_issue_numbers_from_body_no_issues(mocker):
4848
mock_pr = mocker.Mock(spec=PullRequest)
4949
mock_pr.body = "This PR does not fix any issues."
5050
issue_numbers = extract_issue_numbers_from_body(mock_pr)
51-
assert issue_numbers == []
51+
assert issue_numbers == set()
5252

5353

5454
def test_extract_issue_numbers_from_body_single_issue(mocker):
5555
mock_pr = mocker.Mock(spec=PullRequest)
5656
mock_pr.body = "This PR closes #123."
5757
issue_numbers = extract_issue_numbers_from_body(mock_pr)
58-
assert issue_numbers == [123]
58+
assert issue_numbers == {123}
5959

6060

6161
def test_extract_issue_numbers_from_body_multiple_issues(mocker):
6262
mock_pr = mocker.Mock(spec=PullRequest)
6363
mock_pr.body = "This PR fixes #123 and resolves #456."
6464
issue_numbers = extract_issue_numbers_from_body(mock_pr)
65-
assert issue_numbers == [123, 456]
65+
assert issue_numbers == {123, 456}
6666

6767

6868
def test_extract_issue_numbers_from_body_mixed_case_keywords(mocker):
6969
mock_pr = mocker.Mock(spec=PullRequest)
7070
mock_pr.body = "This PR Fixes #123 and Resolves #456."
7171
issue_numbers = extract_issue_numbers_from_body(mock_pr)
72-
assert issue_numbers == [123, 456]
72+
assert issue_numbers == {123, 456}
7373

7474

7575
def test_extract_issue_numbers_from_body_no_body(mocker):
7676
mock_pr = mocker.Mock(spec=PullRequest)
7777
mock_pr.body = None
7878
issue_numbers = extract_issue_numbers_from_body(mock_pr)
79-
assert issue_numbers == []
79+
assert issue_numbers == set()
8080

8181

8282
def test_extract_issue_numbers_from_body_complex_text_with_wrong_syntax(mocker):
@@ -88,7 +88,7 @@ def test_extract_issue_numbers_from_body_complex_text_with_wrong_syntax(mocker):
8888
- resolves the bug in #789
8989
"""
9090
issue_numbers = extract_issue_numbers_from_body(mock_pr)
91-
assert issue_numbers == [123]
91+
assert issue_numbers == {123}
9292

9393

9494
# get_issues_for_pr

0 commit comments

Comments
 (0)