Skip to content

Commit 828c5c7

Browse files
Add Service chapter visibility control (#62)
#52 - Add logic to control the visibility of Service chapters - Clean up of action controls to manage service chapters visibility. - Grouped "features" controls. - Simplified the code using static ActionInputs. - Introduced defaults values in ActionInputs and env variable access methods. - Updated unit tests.
1 parent d659632 commit 828c5c7

File tree

9 files changed

+215
-148
lines changed

9 files changed

+215
-148
lines changed

README.md

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,6 @@ Generate Release Notes action is dedicated to enhance the quality and organizati
4343
- **Description**: A JSON string defining chapters and corresponding labels for categorization. Each chapter should have a title and a label matching your GitHub issues and PRs.
4444
- **Required**: Yes
4545

46-
### `warnings`
47-
- **Description**: Set to true to enable warnings in the release notes. These warnings identify issues without release notes, without user-defined labels, or without associated pull requests, and PRs without linked issues.
48-
- **Required**: No
49-
- **Default**: false
50-
5146
### `published-at`
5247
- **Description**: Set to true to enable the use of the `published-at` timestamp as the reference point for searching closed issues and PRs, instead of the `created-at` date of the latest release.
5348
- **Required**: No
@@ -58,21 +53,29 @@ Generate Release Notes action is dedicated to enhance the quality and organizati
5853
- **Required**: No
5954
- **Default**: `skip-release-notes`
6055

61-
### `print-empty-chapters`
62-
- **Description**: Set to true to print chapters with no issues or PRs.
56+
### `verbose`
57+
- **Description**: Set to true to enable verbose logging for detailed output during the action's execution.
6358
- **Required**: No
6459
- **Default**: false
60+
- **Note**: If workflow run in debug regime, 'verbose' logging is activated.
6561

66-
### `chapters-to-pr-without-issue`
67-
- **Description**: Set to false to avoid application of custom chapters for PRs without linked issues.
62+
### Feature controls
63+
64+
### `warnings`
65+
- **Description**: Set to true to print service chapters in the release notes. These warnings identify issues without release notes, without user-defined labels, or without associated pull requests, and PRs without linked issues.
6866
- **Required**: No
69-
- **Default**: true
67+
- **Default**: true (Service chapters are printed.)
7068

71-
### `verbose`
72-
- **Description**: Set to true to enable verbose logging for detailed output during the action's execution.
69+
### `print-empty-chapters`
70+
- **Description**: Set it to true to print chapters with no issues or PRs.
7371
- **Required**: No
74-
- **Default**: false
75-
- **Note**: If workflow run in debug regime, 'verbose' logging is activated.
72+
- **Default**: true (Empty chapters are printed.)
73+
74+
### `chapters-to-pr-without-issue`
75+
- **Description**: Set it to false to avoid the application of custom chapters for PRs without linked issues.
76+
- **Required**: No
77+
- **Default**: true (Custom chapters are applied to PRs without linked issues.)
78+
7679

7780
## Outputs
7881
The output of the action is a markdown string containing the release notes for the specified tag. This string can be used in subsequent steps to publish the release notes to a file, create a GitHub release, or send notifications.
@@ -120,12 +123,13 @@ Add the following step to your GitHub workflow (in example are used non-default
120123
{"title": "New Features 🎉", "label": "feature"},
121124
{"title": "Bugfixes 🛠", "label": "bug"}
122125
]'
123-
warnings: false
124126
published-at: true
125127
skip-release-notes-label: 'ignore-in-release' # changing default value of label
128+
verbose: false
129+
130+
warnings: false
126131
print-empty-chapters: false
127132
chapters-to-pr-without-issue: false
128-
verbose: false
129133
```
130134
131135
## Features
@@ -261,7 +265,7 @@ export GITHUB_REPOSITORY="< owner >/< repo-name >"
261265
export INPUT_GITHUB_TOKEN=$(printenv <your-env-token-var>)
262266
263267
# Run the Python script
264-
python3 /<path-to-action-project-root>/main.py
268+
python3 ./<path-to-action-project-root>/main.py
265269
```
266270

267271
### Contribution Guidelines

action.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ inputs:
2323
chapters:
2424
description: 'JSON string defining chapters and corresponding labels for categorization.'
2525
required: false
26-
warnings:
27-
description: 'Print warning chapters if true.'
28-
required: false
29-
default: 'true'
3026
published-at:
3127
description: 'Use `published-at` timestamp instead of `created-at` as the reference point.'
3228
required: false
@@ -35,6 +31,10 @@ inputs:
3531
description: 'Skip label used for detection of issues or pull requests to ignore in Release Notes generation process.'
3632
required: false
3733
default: 'skip-release-notes'
34+
warnings:
35+
description: 'Print service chapters if true.'
36+
required: false
37+
default: 'true'
3838
print-empty-chapters:
3939
description: 'Print chapters even if they are empty.'
4040
required: false

release_notes_generator/action_inputs.py

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import json
1818
import logging
1919
import os
20+
import sys
2021

2122
from release_notes_generator.utils.gh_action import get_action_input
2223

@@ -26,14 +27,16 @@ class ActionInputs:
2627
GITHUB_TOKEN = 'github-token'
2728
TAG_NAME = 'tag-name'
2829
CHAPTERS = 'chapters'
29-
WARNINGS = 'warnings'
3030
PUBLISHED_AT = 'published-at'
3131
SKIP_RELEASE_NOTES_LABEL = 'skip-release-notes-label'
32-
PRINT_EMPTY_CHAPTERS = 'print-empty-chapters'
33-
CHAPTERS_TO_PR_WITHOUT_ISSUE = 'chapters-to-pr-without-issue'
3432
VERBOSE = 'verbose'
3533
RUNNER_DEBUG = 'RUNNER_DEBUG'
3634

35+
# Features
36+
WARNINGS = 'warnings'
37+
PRINT_EMPTY_CHAPTERS = 'print-empty-chapters'
38+
CHAPTERS_TO_PR_WITHOUT_ISSUE = 'chapters-to-pr-without-issue'
39+
3740
@staticmethod
3841
def get_github_repository() -> str:
3942
return get_action_input(ActionInputs.GITHUB_REPOSITORY)
@@ -50,89 +53,95 @@ def get_tag_name() -> str:
5053
def get_chapters_json() -> str:
5154
return get_action_input(ActionInputs.CHAPTERS)
5255

53-
@staticmethod
54-
def get_warnings() -> bool:
55-
return get_action_input(ActionInputs.WARNINGS).lower() == 'true'
56-
5756
@staticmethod
5857
def get_published_at() -> bool:
59-
return get_action_input(ActionInputs.PUBLISHED_AT).lower() == 'true'
58+
return get_action_input(ActionInputs.PUBLISHED_AT, "false").lower() == 'true'
6059

6160
@staticmethod
6261
def get_skip_release_notes_label() -> str:
6362
return get_action_input(ActionInputs.SKIP_RELEASE_NOTES_LABEL) or 'skip-release-notes'
6463

6564
@staticmethod
66-
def get_print_empty_chapters() -> bool:
67-
return get_action_input(ActionInputs.PRINT_EMPTY_CHAPTERS).lower() == 'true'
65+
def get_verbose() -> bool:
66+
return os.getenv('RUNNER_DEBUG', 0) == 1 or get_action_input(ActionInputs.VERBOSE).lower() == 'true'
6867

68+
# Features
6969
@staticmethod
70-
def get_chapters_to_pr_without_issue() -> bool:
71-
return get_action_input(ActionInputs.CHAPTERS_TO_PR_WITHOUT_ISSUE).lower() == 'true'
70+
def get_warnings() -> bool:
71+
return get_action_input(ActionInputs.WARNINGS, "true").lower() == 'true'
7272

7373
@staticmethod
74-
def get_verbose() -> bool:
75-
return os.getenv('RUNNER_DEBUG', 0) == 1 or get_action_input(ActionInputs.VERBOSE).lower() == 'true'
74+
def get_print_empty_chapters() -> bool:
75+
return get_action_input(ActionInputs.PRINT_EMPTY_CHAPTERS, "true").lower() == 'true'
76+
77+
@staticmethod
78+
def get_chapters_to_pr_without_issue() -> bool:
79+
return get_action_input(ActionInputs.CHAPTERS_TO_PR_WITHOUT_ISSUE, "true").lower() == 'true'
7680

7781
@staticmethod
7882
def validate_inputs():
7983
"""
8084
Validates the inputs provided for the release notes generator.
81-
82-
:raises ValueError: If any of the inputs are invalid.
85+
Logs any validation errors and exits if any are found.
8386
"""
87+
errors = []
88+
8489
repository_id = ActionInputs.get_github_repository()
8590
if '/' in repository_id:
8691
owner, repo_name = ActionInputs.get_github_repository().split('/')
8792
else:
8893
owner = repo_name = ""
8994

90-
if not isinstance(owner, str) or not owner.strip():
91-
raise ValueError("Owner must be a non-empty string.")
92-
93-
if not isinstance(repo_name, str) or not repo_name.strip():
94-
raise ValueError("Repo name must be a non-empty string.")
95+
if not isinstance(owner, str) or not owner.strip() or not isinstance(repo_name, str) or not repo_name.strip():
96+
errors.append("Owner and Repo must be a non-empty string.")
9597

9698
tag_name = ActionInputs.get_tag_name()
9799
if not isinstance(tag_name, str) or not tag_name.strip():
98-
raise ValueError("Tag name must be a non-empty string.")
100+
errors.append("Tag name must be a non-empty string.")
99101

102+
chapters_json = ActionInputs.get_chapters_json()
100103
try:
101-
chapters_json = ActionInputs.get_chapters_json()
102104
json.loads(chapters_json)
103105
except json.JSONDecodeError:
104-
raise ValueError("Chapters JSON must be a valid JSON string.")
106+
errors.append("Chapters JSON must be a valid JSON string.")
105107

106108
warnings = ActionInputs.get_warnings()
107109
if not isinstance(warnings, bool):
108-
raise ValueError("Warnings must be a boolean.")
110+
errors.append("Warnings must be a boolean.")
109111

110112
published_at = ActionInputs.get_published_at()
111113
if not isinstance(published_at, bool):
112-
raise ValueError("Published at must be a boolean.")
114+
errors.append("Published at must be a boolean.")
113115

114116
skip_release_notes_label = ActionInputs.get_skip_release_notes_label()
115117
if not isinstance(skip_release_notes_label, str) or not skip_release_notes_label.strip():
116-
raise ValueError("Skip release notes label must be a non-empty string.")
118+
errors.append("Skip release notes label must be a non-empty string.")
119+
120+
verbose = ActionInputs.get_verbose()
121+
if not isinstance(verbose, bool):
122+
errors.append("Verbose logging must be a boolean.")
117123

124+
# Features
118125
print_empty_chapters = ActionInputs.get_print_empty_chapters()
119126
if not isinstance(print_empty_chapters, bool):
120-
raise ValueError("Print empty chapters must be a boolean.")
127+
errors.append("Print empty chapters must be a boolean.")
121128

122129
chapters_to_pr_without_issue = ActionInputs.get_chapters_to_pr_without_issue()
123130
if not isinstance(chapters_to_pr_without_issue, bool):
124-
raise ValueError("Chapters to PR without issue must be a boolean.")
131+
errors.append("Chapters to PR without issue must be a boolean.")
125132

126-
verbose = ActionInputs.get_verbose()
127-
if not isinstance(verbose, bool):
128-
raise ValueError("Verbose logging must be a boolean.")
133+
# Log errors if any
134+
if errors:
135+
for error in errors:
136+
logging.error(error)
137+
sys.exit(1)
129138

130139
logging.debug(f'Repository: {owner}/{repo_name}')
131140
logging.debug(f'Tag name: {tag_name}')
132141
logging.debug(f'Chapters JSON: {chapters_json}')
133-
logging.debug(f'Warnings: {warnings}')
134142
logging.debug(f'Published at: {published_at}')
135143
logging.debug(f'Skip release notes label: {skip_release_notes_label}')
144+
logging.debug(f'Verbose logging: {verbose}')
145+
logging.debug(f'Warnings: {warnings}')
136146
logging.debug(f'Print empty chapters: {print_empty_chapters}')
137147
logging.debug(f'Chapters to PR without issue: {chapters_to_pr_without_issue}')
138-
logging.debug(f'Verbose logging: {verbose}')

release_notes_generator/builder.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,33 @@
2121
from release_notes_generator.model.custom_chapters import CustomChapters
2222
from release_notes_generator.model.record import Record
2323
from release_notes_generator.model.service_chapters import ServiceChapters
24+
from release_notes_generator.action_inputs import ActionInputs
2425

2526

2627
class ReleaseNotesBuilder:
2728
def __init__(self, records: dict[int, Record], changelog_url: str,
28-
formatter: RecordFormatter, custom_chapters: CustomChapters,
29-
warnings: bool = True, print_empty_chapters: bool = True):
29+
formatter: RecordFormatter, custom_chapters: CustomChapters):
3030
"""
31-
Constructs all the necessary attributes for the ReleaseNotesBuilder object.
31+
Init all the necessary attributes for the ReleaseNotesBuilder object.
3232
33-
:param records: A dictionary of records where the key is an integer and the value is a Record object.
34-
:param changelog_url: The URL of the changelog.
35-
:param formatter: A RecordFormatter object used to format the records.
36-
:param custom_chapters: A CustomChapters object representing the custom chapters.
37-
:param warnings: A boolean indicating whether to show warnings.
38-
:param print_empty_chapters: A boolean indicating whether to print empty chapters.
33+
@param records: A dictionary of records where the key is an integer and the value is a Record object.
34+
@param changelog_url: The URL of the changelog.
35+
@param formatter: A RecordFormatter object used to format the records.
36+
@param custom_chapters: A CustomChapters object representing the custom chapters.
3937
"""
4038
self.records = records
4139
self.changelog_url = changelog_url
4240
self.formatter = formatter
4341
self.custom_chapters = custom_chapters
44-
self.warnings = warnings
45-
self.print_empty_chapters = print_empty_chapters
42+
43+
self.warnings = ActionInputs.get_warnings()
44+
self.print_empty_chapters = ActionInputs.get_print_empty_chapters()
4645

4746
def build(self) -> str:
4847
"""
4948
Builds the release notes.
5049
51-
:return: The release notes as a string.
50+
@return: The release notes as a string.
5251
"""
5352
logging.info("Building Release Notes")
5453
user_defined_chapters = self.custom_chapters

release_notes_generator/generator.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import logging
1818

1919
from typing import Optional
20-
from github import Github, Auth
20+
from github import Github
2121

2222
from release_notes_generator.record.record_formatter import RecordFormatter
2323
from release_notes_generator.model.custom_chapters import CustomChapters
@@ -75,8 +75,6 @@ def generate(self) -> Optional[str]:
7575
records=rls_notes_records,
7676
custom_chapters=self.custom_chapters,
7777
formatter=RecordFormatter(),
78-
warnings=ActionInputs.get_warnings(),
79-
print_empty_chapters=ActionInputs.get_print_empty_chapters(),
8078
changelog_url=changelog_url
8179
)
8280

release_notes_generator/utils/gh_action.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,23 @@
2525

2626
import os
2727
import sys
28+
from typing import Optional
2829

2930

30-
def get_action_input(name: str) -> str:
31+
def get_action_input(name: str, default: Optional[str] = None) -> str:
3132
"""
3233
Retrieve the value of a specified input parameter from environment variables.
3334
3435
This function constructs the environment variable name by replacing hyphens with
3536
underscores, converting the string to uppercase, and prefixing it with 'INPUT_'.
3637
It then retrieves the value of this environment variable.
3738
38-
Args:
39-
name (str): The name of the input parameter.
39+
@param name: The name of the input parameter.
40+
@param default: The default value to return if the environment variable is not set.
4041
41-
Returns:
42-
str: The value of the specified input parameter, or an empty string if the environment
43-
variable is not set.
42+
@return: The value of the specified input parameter, or an empty string if the environment
4443
"""
45-
return os.getenv(f'INPUT_{name.replace("-", "_").upper()}', '')
44+
return os.getenv(f'INPUT_{name.replace("-", "_").upper()}', default=default)
4645

4746

4847
def set_action_output(name: str, value: str, default_output_path: str = "default_output.txt"):
@@ -52,11 +51,10 @@ def set_action_output(name: str, value: str, default_output_path: str = "default
5251
This function writes the output in a specific format that includes the name of the
5352
output and its value. The output is appended to the specified file.
5453
55-
Args:
56-
name (str): The name of the output parameter.
57-
value (str): The value of the output parameter.
58-
default_output_path (str, optional): The default file path to which the output is
59-
written if the 'GITHUB_OUTPUT' environment variable is not set. Defaults to "default_output.txt".
54+
@param name: The name of the output parameter.
55+
@param value: The value of the output parameter.
56+
@param default_output_path: The default file path to which the output is written if the
57+
'GITHUB_OUTPUT' environment variable is not set. Defaults to "default_output.txt".
6058
"""
6159
output_file = os.getenv('GITHUB_OUTPUT', default_output_path)
6260
with open(output_file, 'a', encoding="utf-8") as f:
@@ -72,8 +70,7 @@ def set_action_failed(message: str):
7270
This function prints an error message in the format expected by GitHub Actions
7371
and then exits the script with a non-zero status code.
7472
75-
Args:
76-
message (str): The error message to be displayed.
73+
@param message: The error message to be displayed.
7774
"""
7875
print(f'::error::{message}')
7976
sys.exit(1)

0 commit comments

Comments
 (0)