Skip to content

Commit 7bc48b1

Browse files
committed
Fixed problem with multilevel issues.
1 parent 6875a1f commit 7bc48b1

12 files changed

Lines changed: 122 additions & 36 deletions

File tree

release_notes_generator/chapters/custom_chapters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def populate(self, records: dict[str, Record]) -> None:
6868

6969
for record_label in records[record_id].labels: # iterate all labels of the record (issue, or 1st PR)
7070
if record_label in ch.labels and pulls_count > 0:
71-
if not records[record_id].is_present_in_chapters:
71+
if not records[record_id].is_present_in_chapters and records[record_id].contains_change_increment():
7272
ch.add_row(record_id, records[record_id].to_chapter_row(True))
7373
self.populated_record_numbers_list.append(record_id)
7474

release_notes_generator/filter.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,27 @@ def filter(self, data: MinedData) -> MinedData:
107107
md.pull_requests = deepcopy(data.pull_requests)
108108
md.commits = deepcopy(data.commits)
109109

110+
print("debug filtering")
111+
print(f"XXX md issues before {len(md.issues)}")
112+
113+
for issue in data.issues:
114+
# if issue.number not in [3689,3688,3687,3686,3674,3620,3580,2960,3606,248,198,199,200,201,202,203]:
115+
if issue.number not in [3689,3688,3687,3686,3674,3620,3580,2960,3606]:
116+
if issue in md.issues:
117+
md.issues.remove(issue)
118+
119+
print(f"XXX md issues after {len(md.issues)}")
120+
121+
md.pull_requests = []
122+
for pull in data.pull_requests:
123+
if pull.number == 3616:
124+
md.pull_requests.append(pull)
125+
126+
# for commit in data.commits:
127+
# if commit.sha in []:
128+
# md.commits.remove(commit)
129+
md.commits = []
130+
110131
return md
111132

112133
def _filter_issues(self, data: MinedData) -> list:

release_notes_generator/model/commit_record.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str:
6565

6666
return row
6767

68+
def contains_change_increment(self) -> bool:
69+
return True
70+
6871
def get_rls_notes(self, line_marks: Optional[list[str]] = None) -> str:
6972
# Hint: direct commits does not support release notes
7073
return ""

release_notes_generator/model/hierarchy_issue_record.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str:
120120

121121
# add sub-hierarchy issues
122122
for sub_hierarchy_issue in self._sub_hierarchy_issues.values():
123-
row = f"{row}\n{sub_hierarchy_issue.to_chapter_row()}"
123+
if sub_hierarchy_issue.contains_change_increment():
124+
row = f"{row}\n{sub_hierarchy_issue.to_chapter_row()}"
124125

125126
# add sub-issues
126127
if len(self._sub_issues) > 0:
@@ -129,6 +130,9 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str:
129130
if sub_issue.is_open:
130131
continue # only closed issues are reported in release notes
131132

133+
if not sub_issue.contains_change_increment():
134+
continue # skip sub-issues without change increment
135+
132136
sub_issue_block = "- " + sub_issue.to_chapter_row()
133137
ind_child_block = "\n".join(
134138
f"{sub_indent}{line}" if line else "" for line in sub_issue_block.splitlines()

release_notes_generator/model/issue_record.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str:
122122

123123
return row
124124

125+
def contains_change_increment(self) -> bool:
126+
return self.pull_requests_count() > 0
127+
125128
def get_rls_notes(self, line_marks: Optional[list[str]] = None) -> str:
126129
release_notes = ""
127130
detection_pattern = ActionInputs.get_release_notes_title()

release_notes_generator/model/pull_request_record.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str:
128128
row = f"{row}\n{self.get_rls_notes()}"
129129
return row
130130

131+
def contains_change_increment(self) -> bool:
132+
return True
133+
131134
def get_rls_notes(self, line_marks: Optional[list[str]] = None) -> str:
132135
release_notes = ""
133136
detection_pattern = ActionInputs.get_release_notes_title()

release_notes_generator/model/record.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str:
117117
str: The string representation of the record in a chapter row.
118118
"""
119119

120+
@abstractmethod
121+
def contains_change_increment(self) -> bool:
122+
"""
123+
Checks if the record contains a change increment.
124+
125+
Returns:
126+
bool: True if the record contains a change increment, False otherwise.
127+
"""
128+
120129
@abstractmethod
121130
def get_labels(self) -> list[str]:
122131
"""

release_notes_generator/record/factory/default_record_factory.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"""
2020

2121
import logging
22-
from functools import singledispatchmethod
22+
from functools import singledispatchmethod, lru_cache
2323
from typing import cast, Optional
2424

2525
from github import Github
@@ -68,7 +68,13 @@ def get_id(self, obj) -> str:
6868

6969
@get_id.register
7070
def _(self, issue: Issue) -> str:
71-
return f"{issue.repository.full_name}#{issue.number}"
71+
# delegate to a cached, hashable-only helper
72+
return self._issue_id(issue.repository.full_name, issue.number)
73+
74+
@staticmethod
75+
@lru_cache(maxsize=2048)
76+
def _issue_id(repo_full_name: str, number: int) -> str:
77+
return f"{repo_full_name.lower()}#{number}"
7278

7379
@get_id.register
7480
def _(self, pull_request: PullRequest) -> str:

release_notes_generator/record/factory/issue_hierarchy_record_factory.py

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"""
2020

2121
import logging
22-
from datetime import datetime
2322
from typing import cast, Optional
2423

2524
from github import Github
@@ -52,7 +51,9 @@ def __init__(self, github: Github) -> None:
5251
self.__registered_issues: set[str] = set()
5352
self.__sub_issue_parents: dict[str, str] = {}
5453
self.__registered_commits: set[str] = set()
54+
5555
self.__external_sub_issues: list[SubIssue] = []
56+
self.__local_sub_issues_checked: list[str] = []
5657

5758
def generate(self, data: MinedData) -> dict[str, Record]:
5859
"""
@@ -64,15 +65,38 @@ def generate(self, data: MinedData) -> dict[str, Record]:
6465
"""
6566
logger.debug("Creation of records started...")
6667
# First register all issues with sub-issues
68+
issues_expansion: list[SubIssue] = []
6769
for issue in data.issues:
6870
if self.get_id(issue) in self.__registered_issues:
6971
continue
7072

71-
self._create_issue_record_using_sub_issues_existence(issue, data.since)
73+
issues_expansion.extend(self._create_issue_record_using_sub_issues_existence(issue, data))
74+
75+
data.issues.extend(issues_expansion)
76+
77+
# Second register all hierarchy issues from sub-issues
78+
registered_before = -1
79+
while registered_before < len(self.__registered_issues):
80+
registered_before = len(self.__registered_issues)
81+
logger.debug(f"Looking for hierarchical issue among sub-issues...")
82+
83+
issues_expansion = []
84+
for issue in data.issues:
85+
iid = self.get_id(issue)
86+
if iid in self.__registered_issues:
87+
continue
7288

73-
# Second register all external sub-issues
74-
# TODO decide if added as issue or sub-issue
89+
if iid in self.__sub_issue_parents.keys() and iid not in self.__local_sub_issues_checked:
90+
issues_expansion.extend(self._create_issue_record_using_sub_issues_existence(issue, data))
91+
self.__local_sub_issues_checked.append(iid)
92+
93+
data.issues.extend(issues_expansion)
94+
95+
# Third register all external sub-issues
7596
for ext_sub_issue in self.__external_sub_issues:
97+
if self.get_id(ext_sub_issue) in self.__registered_issues:
98+
continue
99+
76100
self._create_record_for_sub_issue(ext_sub_issue)
77101

78102
# Now register all issues without sub-issues
@@ -132,7 +156,7 @@ def _register_pull_and_its_commits_to_issue(self, pull: PullRequest, data: Mined
132156
issue_number = int(issue_id.split("#")[1])
133157
parent_issue = self._safe_call(data.repository.get_issue)(issue_number) if data.repository else None
134158
if parent_issue is not None:
135-
self._create_issue_record_using_sub_issues_existence(parent_issue)
159+
self._create_issue_record_using_sub_issues_existence(parent_issue, data)
136160

137161
if issue_id in self._records and isinstance(
138162
self._records[issue_id], (SubIssueRecord, HierarchyIssueRecord, IssueRecord)
@@ -154,19 +178,22 @@ def _register_pull_and_its_commits_to_issue(self, pull: PullRequest, data: Mined
154178
self._records[str(pull.number)] = pr_rec
155179
logger.debug("Created record for PR %d: %s", pull.number, pull.title)
156180

157-
def _create_issue_record_using_sub_issues_existence(self, issue: Issue, since: Optional[datetime] = None) -> None:
181+
def _create_issue_record_using_sub_issues_existence(self, issue: Issue, data: MinedData) -> list[SubIssue]:
158182
# use presence of sub-issues as a hint for hierarchy issue or non hierarchy issue
159183
sub_issues = list(issue.get_sub_issues())
184+
logger.debug(f"Found {len(sub_issues)} sub-issues for {issue.number}")
185+
new_local_issues: list[SubIssue] = []
160186

161187
if len(sub_issues) > 0:
162188
self._create_record_for_hierarchy_issue(issue)
163189
for si in sub_issues:
190+
siid = self.get_id(si)
191+
164192
# check if sub-issue is from current repository
165193
if si.repository.full_name != issue.repository.full_name:
166194
# register sub-issue and its parent for later hierarchy building
167-
self.__sub_issue_parents[self.get_id(si)] = self.get_id(
168-
issue
169-
) # Note: GitHub now allows only 1 parent
195+
# Note: GitHub now allows only 1 parent
196+
self.__sub_issue_parents[siid] = self.get_id(issue)
170197

171198
self.__external_sub_issues.append(si)
172199
logger.debug(
@@ -177,21 +204,33 @@ def _create_issue_record_using_sub_issues_existence(self, issue: Issue, since: O
177204
)
178205

179206
else:
180-
self.__sub_issue_parents[self.get_id(si)] = self.get_id(
181-
issue
182-
) # Note: GitHub now allows only 1 parent
183-
if since and si.state == IssueRecord.ISSUE_STATE_CLOSED and si.closed_at and since > si.closed_at:
184-
logger.debug("Detected sub-issue %d closed in previous release - skipping", si.number)
185-
continue
186-
187-
if si.state == IssueRecord.ISSUE_STATE_OPEN:
188-
logger.debug("Detected sub-issue %d is still open - skipping", si.number)
189-
continue
190-
191-
if si.state == IssueRecord.ISSUE_STATE_CLOSED: # issue is valid
192-
continue
193-
194-
logger.warning("Detected unexpected sub-issue %d with parent %d", si.number, issue.number)
207+
use_issue = False
208+
if data.since and si.state == IssueRecord.ISSUE_STATE_CLOSED and si.closed_at and data.since > si.closed_at:
209+
logger.debug("Detected sub-issue %d closed in previous release.", si.number)
210+
if len(list(si.get_sub_issues())) > 0:
211+
use_issue = True
212+
else:
213+
self.__registered_issues.add(siid)
214+
215+
elif si.state == IssueRecord.ISSUE_STATE_OPEN:
216+
logger.debug("Detected sub-issue %d is still open.", si.number)
217+
if len(list(si.get_sub_issues())) > 0:
218+
use_issue = True
219+
else:
220+
self.__registered_issues.add(siid)
221+
222+
elif si.state == IssueRecord.ISSUE_STATE_CLOSED: # issue is valid
223+
use_issue = True
224+
225+
else:
226+
logger.warning("Detected unexpected sub-issue %d with parent %d", si.number, issue.number)
227+
228+
if use_issue:
229+
self.__sub_issue_parents[siid] = self.get_id(issue)
230+
if si not in data.issues:
231+
new_local_issues.append(si)
232+
233+
return new_local_issues
195234

196235
def _create_issue_record_using_sub_issues_not_existence(self, issue: Issue) -> None:
197236
# Expected to run after all issue with sub-issues are registered
@@ -249,6 +288,8 @@ def _create_record_for_sub_issue(self, issue: Issue, issue_labels: Optional[list
249288
self._records[iid] = SubIssueRecord(issue, issue_labels, skip_record)
250289

251290
def _re_register_hierarchy_issues(self):
291+
logger.debug("Re-registering hierarchy issues ...")
292+
252293
sub_issues_ids: list[str] = list(self.__sub_issue_parents.keys())
253294

254295
made_progress = False

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,7 @@ def mined_data_isolated_record_types_with_labels_with_type_defined(mocker, mined
834834
t_epic = mocker.Mock(spec=IssueType)
835835
t_epic.name = "Epic"
836836
t_feature = mocker.Mock(spec=IssueType)
837-
t_feature.name = "feature"
837+
t_feature.name = "Feature"
838838
t_task = mocker.Mock(spec=IssueType)
839839
t_task.name = "Task"
840840
t_bug = mocker.Mock(spec=IssueType)

0 commit comments

Comments
 (0)