Skip to content

Commit 2fd72de

Browse files
#174 - Add logic for fetching cross-repo sub issues's PRs (#186)
* #174 - Add logic for fetching cross-repo sub issues's PRs - Added mining of Pull Requests for cross-repo issues.
1 parent 41deabb commit 2fd72de

File tree

4 files changed

+47
-4
lines changed

4 files changed

+47
-4
lines changed

release_notes_generator/data/miner.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from github import Github
2828
from github.GitRelease import GitRelease
2929
from github.Issue import Issue
30+
from github.PullRequest import PullRequest
3031
from github.Repository import Repository
3132

3233
from release_notes_generator.action_inputs import ActionInputs
@@ -83,19 +84,25 @@ def mine_data(self) -> MinedData:
8384

8485
return de_duplicated_data
8586

86-
def mine_missing_sub_issues(self, data: MinedData) -> dict[Issue, Repository]:
87+
def mine_missing_sub_issues(self, data: MinedData) -> tuple[dict[Issue, Repository], dict[str, list[PullRequest]]]:
8788
"""
8889
Mines missing sub-issues from GitHub.
8990
Parameters:
9091
data (MinedData): The mined data containing origin sets of issues and pull requests.
9192
Returns:
9293
dict[Issue, Repository]: A dictionary mapping fetched issues to their repositories.
94+
dict[str, list[PullRequest]]: A dictionary mapping fetched cross-repo issue with its pull requests.
9395
"""
9496
logger.info("Mapping sub-issues...")
9597
data.parents_sub_issues = self._scan_sub_issues_for_parents([get_id(i, r) for i, r in data.issues.items()])
9698

9799
logger.info("Fetching missing issues...")
98-
return self._fetch_missing_issues_and_prs(data)
100+
fetched_issues = self._fetch_missing_issues(data)
101+
102+
logger.info("Getting PRs and Commits for missing issues...")
103+
prs_of_fetched_cross_repo_issues = self._fetch_prs_for_fetched_cross_issues(fetched_issues)
104+
105+
return fetched_issues, prs_of_fetched_cross_repo_issues
99106

100107
def _scan_sub_issues_for_parents(self, parents_to_check: list[str]) -> dict[str, list[str]]:
101108
"""
@@ -119,7 +126,7 @@ def _scan_sub_issues_for_parents(self, parents_to_check: list[str]) -> dict[str,
119126

120127
return parents_sub_issues
121128

122-
def _fetch_missing_issues_and_prs(self, data: MinedData) -> dict[Issue, Repository]:
129+
def _fetch_missing_issues(self, data: MinedData) -> dict[Issue, Repository]:
123130
"""
124131
Fetch missing issues.
125132
@@ -167,6 +174,8 @@ def _fetch_missing_issues_and_prs(self, data: MinedData) -> dict[Issue, Reposito
167174
else:
168175
logger.debug("Skipping issue %s since it does not meet criteria.", parent_id)
169176
issues_for_remove.append(parent_id)
177+
else:
178+
logger.error("Cannot get repository for issue %s. Skipping...", parent_id)
170179

171180
# remove issue which does not meet criteria
172181
for iid in issues_for_remove:
@@ -350,3 +359,20 @@ def __filter_duplicated_issues(data: MinedData) -> "MinedData":
350359

351360
data.issues = filtered_issues
352361
return data
362+
363+
def _fetch_prs_for_fetched_cross_issues(self, issues: dict[Issue, Repository]) -> dict[str, list[PullRequest]]:
364+
prs_of_cross_repo_issues: dict[str, list[PullRequest]] = {}
365+
for i, repo in issues.items():
366+
prs_of_cross_repo_issues[iid := get_id(i, repo)] = []
367+
try:
368+
for ev in i.get_timeline(): # timeline includes cross-references
369+
if ev.event == "cross-referenced" and getattr(ev, "source", None):
370+
# <- this is a github.Issue.Issue
371+
src_issue = ev.source.issue # type: ignore[union-attr]
372+
if getattr(src_issue, "pull_request", None):
373+
pr = src_issue.as_pull_request() # github.PullRequest.PullRequest
374+
prs_of_cross_repo_issues[iid].append(pr)
375+
except Exception as e:
376+
logger.warning("Failed to fetch timeline events for issue %s: %s", iid, str(e))
377+
378+
return prs_of_cross_repo_issues

release_notes_generator/generator.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ def generate(self) -> Optional[str]:
9191

9292
# data expansion when hierarchy is enabled
9393
if ActionInputs.get_hierarchy():
94-
data_filtered_by_release.issues.update(miner.mine_missing_sub_issues(data_filtered_by_release))
94+
fetched_issues, prs_of_fetched_issues = miner.mine_missing_sub_issues(data_filtered_by_release)
95+
data_filtered_by_release.issues.update(fetched_issues)
96+
data_filtered_by_release.pull_requests_of_fetched_cross_issues = prs_of_fetched_issues
9597
else:
9698
# fill flat structure with empty lists, no hierarchy
9799
for i, repo in data_filtered_by_release.issues.items():

release_notes_generator/model/mined_data.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ def __init__(self, repository: Repository):
5050
self.commits: dict[Commit, Repository] = {}
5151

5252
self.parents_sub_issues: dict[str, list[str]] = {} # parent issue id -> list of its sub-issues ids
53+
# dictionary of fetched cross issues and their pull requests
54+
self.pull_requests_of_fetched_cross_issues: dict[str, list[PullRequest]] = {}
5355

5456
@property
5557
def home_repository(self) -> Repository:

release_notes_generator/record/factory/default_record_factory.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ def generate(self, data: MinedData) -> dict[str, Record]:
9797
for pull, repo in data.pull_requests.items():
9898
self._register_pull_and_its_commits_to_issue(pull, get_id(pull, repo), data, target_repository=repo)
9999

100+
if data.pull_requests_of_fetched_cross_issues:
101+
logger.debug("Register cross-repo Pull Requests to its issues")
102+
for iid, prs in data.pull_requests_of_fetched_cross_issues.items():
103+
self._register_cross_repo_prs_to_issue(iid, prs)
104+
100105
logger.debug("Registering direct commits to records...")
101106
for commit, repo in data.commits.items():
102107
if commit.sha not in self.__registered_commits:
@@ -180,6 +185,14 @@ def _register_pull_and_its_commits_to_issue(
180185
self._records[pid] = pr_rec
181186
logger.debug("Created record for PR %s: %s", pid, pull.title)
182187

188+
def _register_cross_repo_prs_to_issue(self, iid: str, prs: list[PullRequest]) -> None:
189+
if iid not in self.__registered_issues:
190+
logger.error("Issue '%s' not found among collected records.", iid)
191+
return
192+
193+
for pr in prs:
194+
cast(IssueRecord, self._records[iid]).register_pull_request(pr)
195+
183196
def _create_record_for_hierarchy_issue(self, i: Issue, iid: str, issue_labels: Optional[list[str]] = None) -> None:
184197
"""
185198
Create a hierarchy issue record and register sub-issues.

0 commit comments

Comments
 (0)