Skip to content

Commit

Permalink
#57 - Configurable regex-based Release note detection in the PR body
Browse files Browse the repository at this point in the history
- Release notes title can be defined by user.
- Release notes title can be defined by regex.
  • Loading branch information
miroslavpojer committed Nov 20, 2024
1 parent 66e52bd commit 1db14f8
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 13 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ Generate Release Notes action is dedicated to enhance the quality and organizati
- **Default**: false
- **Note**: If workflow run in debug regime, 'verbose' logging is activated.

### `release-notes-title`
- **Description**: The title of the release notes section in the PR description.
- **Required**: No
- **Default**: `[Rr]elease [Nn]otes:`

### Feature controls

### `warnings`
Expand Down Expand Up @@ -159,6 +164,7 @@ Add the following step to your GitHub workflow (in example are used non-default
published-at: true
skip-release-notes-label: 'ignore-in-release' # changing default value of label
verbose: false
release-notes-title: '[Rr]elease Notes:'

warnings: false
print-empty-chapters: false
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ inputs:
description: 'Print verbose logs.'
required: false
default: 'false'
release-notes-title:
description: 'The title of the release notes section in the PR body. Value supports regex.'
required: false
default: '[Rr]elease [Nn]otes:'
row-format-issue:
description: 'Format of the issue row in the release notes. Available placeholders: {number}, {title}, {pull-requests}. Placeholders are case-insensitive.'
required: false
Expand Down Expand Up @@ -118,6 +122,7 @@ runs:
INPUT_SKIP_RELEASE_NOTES_LABEL: ${{ inputs.skip-release-notes-label }}
INPUT_PRINT_EMPTY_CHAPTERS: ${{ inputs.print-empty-chapters }}
INPUT_VERBOSE: ${{ inputs.verbose }}
INPUT_RELEASE_NOTES_TITLE: ${{ inputs.release-notes-title }}
INPUT_GITHUB_REPOSITORY: ${{ github.repository }}
INPUT_ROW_FORMAT_ISSUE: ${{ inputs.row-format-issue }}
INPUT_ROW_FORMAT_PR: ${{ inputs.row-format-pr }}
Expand Down
13 changes: 13 additions & 0 deletions release_notes_generator/action_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
ROW_FORMAT_LINK_PR,
ROW_FORMAT_ISSUE,
ROW_FORMAT_PR,
RELEASE_NOTES_TITLE,
RELEASE_NOTE_TITLE_DEFAULT,
)
from release_notes_generator.utils.enums import DuplicityScopeEnum
from release_notes_generator.utils.gh_action import get_action_input
Expand Down Expand Up @@ -122,6 +124,13 @@ def get_verbose() -> bool:
"""
return os.getenv(RUNNER_DEBUG, "0") == "1" or get_action_input(VERBOSE).lower() == "true"

@staticmethod
def get_release_notes_title() -> str:
"""
Get the release notes title from the action inputs.
"""
return get_action_input(RELEASE_NOTES_TITLE, RELEASE_NOTE_TITLE_DEFAULT)

# Features
@staticmethod
def get_warnings() -> bool:
Expand Down Expand Up @@ -219,6 +228,10 @@ def validate_inputs():
verbose = ActionInputs.get_verbose()
ActionInputs.validate_input(verbose, bool, "Verbose logging must be a boolean.", errors)

release_notes_title = ActionInputs.get_release_notes_title()
if not isinstance(release_notes_title, str) or len(release_notes_title) == 0:
errors.append("Release Notes title must be a non-empty string and have non-zero length.")

row_format_issue = ActionInputs.get_row_format_issue()
if not isinstance(row_format_issue, str) or not row_format_issue.strip():
errors.append("Issue row format must be a non-empty string.")
Expand Down
21 changes: 10 additions & 11 deletions release_notes_generator/model/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"""

import logging
import re

from typing import Optional

from github.Issue import Issue
Expand All @@ -31,7 +33,6 @@
PR_STATE_CLOSED,
ISSUE_STATE_CLOSED,
ISSUE_STATE_OPEN,
RELEASE_NOTE_DETECTION_PATTERN,
RELEASE_NOTE_LINE_MARKS,
)
from release_notes_generator.utils.pull_reuqest_utils import extract_issue_numbers_from_body
Expand Down Expand Up @@ -126,28 +127,26 @@ def labels(self) -> list[str]:

return [label.name for label in self.__gh_issue.labels]

# TODO in Issue named 'Configurable regex-based Release note detection in the PR body'
# - 'Release notest:' as detection pattern default - can be defined by user
# - '-' as leading line mark for each release note to be used
def get_rls_notes(
self, detection_pattern=RELEASE_NOTE_DETECTION_PATTERN, line_marks=RELEASE_NOTE_LINE_MARKS
) -> str:
def get_rls_notes(self, detection_pattern: str, line_marks: str = RELEASE_NOTE_LINE_MARKS) -> str:
"""
Gets the release notes of the record.
@param detection_pattern: The detection pattern to use.
@param detection_pattern: The detection pattern (regex allowed) to use.
@param line_marks: The line marks to use.
@return: The release notes of the record as a string.
"""
release_notes = ""

# Compile the regex pattern for efficiency
detection_regex = re.compile(detection_pattern)

# Iterate over all PRs
for pull in self.__pulls:
body_lines = pull.body.split("\n") if pull.body is not None else []
inside_release_notes = False

for line in body_lines:
if detection_pattern in line:
if detection_regex.search(line): # Use regex search
inside_release_notes = True
continue

Expand All @@ -167,7 +166,7 @@ def contains_release_notes(self) -> bool:
if self.__is_release_note_detected:
return self.__is_release_note_detected

rls_notes: str = self.get_rls_notes()
rls_notes: str = self.get_rls_notes(detection_pattern=ActionInputs.get_release_notes_title())
if any(mark in rls_notes for mark in RELEASE_NOTE_LINE_MARKS):
self.__is_release_note_detected = True

Expand Down Expand Up @@ -298,7 +297,7 @@ def to_chapter_row(self) -> str:
row = f"{row_prefix}" + ActionInputs.get_row_format_issue().format(**format_values)

if self.contains_release_notes:
row = f"{row}\n{self.get_rls_notes()}"
row = f"{row}\n{self.get_rls_notes(detection_pattern=ActionInputs.get_release_notes_title())}"

return row

Expand Down
3 changes: 2 additions & 1 deletion release_notes_generator/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
PUBLISHED_AT = "published-at"
SKIP_RELEASE_NOTES_LABEL = "skip-release-notes-label"
VERBOSE = "verbose"
RELEASE_NOTES_TITLE = "release-notes-title"
RUNNER_DEBUG = "RUNNER_DEBUG"
ROW_FORMAT_ISSUE = "row-format-issue"
ROW_FORMAT_PR = "row-format-pr"
Expand All @@ -48,7 +49,7 @@
ISSUE_STATE_ALL = "all"

# Release notes comment constants
RELEASE_NOTE_DETECTION_PATTERN = "Release Notes:"
RELEASE_NOTE_TITLE_DEFAULT = "[Rr]elease [Nn]otes:"
RELEASE_NOTE_LINE_MARKS = ["-", "*", "+"]

# Service chapters titles
Expand Down
6 changes: 5 additions & 1 deletion tests/release_notes/model/test_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from github.Commit import Commit

from release_notes_generator.action_inputs import ActionInputs
from release_notes_generator.utils.constants import ISSUE_STATE_CLOSED, PR_STATE_CLOSED


Expand Down Expand Up @@ -67,7 +68,10 @@ def test_record_properties_authors_contributors(record_with_no_issue_one_pull_cl

def test_get_rls_notes(record_with_no_issue_one_pull_closed):
expected_notes = " - Fixed bug\n - Improved performance\n + More nice code\n * Awesome architecture"
assert record_with_no_issue_one_pull_closed.get_rls_notes() == expected_notes
assert record_with_no_issue_one_pull_closed.get_rls_notes(detection_pattern=ActionInputs.get_release_notes_title()) == expected_notes

def test_get_rls_notes_not_detected(record_with_no_issue_one_pull_closed):
assert '' == record_with_no_issue_one_pull_closed.get_rls_notes(detection_pattern="XXX")


# contains_release_notes
Expand Down

0 comments on commit 1db14f8

Please sign in to comment.