Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ inputs:
required: false
default: ''
row-format-hierarchy-issue:
description: 'Format of the hierarchy issue in the release notes. Available placeholders: {number}, {title}, {type}. Placeholders are case-insensitive.'
description: 'Format of the hierarchy issue in the release notes. Available placeholders: {type}, {number}, {title}. Placeholders are case-insensitive.'
required: false
default: '{type}: _{title}_ {number}'
row-format-issue:
description: 'Format of the issue row in the release notes. Available placeholders: {number}, {title}, {pull-requests}. Placeholders are case-insensitive.'
description: 'Format of the issue row in the release notes. Available placeholders: {type}, {number}, {title}, {pull-requests}. Placeholders are case-insensitive.'
required: false
default: '{number} _{title}_ in {pull-requests}'
default: '{type}: {number} _{title}_ in {pull-requests}'
row-format-pr:
description: 'Format of the pr row in the release notes. Available placeholders: {number}, {title}. Placeholders are case-insensitive.'
required: false
Expand Down
1 change: 0 additions & 1 deletion integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

class MissingTokenError(ValueError):
"""Raised when GITHUB_TOKEN environment variable is not set."""
pass

token = os.getenv("GITHUB_TOKEN")
if token is None:
Expand Down
4 changes: 3 additions & 1 deletion release_notes_generator/action_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def get_row_format_issue() -> str:
if ActionInputs._row_format_issue is None:
ActionInputs._row_format_issue = ActionInputs._detect_row_format_invalid_keywords(
get_action_input(
ROW_FORMAT_ISSUE, "{number} _{title}_ in {pull-requests}"
ROW_FORMAT_ISSUE, "{type}: {number} _{title}_ in {pull-requests}"
).strip(), # type: ignore[union-attr]
clean=True,
# mypy: string is returned as default
Expand Down Expand Up @@ -463,6 +463,8 @@ def validate_inputs() -> None:
logger.debug("Tag name: %s", tag_name)
logger.debug("From tag name: %s", from_tag_name)
logger.debug("Chapters: %s", chapters)
logger.debug("Duplicity scope: %s", ActionInputs.get_duplicity_scope())
logger.debug("Duplicity icon: %s", ActionInputs.get_duplicity_icon())
logger.debug("Hierarchy: %s", hierarchy)
logger.debug("Published at: %s", published_at)
logger.debug("Skip release notes labels: %s", ActionInputs.get_skip_release_notes_labels())
Expand Down
29 changes: 11 additions & 18 deletions release_notes_generator/chapters/custom_chapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@
notes.
"""
import logging
from typing import cast

from release_notes_generator.action_inputs import ActionInputs
from release_notes_generator.chapters.base_chapters import BaseChapters
from release_notes_generator.model.chapter import Chapter
from release_notes_generator.model.commit_record import CommitRecord
from release_notes_generator.model.hierarchy_issue_record import HierarchyIssueRecord
from release_notes_generator.model.issue_record import IssueRecord
from release_notes_generator.model.record import Record
from release_notes_generator.model.sub_issue_record import SubIssueRecord
from release_notes_generator.utils.enums import DuplicityScopeEnum

logger = logging.getLogger(__name__)
Expand All @@ -50,6 +46,9 @@ def populate(self, records: dict[str, Record]) -> None:
None
"""
for record_id, record in records.items(): # iterate all records
if not records[record_id].contains_change_increment():
continue

# check if the record should be skipped
if records[record_id].skip:
continue
Expand All @@ -65,25 +64,19 @@ def populate(self, records: dict[str, Record]) -> None:
):
continue

pulls_count = 1
if isinstance(records[record_id], (HierarchyIssueRecord, IssueRecord, SubIssueRecord)):
pulls_count = cast(IssueRecord, records[record_id]).pull_requests_count()

for record_label in records[record_id].labels: # iterate all labels of the record (issue, or 1st PR)
if record_label in ch.labels and pulls_count > 0:
if (
not records[record_id].is_present_in_chapters
and records[record_id].contains_change_increment()
):
if record_label in ch.labels:
if records[record_id].is_present_in_chapters:
allow_dup = ActionInputs.get_duplicity_scope() in (
DuplicityScopeEnum.CUSTOM,
DuplicityScopeEnum.BOTH,
)
if (allow_dup or not records[record_id].is_present_in_chapters) and records[
record_id
].contains_change_increment():
ch.add_row(record_id, records[record_id].to_chapter_row(True))
self.populated_record_numbers_list.append(record_id)
if not allow_dup:
continue

if record_id not in ch.rows.keys():
ch.add_row(record_id, records[record_id].to_chapter_row(True))
self.populated_record_numbers_list.append(record_id)

def from_yaml_array(self, chapters: list[dict[str, str]]) -> "CustomChapters":
"""
Expand Down
45 changes: 25 additions & 20 deletions release_notes_generator/data/miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from github import Github
from github.GitRelease import GitRelease
from github.Issue import Issue
from github.PullRequest import PullRequest
from github.Repository import Repository

from release_notes_generator.action_inputs import ActionInputs
Expand Down Expand Up @@ -69,9 +68,6 @@ def mine_data(self) -> MinedData:
pull_requests = list(
self._safe_call(repo.get_pulls)(state=PullRequestRecord.PR_STATE_CLOSED, base=repo.default_branch)
)
open_pull_requests = list(
self._safe_call(repo.get_pulls)(state=PullRequestRecord.PR_STATE_OPEN, base=repo.default_branch)
)
data.pull_requests = {pr: data.home_repository for pr in pull_requests}
if data.since:
commits = list(self._safe_call(repo.get_commits)(since=data.since))
Expand All @@ -82,7 +78,7 @@ def mine_data(self) -> MinedData:
logger.info("Initial data mining from GitHub completed.")

logger.info("Filtering duplicated issues from the list of issues...")
de_duplicated_data = self.__filter_duplicated_issues(data, open_pull_requests)
de_duplicated_data = self.__filter_duplicated_issues(data)
logger.info("Filtering duplicated issues from the list of issues finished.")

return de_duplicated_data
Expand Down Expand Up @@ -135,6 +131,7 @@ def _fetch_missing_issues_and_prs(self, data: MinedData) -> dict[Issue, Reposito
fetched_issues: dict[Issue, Repository] = {}

origin_issue_ids = {get_id(i, r) for i, r in data.issues.items()}
issues_for_remove: list[str] = []
for parent_id in data.parents_sub_issues.keys():
if parent_id in origin_issue_ids:
continue
Expand All @@ -151,15 +148,32 @@ def _fetch_missing_issues_and_prs(self, data: MinedData) -> dict[Issue, Reposito
issue = None
r = data.get_repository(f"{org}/{repo}")
if r is not None:
logger.debug("Fetching missing issue: %s", parent_id)
issue = self._safe_call(r.get_issue)(num)
if issue is None:
logger.error("Issue not found: %s", parent_id)
continue

logger.debug("Fetching missing issue: %s", parent_id)

# add to issues list
fetched_issues[issue] = r
fetch: bool = True
if not issue.closed_at:
fetch = False
elif data.since:
if issue.closed_at and data.since > issue.closed_at:
fetch = False

if fetch:
# add to issues list
fetched_issues[issue] = r
else:
logger.debug("Skipping issue %s since it does not meet criteria.", parent_id)
issues_for_remove.append(parent_id)

# remove issue which does not meet criteria
for iid in issues_for_remove:
data.parents_sub_issues.pop(iid, None)
for sub_issues in data.parents_sub_issues.values():
if iid in sub_issues:
sub_issues.remove(iid)

logger.debug("Fetched %d missing issues.", len(fetched_issues))
return fetched_issues
Expand Down Expand Up @@ -319,29 +333,20 @@ def __get_latest_semantic_release(releases) -> Optional[GitRelease]:
return rls

@staticmethod
def __filter_duplicated_issues(data: MinedData, open_pull_requests: list[PullRequest]) -> "MinedData":
def __filter_duplicated_issues(data: MinedData) -> "MinedData":
"""
Filters out duplicated issues from the list of issues.
This method address problem in output of GitHub API where issues list contains PR values.

Parameters:
data (MinedData): The mined data containing issues and pull requests.
open_pull_requests (list[PullRequest]): List of currently open pull requests.

Returns:
MinedData: The mined data with duplicated issues removed.
"""
pr_numbers = {pr.number for pr in data.pull_requests.keys()}
open_pr_numbers = [pr.number for pr in open_pull_requests]

filtered_issues = {
issue: repo
for issue, repo in data.issues.items()
if issue.number not in pr_numbers and issue.number not in open_pr_numbers
}
filtered_issues = {issue: repo for issue, repo in data.issues.items() if "/issues/" in issue.html_url}

logger.debug("Duplicated issues removed: %s", len(data.issues.items()) - len(filtered_issues.items()))

data.issues = filtered_issues

return data
28 changes: 6 additions & 22 deletions release_notes_generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from typing import Optional

from github import Github
from github.Repository import Repository

from release_notes_generator.data.filter import FilterByRelease
from release_notes_generator.data.miner import DataMiner
Expand All @@ -33,8 +32,8 @@
from release_notes_generator.chapters.custom_chapters import CustomChapters
from release_notes_generator.model.record import Record
from release_notes_generator.record.factory.default_record_factory import DefaultRecordFactory
from release_notes_generator.record.factory.issue_hierarchy_record_factory import IssueHierarchyRecordFactory
from release_notes_generator.utils.github_rate_limiter import GithubRateLimiter
from release_notes_generator.utils.record_utils import get_id
from release_notes_generator.utils.utils import get_change_url

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -93,6 +92,10 @@ def generate(self) -> Optional[str]:
# data expansion when hierarchy is enabled
if ActionInputs.get_hierarchy():
data_filtered_by_release.issues.update(miner.mine_missing_sub_issues(data_filtered_by_release))
else:
# fill flat structure with empty lists, no hierarchy
for i, repo in data_filtered_by_release.issues.items():
data_filtered_by_release.parents_sub_issues[get_id(i, repo)] = []

changelog_url: str = get_change_url(
tag_name=ActionInputs.get_tag_name(),
Expand All @@ -102,7 +105,7 @@ def generate(self) -> Optional[str]:

assert data_filtered_by_release.home_repository is not None, "Repository must not be None"

rls_notes_records: dict[str, Record] = self._get_record_factory(
rls_notes_records: dict[str, Record] = DefaultRecordFactory(
github=self._github_instance, home_repository=data_filtered_by_release.home_repository
).generate(data=data_filtered_by_release)

Expand All @@ -111,22 +114,3 @@ def generate(self) -> Optional[str]:
custom_chapters=self._custom_chapters,
changelog_url=changelog_url,
).build()

@staticmethod
def _get_record_factory(github: Github, home_repository: Repository) -> DefaultRecordFactory:
"""
Determines and returns the appropriate RecordFactory instance based on the action inputs.

Parameters:
github (GitHub): An instance of the GitHub class.
home_repository (Repository): The home repository for which records are to be generated.

Returns:
DefaultRecordFactory: An instance of either IssueHierarchyRecordFactory or RecordFactory.
"""
if ActionInputs.get_hierarchy():
logger.info("Using IssueHierarchyRecordFactory based on action inputs.")
return IssueHierarchyRecordFactory(github, home_repository)

logger.info("Using default RecordFactory based on action inputs.")
return DefaultRecordFactory(github, home_repository)
1 change: 1 addition & 0 deletions release_notes_generator/model/issue_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str:
format_values: dict[str, Any] = {}

# collect format values
format_values["type"] = f"{self._issue.type.name if self._issue.type else 'N/A'}"
format_values["number"] = f"#{self._issue.number}"
format_values["title"] = self._issue.title
list_pr_links = self.get_pr_links()
Expand Down
Loading
Loading