Skip to content

Commit adeae67

Browse files
#57 - Configurable regex-based Release note detection in the PR body (#112)
* #57 - Configurable regex-based Release note detection in the PR body - Release notes title can be defined by user. - Release notes title can be defined by regex.
1 parent ad368d7 commit adeae67

File tree

6 files changed

+46
-16
lines changed

6 files changed

+46
-16
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ Generate Release Notes action is dedicated to enhance the quality and organizati
9999
- **Default**: false
100100
- **Note**: If workflow run in debug regime, 'verbose' logging is activated.
101101

102+
### `release-notes-title`
103+
- **Description**: The title of the release notes section in the PR description.
104+
- **Required**: No
105+
- **Default**: `[Rr]elease [Nn]otes:`
106+
102107
### Feature controls
103108

104109
### `warnings`
@@ -163,6 +168,7 @@ Add the following step to your GitHub workflow (in example are used non-default
163168
published-at: true
164169
skip-release-notes-labels: 'ignore-in-release' # changing default value of label
165170
verbose: false
171+
release-notes-title: '[Rr]elease Notes:'
166172

167173
warnings: false
168174
print-empty-chapters: false

action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ inputs:
5151
description: 'Print verbose logs.'
5252
required: false
5353
default: 'false'
54+
release-notes-title:
55+
description: 'The title of the release notes section in the PR body. Value supports regex.'
56+
required: false
57+
default: '[Rr]elease [Nn]otes:'
5458
row-format-issue:
5559
description: 'Format of the issue row in the release notes. Available placeholders: {number}, {title}, {pull-requests}. Placeholders are case-insensitive.'
5660
required: false
@@ -118,6 +122,7 @@ runs:
118122
INPUT_SKIP_RELEASE_NOTES_LABELS: ${{ inputs.skip-release-notes-labels }}
119123
INPUT_PRINT_EMPTY_CHAPTERS: ${{ inputs.print-empty-chapters }}
120124
INPUT_VERBOSE: ${{ inputs.verbose }}
125+
INPUT_RELEASE_NOTES_TITLE: ${{ inputs.release-notes-title }}
121126
INPUT_GITHUB_REPOSITORY: ${{ github.repository }}
122127
INPUT_ROW_FORMAT_ISSUE: ${{ inputs.row-format-issue }}
123128
INPUT_ROW_FORMAT_PR: ${{ inputs.row-format-pr }}

release_notes_generator/action_inputs.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
ROW_FORMAT_ISSUE,
4040
ROW_FORMAT_PR,
4141
SKIP_RELEASE_NOTES_LABELS,
42+
RELEASE_NOTES_TITLE,
43+
RELEASE_NOTE_TITLE_DEFAULT,
4244
)
4345
from release_notes_generator.utils.enums import DuplicityScopeEnum
4446
from release_notes_generator.utils.gh_action import get_action_input
@@ -109,7 +111,7 @@ def get_published_at() -> bool:
109111
return get_action_input(PUBLISHED_AT, "false").lower() == "true"
110112

111113
@staticmethod
112-
def get_skip_release_notes_labels() -> list[str]:
114+
def get_skip_release_notes_labels() -> str:
113115
"""
114116
Get the skip release notes label from the action inputs.
115117
"""
@@ -125,6 +127,13 @@ def get_verbose() -> bool:
125127
"""
126128
return os.getenv(RUNNER_DEBUG, "0") == "1" or get_action_input(VERBOSE).lower() == "true"
127129

130+
@staticmethod
131+
def get_release_notes_title() -> str:
132+
"""
133+
Get the release notes title from the action inputs.
134+
"""
135+
return get_action_input(RELEASE_NOTES_TITLE, RELEASE_NOTE_TITLE_DEFAULT)
136+
128137
# Features
129138
@staticmethod
130139
def get_warnings() -> bool:
@@ -179,10 +188,11 @@ def get_row_format_link_pr() -> bool:
179188
return get_action_input(ROW_FORMAT_LINK_PR, "true").lower() == "true"
180189

181190
@staticmethod
182-
def validate_inputs():
191+
def validate_inputs() -> None:
183192
"""
184193
Validates the inputs provided for the release notes generator.
185194
Logs any validation errors and exits if any are found.
195+
@return: None
186196
"""
187197
errors = []
188198

@@ -218,6 +228,10 @@ def validate_inputs():
218228
verbose = ActionInputs.get_verbose()
219229
ActionInputs.validate_input(verbose, bool, "Verbose logging must be a boolean.", errors)
220230

231+
release_notes_title = ActionInputs.get_release_notes_title()
232+
if not isinstance(release_notes_title, str) or len(release_notes_title) == 0:
233+
errors.append("Release Notes title must be a non-empty string and have non-zero length.")
234+
221235
row_format_issue = ActionInputs.get_row_format_issue()
222236
if not isinstance(row_format_issue, str) or not row_format_issue.strip():
223237
errors.append("Issue row format must be a non-empty string.")
@@ -243,11 +257,12 @@ def validate_inputs():
243257
logger.error(error)
244258
sys.exit(1)
245259

246-
logging.debug("Repository: %s/%s", owner, repo_name)
260+
logger.debug("Repository: %s/%s", owner, repo_name)
247261
logger.debug("Tag name: %s", tag_name)
248262
logger.debug("Chapters JSON: %s", chapters_json)
249263
logger.debug("Published at: %s", published_at)
250264
logger.debug("Skip release notes labels: %s", ActionInputs.get_skip_release_notes_labels())
251265
logger.debug("Verbose logging: %s", verbose)
252266
logger.debug("Warnings: %s", warnings)
253267
logger.debug("Print empty chapters: %s", print_empty_chapters)
268+
logger.debug("Release notes title: %s", release_notes_title)

release_notes_generator/model/record.py

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

2121
import logging
22+
import re
23+
2224
from typing import Optional
2325

2426
from github.Issue import Issue
@@ -31,7 +33,6 @@
3133
PR_STATE_CLOSED,
3234
ISSUE_STATE_CLOSED,
3335
ISSUE_STATE_OPEN,
34-
RELEASE_NOTE_DETECTION_PATTERN,
3536
RELEASE_NOTE_LINE_MARKS,
3637
)
3738
from release_notes_generator.utils.pull_reuqest_utils import extract_issue_numbers_from_body
@@ -132,28 +133,26 @@ def labels(self) -> list[str]:
132133

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

135-
# TODO in Issue named 'Configurable regex-based Release note detection in the PR body'
136-
# - 'Release notest:' as detection pattern default - can be defined by user
137-
# - '-' as leading line mark for each release note to be used
138-
def get_rls_notes(
139-
self, detection_pattern=RELEASE_NOTE_DETECTION_PATTERN, line_marks=RELEASE_NOTE_LINE_MARKS
140-
) -> str:
136+
def get_rls_notes(self, detection_pattern: str, line_marks: str = RELEASE_NOTE_LINE_MARKS) -> str:
141137
"""
142138
Gets the release notes of the record.
143139
144-
@param detection_pattern: The detection pattern to use.
140+
@param detection_pattern: The detection pattern (regex allowed) to use.
145141
@param line_marks: The line marks to use.
146142
@return: The release notes of the record as a string.
147143
"""
148144
release_notes = ""
149145

146+
# Compile the regex pattern for efficiency
147+
detection_regex = re.compile(detection_pattern)
148+
150149
# Iterate over all PRs
151150
for pull in self.__pulls:
152151
body_lines = pull.body.split("\n") if pull.body is not None else []
153152
inside_release_notes = False
154153

155154
for line in body_lines:
156-
if detection_pattern in line:
155+
if detection_regex.search(line): # Use regex search
157156
inside_release_notes = True
158157
continue
159158

@@ -173,7 +172,7 @@ def contains_release_notes(self) -> bool:
173172
if self.__is_release_note_detected:
174173
return self.__is_release_note_detected
175174

176-
rls_notes: str = self.get_rls_notes()
175+
rls_notes: str = self.get_rls_notes(detection_pattern=ActionInputs.get_release_notes_title())
177176
if any(mark in rls_notes for mark in RELEASE_NOTE_LINE_MARKS):
178177
self.__is_release_note_detected = True
179178

@@ -304,7 +303,7 @@ def to_chapter_row(self) -> str:
304303
row = f"{row_prefix}" + ActionInputs.get_row_format_issue().format(**format_values)
305304

306305
if self.contains_release_notes:
307-
row = f"{row}\n{self.get_rls_notes()}"
306+
row = f"{row}\n{self.get_rls_notes(detection_pattern=ActionInputs.get_release_notes_title())}"
308307

309308
return row
310309

release_notes_generator/utils/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
PUBLISHED_AT = "published-at"
2929
SKIP_RELEASE_NOTES_LABELS = "skip-release-notes-labels"
3030
VERBOSE = "verbose"
31+
RELEASE_NOTES_TITLE = "release-notes-title"
3132
RUNNER_DEBUG = "RUNNER_DEBUG"
3233
ROW_FORMAT_ISSUE = "row-format-issue"
3334
ROW_FORMAT_PR = "row-format-pr"
@@ -48,7 +49,7 @@
4849
ISSUE_STATE_ALL = "all"
4950

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

5455
# Service chapters titles

tests/release_notes/model/test_record.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from github.Commit import Commit
2020

21+
from release_notes_generator.action_inputs import ActionInputs
2122
from release_notes_generator.utils.constants import ISSUE_STATE_CLOSED, PR_STATE_CLOSED
2223

2324

@@ -67,7 +68,10 @@ def test_record_properties_authors_contributors(record_with_no_issue_one_pull_cl
6768

6869
def test_get_rls_notes(record_with_no_issue_one_pull_closed):
6970
expected_notes = " - Fixed bug\n - Improved performance\n + More nice code\n * Awesome architecture"
70-
assert record_with_no_issue_one_pull_closed.get_rls_notes() == expected_notes
71+
assert record_with_no_issue_one_pull_closed.get_rls_notes(detection_pattern=ActionInputs.get_release_notes_title()) == expected_notes
72+
73+
def test_get_rls_notes_not_detected(record_with_no_issue_one_pull_closed):
74+
assert '' == record_with_no_issue_one_pull_closed.get_rls_notes(detection_pattern="XXX")
7175

7276

7377
# contains_release_notes

0 commit comments

Comments
 (0)