Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not fail when unknown placeholder received #115

Merged
merged 7 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
53 changes: 48 additions & 5 deletions release_notes_generator/action_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import logging
import os
import sys
import re

from release_notes_generator.utils.constants import (
GITHUB_REPOSITORY,
Expand All @@ -39,10 +40,10 @@
ROW_FORMAT_LINK_PR,
ROW_FORMAT_ISSUE,
ROW_FORMAT_PR,
SUPPORTED_ROW_FORMAT_KEYS,
)
from release_notes_generator.utils.enums import DuplicityScopeEnum
from release_notes_generator.utils.gh_action import get_action_input
from release_notes_generator.utils.utils import detect_row_format_invalid_keywords

logger = logging.getLogger(__name__)

Expand All @@ -53,6 +54,10 @@ class ActionInputs:
A class representing the inputs provided to the GH action.
"""

_row_format_issue = None
_row_format_pr = None
_row_format_link_pr = None

@staticmethod
def get_github_repository() -> str:
"""
Expand Down Expand Up @@ -159,14 +164,22 @@ def get_row_format_issue() -> str:
"""
Get the issue row format for the release notes.
"""
return get_action_input(ROW_FORMAT_ISSUE, "{number} _{title}_ in {pull-requests}").strip()
if ActionInputs._row_format_issue is None:
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved
ActionInputs._row_format_issue = ActionInputs._clean_row_format_invalid_keywords(
get_action_input(ROW_FORMAT_ISSUE, "{number} _{title}_ in {pull-requests}").strip()
)
return ActionInputs._row_format_issue

@staticmethod
def get_row_format_pr() -> str:
"""
Get the pr row format for the release notes.
"""
return get_action_input(ROW_FORMAT_PR, "{number} _{title}_").strip()
if ActionInputs._row_format_pr is None:
ActionInputs._row_format_pr = ActionInputs._clean_row_format_invalid_keywords(
get_action_input(ROW_FORMAT_PR, "{number} _{title}_").strip()
)
return ActionInputs._row_format_pr

@staticmethod
def get_row_format_link_pr() -> bool:
Expand Down Expand Up @@ -223,13 +236,13 @@ def validate_inputs():
if not isinstance(row_format_issue, str) or not row_format_issue.strip():
errors.append("Issue row format must be a non-empty string.")

errors.extend(detect_row_format_invalid_keywords(row_format_issue))
ActionInputs._detect_row_format_invalid_keywords(row_format_issue)

row_format_pr = ActionInputs.get_row_format_pr()
if not isinstance(row_format_pr, str) or not row_format_pr.strip():
errors.append("PR Row format must be a non-empty string.")

errors.extend(detect_row_format_invalid_keywords(row_format_pr, row_type="PR"))
ActionInputs._detect_row_format_invalid_keywords(row_format_pr, row_type="PR")

row_format_link_pr = ActionInputs.get_row_format_link_pr()
ActionInputs.validate_input(row_format_link_pr, bool, "'row-format-link-pr' value must be a boolean.", errors)
Expand All @@ -252,3 +265,33 @@ def validate_inputs():
logger.debug("Verbose logging: %s", verbose)
logger.debug("Warnings: %s", warnings)
logger.debug("Print empty chapters: %s", print_empty_chapters)

@staticmethod
def _detect_row_format_invalid_keywords(row_format: str, row_type: str = "Issue") -> None:
"""
Detects invalid keywords in the row format.

@param row_format: The row format to be checked for invalid keywords.
@param row_type: The type of row format. Default is "Issue".
@return: None
"""
keywords_in_braces = re.findall(r"\{(.*?)\}", row_format)
invalid_keywords = [keyword for keyword in keywords_in_braces if keyword not in SUPPORTED_ROW_FORMAT_KEYS]
for invalid_keyword in invalid_keywords:
logger.error(
f"Invalid `{invalid_keyword}` detected in `{row_type}` row format keyword(s) found: {', '.join(invalid_keywords)}. Will be removed from string."
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved
)

@staticmethod
def _clean_row_format_invalid_keywords(row_format: str) -> str:
"""
Detects and clean invalid keywords in the row format.

@param row_format: The row format to be checked for invalid keywords.
@return: The cleaned row format.
"""
keywords_in_braces = re.findall(r"\{(.*?)\}", row_format)
invalid_keywords = [keyword for keyword in keywords_in_braces if keyword not in SUPPORTED_ROW_FORMAT_KEYS]
for invalid_keyword in invalid_keywords:
row_format = row_format.replace(f"{{{invalid_keyword}}}", "")
return row_format
16 changes: 0 additions & 16 deletions release_notes_generator/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,3 @@ def get_change_url(
changelog_url = f"https://github.com/{repo.full_name}/compare/{rls.tag_name}...{tag_name}"

return changelog_url


def detect_row_format_invalid_keywords(row_format: str, row_type: str = "Issue") -> list[str]:
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved
"""
Detects invalid keywords in the row format.

@param row_format: The row format to be checked for invalid keywords.
@param row_type: The type of row format. Default is "Issue".
@return: A list of errors if invalid keywords are found, otherwise an empty list.
"""
errors = []
keywords_in_braces = re.findall(r"\{(.*?)\}", row_format)
invalid_keywords = [keyword for keyword in keywords_in_braces if keyword not in SUPPORTED_ROW_FORMAT_KEYS]
if invalid_keywords:
errors.append(f"Invalid {row_type} row format keyword(s) found: {', '.join(invalid_keywords)}")
return errors
37 changes: 37 additions & 0 deletions tests/test_action_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
import logging

import pytest

Expand Down Expand Up @@ -44,6 +45,8 @@
("get_verbose", "not_bool", "Verbose logging must be a boolean."),
("get_duplicity_icon", "", "Duplicity icon must be a non-empty string and have a length of 1."),
("get_duplicity_icon", "Oj", "Duplicity icon must be a non-empty string and have a length of 1."),
("get_row_format_issue", "", "Issue row format must be a non-empty string."),
("get_row_format_pr", "", "PR Row format must be a non-empty string."),
]


Expand Down Expand Up @@ -157,3 +160,37 @@ def test_get_duplicity_scope_wrong_value(mocker):

assert ActionInputs.get_duplicity_scope() == "BOTH"
mock_error.assert_called_with("Error: '%s' is not a valid DuplicityType.", "HUH")


def test_detect_row_format_invalid_keywords_no_invalid_keywords(caplog):
caplog.set_level(logging.ERROR)
row_format = "{number} _{title}_ in {pull-requests}"
ActionInputs._detect_row_format_invalid_keywords(row_format)
assert len(caplog.records) == 0


def test_detect_row_format_invalid_keywords_with_invalid_keywords(caplog):
caplog.set_level(logging.ERROR)
row_format = "{number} _{title}_ in {pull-requests} {invalid_key} {another_invalid}"
ActionInputs._detect_row_format_invalid_keywords(row_format)
assert len(caplog.records) == 2
expected_errors = [
"Invalid `invalid_key` detected in `Issue` row format keyword(s) found: invalid_key, another_invalid. Will be removed from string.",
"Invalid `another_invalid` detected in `Issue` row format keyword(s) found: invalid_key, another_invalid. Will be removed from string."
]
actual_errors = [record.getMessage() for record in caplog.records]
assert actual_errors == expected_errors


def test_clean_row_format_invalid_keywords_no_keywords():
row_format = "{number} _{title}_ in {pull-requests}"
cleaned_format = ActionInputs._clean_row_format_invalid_keywords(row_format)
assert cleaned_format == row_format
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved


def test_clean_row_format_invalid_keywords_nested_braces():
row_format = "{number} _{title}_ in {pull-requests} {invalid_key} {another_invalid}"
expected_format = "{number} _{title}_ in {pull-requests} "
cleaned_format = ActionInputs._clean_row_format_invalid_keywords(row_format)
assert cleaned_format == expected_format
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved

6 changes: 6 additions & 0 deletions tests/test_release_notes_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from release_notes_generator.generator import ReleaseNotesGenerator
from release_notes_generator.model.custom_chapters import CustomChapters
from release_notes_generator.utils.constants import ROW_FORMAT_ISSUE


# generate_release_notes tests
Expand Down Expand Up @@ -111,6 +112,11 @@ def test_generate_release_notes_latest_release_found_by_created_at(
mock_rate_limit.core.remaining = 1000
github_mock.get_rate_limit.return_value = mock_rate_limit

mock_get_action_input = mocker.patch("release_notes_generator.utils.gh_action.get_action_input")
mock_get_action_input.side_effect = lambda first_arg, **kwargs: (
"{number} _{title}_ in {pull-requests} {unknown} {another-unknown}" if first_arg == ROW_FORMAT_ISSUE else None
)

custom_chapters = CustomChapters(print_empty_chapters=True)

release_notes = ReleaseNotesGenerator(github_mock, custom_chapters).generate()
Expand Down
20 changes: 2 additions & 18 deletions tests/utils/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#

from release_notes_generator.utils.utils import get_change_url, detect_row_format_invalid_keywords
from release_notes_generator.action_inputs import ActionInputs
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved
from release_notes_generator.utils.utils import get_change_url


# get_change_url
Expand All @@ -33,19 +33,3 @@ def test_get_change_url_no_git_release(mock_repo):
def test_get_change_url_with_git_release(mock_repo, mock_git_release):
url = get_change_url(tag_name="v2.0.0", repository=mock_repo, git_release=mock_git_release)
assert url == "https://github.com/org/repo/compare/v1.0.0...v2.0.0"


# detect_row_format_invalid_keywords


def test_valid_row_format():
row_format = "{number} - {title} in {pull-requests}"
errors = detect_row_format_invalid_keywords(row_format)
assert not errors, "Expected no errors for valid keywords"


def test_multiple_invalid_keywords():
row_format = "{number} - {link} - {Title} and {Pull-requests}"
errors = detect_row_format_invalid_keywords(row_format)
assert len(errors) == 1
assert "Invalid Issue row format keyword(s) found: link, Title, Pull-requests" in errors[0]