diff --git a/.github/workflows/release_draft.yml b/.github/workflows/release_draft.yml index 479795fc..09d06086 100644 --- a/.github/workflows/release_draft.yml +++ b/.github/workflows/release_draft.yml @@ -51,19 +51,16 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag-name: ${{ github.event.inputs.tag-name }} - chapters: '[ - {"title": "No entry 🚫", "label": "duplicate"}, - {"title": "No entry 🚫", "label": "invalid"}, - {"title": "No entry 🚫", "label": "wontfix"}, - {"title": "No entry 🚫", "label": "no RN"}, - {"title": "Breaking Changes 💥", "label": "breaking-change"}, - {"title": "New Features 🎉", "label": "enhancement"}, - {"title": "New Features 🎉", "label": "feature"}, - {"title": "Bugfixes 🛠", "label": "bug"}, - {"title": "Infrastructure ⚙️", "label": "infrastructure"}, - {"title": "Silent-live 🤫", "label": "silent-live"}, - {"title": "Documentation 📜", "label": "documentation"} - ]' + chapters: | + - { title: No entry 🚫, label: duplicate } + - { title: Breaking Changes 💥, label: breaking-change } + - { title: New Features 🎉, label: enhancement } + - { title: New Features 🎉, label: feature } + - { title: Bugfixes 🛠, label: bug } + - { title: Infrastructure ⚙️, label: infrastructure } + - { title: Silent-live 🤫, label: silent-live } + - { title: Documentation 📜, label: documentation } + skip-release-notes-label: 'no RN' verbose: true diff --git a/README.md b/README.md index b13f4073..9290caca 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Generate Release Notes action is dedicated to enhance the quality and organizati - **Required**: Yes ### `chapters` -- **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. +- **Description**: An Yaml array defining chapters and corresponding labels for categorization. Each chapter should have a title and a label matching your GitHub issues and PRs. - **Required**: Yes ### `row-format-issue` @@ -374,11 +374,16 @@ Create *.sh file and place it in the project root. # Set environment variables based on the action inputs export INPUT_TAG_NAME="v0.2.0" + export INPUT_CHAPTERS='[ - {"title": "Breaking Changes 💥", "label": "breaking-change"}, - {"title": "New Features 🎉", "label": "enhancement"}, - {"title": "New Features 🎉", "label": "feature"}, - {"title": "Bugfixes 🛠", "label": "bug"} +{ title: No entry 🚫, label: duplicate }, +{ title: Breaking Changes 💥, label: breaking-change }, +{ title: New Features 🎉, label: enhancement }, +{ title: New Features 🎉, label: feature }, +{ title: Bugfixes 🛠, label: bug }, +{ title: Infrastructure ⚙️, label: infrastructure }, +{ title: Silent-live 🤫, label: silent-live }, +{ title: Documentation 📜, label: documentation } ]' export INPUT_WARNINGS="true" export INPUT_PUBLISHED_AT="true" diff --git a/action.yml b/action.yml index 84aa8023..e8bcffcf 100644 --- a/action.yml +++ b/action.yml @@ -21,7 +21,7 @@ inputs: description: 'The tag name of the release to generate notes for.' required: true chapters: - description: 'JSON string defining chapters and corresponding labels for categorization.' + description: 'Yaml array defining chapters and corresponding labels for categorization.' required: false default: '' duplicity-scope: diff --git a/examples/release_draft.yml b/examples/release_draft.yml index 06905127..e1230b10 100644 --- a/examples/release_draft.yml +++ b/examples/release_draft.yml @@ -38,19 +38,16 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag-name: ${{ github.event.inputs.tag-name }} - chapters: '[ - {"title": "No entry 🚫", "label": "duplicate"}, - {"title": "No entry 🚫", "label": "invalid"}, - {"title": "No entry 🚫", "label": "wontfix"}, - {"title": "No entry 🚫", "label": "no RN"}, - {"title": "Breaking Changes 💥", "label": "breaking-change"}, - {"title": "New Features 🎉", "label": "enhancement"}, - {"title": "New Features 🎉", "label": "feature"}, - {"title": "Bugfixes 🛠", "label": "bug"}, - {"title": "Infrastructure ⚙️", "label": "infrastructure"}, - {"title": "Silent-live 🤫", "label": "silent-live"}, - {"title": "Documentation 📜", "label": "documentation"} - ]' + chapters: | + - { title: No entry 🚫, label: duplicate } + - { title: Breaking Changes 💥, label: breaking-change } + - { title: New Features 🎉, label: enhancement } + - { title: New Features 🎉, label: feature } + - { title: Bugfixes 🛠, label: bug } + - { title: Infrastructure ⚙️, label: infrastructure } + - { title: Silent-live 🤫, label: silent-live } + - { title: Documentation 📜, label: documentation } + skip-release-notes-label: 'no RN' verbose: true diff --git a/release_notes_generator/action_inputs.py b/release_notes_generator/action_inputs.py index 1358faf2..4c8c6d72 100644 --- a/release_notes_generator/action_inputs.py +++ b/release_notes_generator/action_inputs.py @@ -23,8 +23,8 @@ import sys import re -import yaml from typing import Optional +import yaml from release_notes_generator.utils.constants import ( @@ -90,7 +90,7 @@ def get_chapters() -> Optional[list[dict[str, str]]]: Get list of the chapters from the action inputs. Each chapter is a dict. """ # Get the 'chapters' input from environment variables - chapters_input: str = get_action_input(CHAPTERS, default='') + chapters_input: str = get_action_input(CHAPTERS, default="") # Parse the YAML input try: @@ -99,7 +99,7 @@ def get_chapters() -> Optional[list[dict[str, str]]]: logger.error("Error: 'chapters' input is not a valid YAML list.") return None except yaml.YAMLError as exc: - logger.error(f"Error parsing 'chapters' input: {exc}") + logger.error("Error parsing 'chapters' input: {%s}", exc) return None return chapters @@ -240,7 +240,7 @@ def validate_inputs() -> None: chapters = ActionInputs.get_chapters() if chapters is None: - errors.append("Chapters JSON must be a valid JSON array.") + errors.append("Chapters must be a valid yaml array.") duplicity_icon = ActionInputs.get_duplicity_icon() if not isinstance(duplicity_icon, str) or not duplicity_icon.strip() or len(duplicity_icon) != 1: @@ -286,7 +286,7 @@ def validate_inputs() -> None: logger.debug("Repository: %s/%s", owner, repo_name) logger.debug("Tag name: %s", tag_name) - logger.debug("Chapters JSON: %s", chapters) + logger.debug("Chapters: %s", chapters) logger.debug("Published at: %s", published_at) logger.debug("Skip release notes labels: %s", ActionInputs.get_skip_release_notes_labels()) logger.debug("Verbose logging: %s", verbose) @@ -308,9 +308,10 @@ def _detect_row_format_invalid_keywords(row_format: str, row_type: str = "Issue" cleaned_row_format = row_format for invalid_keyword in invalid_keywords: logger.error( - "Invalid `{}` detected in `{}` row format keyword(s) found: {}. Will be removed from string.".format( - invalid_keyword, row_type, ", ".join(invalid_keywords) - ) + "Invalid `%s` detected in `%s` row format keyword(s) found: %s. Will be removed from string.", + invalid_keyword, + row_type, + ", ".join(invalid_keywords), ) if clean: cleaned_row_format = cleaned_row_format.replace(f"{{{invalid_keyword}}}", "") diff --git a/release_notes_generator/model/custom_chapters.py b/release_notes_generator/model/custom_chapters.py index 9795dd4e..6d202ee9 100644 --- a/release_notes_generator/model/custom_chapters.py +++ b/release_notes_generator/model/custom_chapters.py @@ -19,8 +19,6 @@ notes. """ -import json - from release_notes_generator.action_inputs import ActionInputs from release_notes_generator.model.base_chapters import BaseChapters from release_notes_generator.model.chapter import Chapter diff --git a/release_notes_generator/utils/pull_reuqest_utils.py b/release_notes_generator/utils/pull_reuqest_utils.py index 66629de9..3cbeb851 100644 --- a/release_notes_generator/utils/pull_reuqest_utils.py +++ b/release_notes_generator/utils/pull_reuqest_utils.py @@ -14,6 +14,10 @@ # limitations under the License. # +""" +This module contains the PullRequestRecord class which is responsible for representing a record in the release notes. +""" + import re from github.PullRequest import PullRequest diff --git a/tests/release_notes/model/test_custom_chapters.py b/tests/release_notes/model/test_custom_chapters.py index e2d00dc9..920ea37e 100644 --- a/tests/release_notes/model/test_custom_chapters.py +++ b/tests/release_notes/model/test_custom_chapters.py @@ -149,17 +149,16 @@ def test_populate_none_duplicity_scope(custom_chapters, mocker): # from_json -def test_custom_chapters_from_json(): +def test_custom_chapters_from_yaml_array(): custom_chapters = CustomChapters() - json_string = """ - [ + yaml_array_in_string = [ {"title": "Breaking Changes 💥", "label": "breaking-change"}, {"title": "New Features 🎉", "label": "enhancement"}, {"title": "New Features 🎉", "label": "feature"}, {"title": "Bugfixes 🛠", "label": "bug"} ] - """ - custom_chapters.from_json(json_string) + + custom_chapters.from_yaml_array(yaml_array_in_string) assert "Breaking Changes 💥" in custom_chapters.titles assert "New Features 🎉" in custom_chapters.titles diff --git a/tests/release_notes/test_release_notes_builder.py b/tests/release_notes/test_release_notes_builder.py index a384849a..aaabe435 100644 --- a/tests/release_notes/test_release_notes_builder.py +++ b/tests/release_notes/test_release_notes_builder.py @@ -78,14 +78,12 @@ def __init__(self, name): DEFAULT_CHANGELOG_URL = "http://example.com/changelog" -default_chapters_json = json.dumps( - [ +default_chapters = [ {"title": "Breaking Changes 💥", "label": "breaking-change"}, {"title": "New Features 🎉", "label": "feature"}, {"title": "New Features 🎉", "label": "enhancement"}, {"title": "Bugfixes 🛠", "label": "bug"}, ] -) RELEASE_NOTES_NO_DATA = """### Breaking Changes 💥 No entries detected. @@ -302,7 +300,7 @@ def __init__(self, name): def test_build_no_data(): custom_chapters = CustomChapters() - custom_chapters.from_json(default_chapters_json) + custom_chapters.from_yaml_array(default_chapters) expected_release_notes = RELEASE_NOTES_NO_DATA @@ -318,7 +316,7 @@ def test_build_no_data(): def test_build_no_data_no_warnings(mocker): custom_chapters = CustomChapters() - custom_chapters.from_json(default_chapters_json) + custom_chapters.from_yaml_array(default_chapters) mocker.patch("release_notes_generator.builder.ActionInputs.get_warnings", return_value=False) expected_release_notes = RELEASE_NOTES_NO_DATA_NO_WARNING @@ -335,7 +333,7 @@ def test_build_no_data_no_warnings(mocker): def test_build_no_data_no_warnings_no_empty_chapters(mocker): custom_chapters_no_empty_chapters = CustomChapters() - custom_chapters_no_empty_chapters.from_json(default_chapters_json) + custom_chapters_no_empty_chapters.from_yaml_array(default_chapters) custom_chapters_no_empty_chapters.print_empty_chapters = False mocker.patch("release_notes_generator.builder.ActionInputs.get_warnings", return_value=False) mocker.patch("release_notes_generator.builder.ActionInputs.get_print_empty_chapters", return_value=False) @@ -354,7 +352,7 @@ def test_build_no_data_no_warnings_no_empty_chapters(mocker): def test_build_no_data_no_empty_chapters(mocker): custom_chapters_no_empty_chapters = CustomChapters() - custom_chapters_no_empty_chapters.from_json(default_chapters_json) + custom_chapters_no_empty_chapters.from_yaml_array(default_chapters) custom_chapters_no_empty_chapters.print_empty_chapters = False mocker.patch("release_notes_generator.builder.ActionInputs.get_print_empty_chapters", return_value=False) diff --git a/tests/test_action_inputs.py b/tests/test_action_inputs.py index 2afc31b0..8794bee3 100644 --- a/tests/test_action_inputs.py +++ b/tests/test_action_inputs.py @@ -23,7 +23,7 @@ success_case = { "get_github_repository": "owner/repo_name", "get_tag_name": "tag_name", - "get_chapters_json": '{"chapter": "content"}', + "get_chapters": [{"title": "Title", "label": "Label"}], "get_duplicity_scope": "custom", "get_duplicity_icon": "🔁", "get_warnings": True, @@ -37,7 +37,7 @@ ("get_github_repository", "", "Owner and Repo must be a non-empty string."), ("get_github_repository", "owner/", "Owner and Repo must be a non-empty string."), ("get_tag_name", "", "Tag name must be a non-empty string."), - ("get_chapters_json", "invalid_json", "Chapters JSON must be a valid JSON string."), + ("get_chapters", None, "Chapters must be a valid yaml array."), ("get_warnings", "not_bool", "Warnings must be a boolean."), ("get_published_at", "not_bool", "Published at must be a boolean."), ("get_print_empty_chapters", "not_bool", "Print empty chapters must be a boolean."), @@ -104,9 +104,19 @@ def test_get_tag_name(mocker): assert ActionInputs.get_tag_name() == "v1.0.0" -def test_get_chapters_json(mocker): - mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value='{"chapters": []}') - assert ActionInputs.get_chapters_json() == '{"chapters": []}' +def test_get_chapters_success(mocker): + mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="[{\"title\": \"Title\", \"label\": \"Label\"}]") + assert ActionInputs.get_chapters() == [{"title": "Title", "label": "Label"}] + + +def test_get_chapters_exception(mocker): + mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="wrong value") + assert None == ActionInputs.get_chapters() + + +def test_get_chapters_yaml_error(mocker): + mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="[{\"title\": \"Title\" \"label\": \"Label\"}]") + assert None == ActionInputs.get_chapters() def test_get_warnings(mocker):