Skip to content

Commit

Permalink
- Addressing review note. Finished OOP design for main script.
Browse files Browse the repository at this point in the history
  • Loading branch information
miroslavpojer committed Jun 28, 2024
1 parent f68742d commit 510b0c7
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 186 deletions.
Empty file added src/action/__init__.py
Empty file.
120 changes: 120 additions & 0 deletions src/action/action_inputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import json
import logging

from github_integration.gh_action import get_action_input


class ActionInputs:
GITHUB_REPOSITORY = 'GITHUB_REPOSITORY'
GITHUB_TOKEN = 'github-token'
TAG_NAME = 'tag-name'
CHAPTERS = 'chapters'
WARNINGS = 'warnings'
PUBLISHED_AT = 'published-at'
SKIP_RELEASE_NOTES_LABEL = 'skip-release-notes-label'
PRINT_EMPTY_CHAPTERS = 'print-empty-chapters'
CHAPTERS_TO_PR_WITHOUT_ISSUE = 'chapters-to-pr-without-issue'
VERBOSE = 'verbose'

@staticmethod
def get_github_repository() -> str:
return get_action_input(ActionInputs.GITHUB_REPOSITORY)

@staticmethod
def get_github_token() -> str:
return get_action_input(ActionInputs.GITHUB_TOKEN)

@staticmethod
def get_tag_name() -> str:
return get_action_input(ActionInputs.TAG_NAME)

@staticmethod
def get_chapters_json() -> str:
return get_action_input(ActionInputs.CHAPTERS)

@staticmethod
def get_warnings() -> bool:
return get_action_input(ActionInputs.WARNINGS).lower() == 'true'

@staticmethod
def get_published_at() -> bool:
return get_action_input(ActionInputs.PUBLISHED_AT).lower() == 'true'

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

@staticmethod
def get_print_empty_chapters() -> bool:
return get_action_input(ActionInputs.PRINT_EMPTY_CHAPTERS).lower() == 'true'

@staticmethod
def get_chapters_to_pr_without_issue() -> bool:
return get_action_input(ActionInputs.CHAPTERS_TO_PR_WITHOUT_ISSUE).lower() == 'true'

@staticmethod
def get_verbose() -> bool:
return get_action_input(ActionInputs.VERBOSE).lower() == 'true'

@staticmethod
def validate_inputs():
"""
Validates the inputs provided for the release notes generator.
:raises ValueError: If any of the inputs are invalid.
"""
repository_id = ActionInputs.get_github_repository()
if '/' in repository_id:
owner, repo_name = ActionInputs.get_github_repository().split('/')
else:
owner = repo_name = ""

if not isinstance(owner, str) or not owner.strip():
raise ValueError("Owner must be a non-empty string.")

if not isinstance(repo_name, str) or not repo_name.strip():
raise ValueError("Repo name must be a non-empty string.")

tag_name = ActionInputs.get_tag_name()
if not isinstance(tag_name, str) or not tag_name.strip():
raise ValueError("Tag name must be a non-empty string.")

try:
chapters_json = ActionInputs.get_chapters_json()
json.loads(chapters_json)
except json.JSONDecodeError:
raise ValueError("Chapters JSON must be a valid JSON string.")

warnings = ActionInputs.get_warnings()
if not isinstance(warnings, bool):
raise ValueError("Warnings must be a boolean.")

published_at = ActionInputs.get_published_at()
if not isinstance(published_at, bool):
raise ValueError("Published at must be a boolean.")

skip_release_notes_label = ActionInputs.get_skip_release_notes_label()
if not isinstance(skip_release_notes_label, str) or not skip_release_notes_label.strip():
raise ValueError("Skip release notes label must be a non-empty string.")

print_empty_chapters = ActionInputs.get_print_empty_chapters()
if not isinstance(print_empty_chapters, bool):
raise ValueError("Print empty chapters must be a boolean.")

chapters_to_pr_without_issue = ActionInputs.get_chapters_to_pr_without_issue()
if not isinstance(chapters_to_pr_without_issue, bool):
raise ValueError("Chapters to PR without issue must be a boolean.")

verbose = ActionInputs.get_verbose()
if not isinstance(verbose, bool):
raise ValueError("Verbose logging must be a boolean.")

logging.debug(f'Repository: {owner}/{repo_name}')
logging.debug(f'Tag name: {tag_name}')
logging.debug(f'Chapters JSON: {chapters_json}')
logging.debug(f'Warnings: {warnings}')
logging.debug(f'Published at: {published_at}')
logging.debug(f'Skip release notes label: {skip_release_notes_label}')
logging.debug(f'Print empty chapters: {print_empty_chapters}')
logging.debug(f'Chapters to PR without issue: {chapters_to_pr_without_issue}')
logging.debug(f'Verbose logging: {verbose}')
123 changes: 4 additions & 119 deletions src/release_notes_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Optional
from github import Github, Auth

from action.action_inputs import ActionInputs
from github_integration.gh_action import get_action_input, set_action_output, set_action_failed
from github_integration.github_manager import GithubManager

Expand All @@ -19,123 +20,7 @@
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')


class ActionInputs:
GITHUB_REPOSITORY = 'GITHUB_REPOSITORY'
GITHUB_TOKEN = 'github-token'
TAG_NAME = 'tag-name'
CHAPTERS = 'chapters'
WARNINGS = 'warnings'
PUBLISHED_AT = 'published-at'
SKIP_RELEASE_NOTES_LABEL = 'skip-release-notes-label'
PRINT_EMPTY_CHAPTERS = 'print-empty-chapters'
CHAPTERS_TO_PR_WITHOUT_ISSUE = 'chapters-to-pr-without-issue'
VERBOSE = 'verbose'

@staticmethod
def get_github_repository() -> str:
return get_action_input(ActionInputs.GITHUB_REPOSITORY)

@staticmethod
def get_github_token() -> str:
return get_action_input(ActionInputs.GITHUB_TOKEN)

@staticmethod
def get_tag_name() -> str:
return get_action_input(ActionInputs.TAG_NAME)

@staticmethod
def get_chapters_json() -> str:
return get_action_input(ActionInputs.CHAPTERS)

@staticmethod
def get_warnings() -> bool:
return get_action_input(ActionInputs.WARNINGS).lower() == 'true'

@staticmethod
def get_published_at() -> bool:
return get_action_input(ActionInputs.PUBLISHED_AT).lower() == 'true'

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

@staticmethod
def get_print_empty_chapters() -> bool:
return get_action_input(ActionInputs.PRINT_EMPTY_CHAPTERS).lower() == 'true'

@staticmethod
def get_chapters_to_pr_without_issue() -> bool:
return get_action_input(ActionInputs.CHAPTERS_TO_PR_WITHOUT_ISSUE).lower() == 'true'

@staticmethod
def get_verbose() -> bool:
return get_action_input(ActionInputs.VERBOSE).lower() == 'true'


def validate_inputs():
"""
Validates the inputs provided for the release notes generator.
:raises ValueError: If any of the inputs are invalid.
"""
repository_id = ActionInputs.get_github_repository()
if '/' in repository_id:
owner, repo_name = ActionInputs.get_github_repository().split('/')
else:
owner = repo_name = ""

if not isinstance(owner, str) or not owner.strip():
raise ValueError("Owner must be a non-empty string.")

if not isinstance(repo_name, str) or not repo_name.strip():
raise ValueError("Repo name must be a non-empty string.")

tag_name = ActionInputs.get_tag_name()
if not isinstance(tag_name, str) or not tag_name.strip():
raise ValueError("Tag name must be a non-empty string.")

try:
chapters_json = ActionInputs.get_chapters_json()
json.loads(chapters_json)
except json.JSONDecodeError:
raise ValueError("Chapters JSON must be a valid JSON string.")

warnings = ActionInputs.get_warnings()
if not isinstance(warnings, bool):
raise ValueError("Warnings must be a boolean.")

published_at = ActionInputs.get_published_at()
if not isinstance(published_at, bool):
raise ValueError("Published at must be a boolean.")

skip_release_notes_label = ActionInputs.get_skip_release_notes_label()
if not isinstance(skip_release_notes_label, str) or not skip_release_notes_label.strip():
raise ValueError("Skip release notes label must be a non-empty string.")

print_empty_chapters = ActionInputs.get_print_empty_chapters()
if not isinstance(print_empty_chapters, bool):
raise ValueError("Print empty chapters must be a boolean.")

chapters_to_pr_without_issue = ActionInputs.get_chapters_to_pr_without_issue()
if not isinstance(chapters_to_pr_without_issue, bool):
raise ValueError("Chapters to PR without issue must be a boolean.")

verbose = ActionInputs.get_verbose()
if not isinstance(verbose, bool):
raise ValueError("Verbose logging must be a boolean.")

logging.debug(f'Repository: {owner}/{repo_name}')
logging.debug(f'Tag name: {tag_name}')
logging.debug(f'Chapters JSON: {chapters_json}')
logging.debug(f'Warnings: {warnings}')
logging.debug(f'Published at: {published_at}')
logging.debug(f'Skip release notes label: {skip_release_notes_label}')
logging.debug(f'Print empty chapters: {print_empty_chapters}')
logging.debug(f'Chapters to PR without issue: {chapters_to_pr_without_issue}')
logging.debug(f'Verbose logging: {verbose}')


def release_notes_generator(custom_chapters: CustomChapters) -> Optional[str]:
def generate_release_notes(custom_chapters: CustomChapters) -> Optional[str]:
"""
Generates the release notes for a given repository.
Expand Down Expand Up @@ -202,12 +87,12 @@ def run():
GithubManager().github = g # creat singleton instance and init with g (Github)
GithubManager().show_rate_limit()

validate_inputs()
ActionInputs.validate_inputs()

custom_chapters = CustomChapters(print_empty_chapters=ActionInputs.get_print_empty_chapters())
custom_chapters.from_json(ActionInputs.get_chapters_json())

rls_notes = release_notes_generator(custom_chapters)
rls_notes = generate_release_notes(custom_chapters)
logging.debug(f"Release notes: \n{rls_notes}")

set_action_output('release-notes', rls_notes)
Expand Down
Empty file added tests/action/__init__.py
Empty file.
68 changes: 68 additions & 0 deletions tests/action/test_action_inputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from unittest.mock import patch
import pytest

from action.action_inputs import ActionInputs

# Data-driven test cases
success_case = {
'get_github_repository': 'owner/repo_name',
'get_tag_name': 'tag_name',
'get_chapters_json': '{"chapter": "content"}',
'get_warnings': True,
'get_published_at': False,
'get_skip_release_notes_label': 'skip',
'get_print_empty_chapters': True,
'get_chapters_to_pr_without_issue': False,
'get_verbose': True,
}

failure_cases = [
('get_github_repository', '', "Owner must be a non-empty string."),
('get_github_repository', 'owner/', "Repo name 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_warnings', 'not_bool', "Warnings must be a boolean."),
('get_published_at', 'not_bool', "Published at must be a boolean."),
('get_skip_release_notes_label', '', "Skip release notes label must be a non-empty string."),
('get_print_empty_chapters', 'not_bool', "Print empty chapters must be a boolean."),
('get_chapters_to_pr_without_issue', 'not_bool', "Chapters to PR without issue must be a boolean."),
('get_verbose', 'not_bool', "Verbose logging must be a boolean."),
]


def apply_mocks(case):
patchers = []
for key, value in case.items():
patcher = patch(f'action.action_inputs.ActionInputs.{key}', return_value=value)
patcher.start()
patchers.append(patcher)
return patchers


def stop_mocks(patchers):
for patcher in patchers:
patcher.stop()


def test_validate_inputs_success():
patchers = apply_mocks(success_case)
try:
ActionInputs.validate_inputs()
finally:
stop_mocks(patchers)


@pytest.mark.parametrize('method, value, expected_error', failure_cases)
def test_validate_inputs_failure(method, value, expected_error):
case = success_case.copy()
case[method] = value
patchers = apply_mocks(case)
try:
with pytest.raises(ValueError, match=expected_error):
ActionInputs.validate_inputs()
finally:
stop_mocks(patchers)


if __name__ == '__main__':
pytest.main()
Loading

0 comments on commit 510b0c7

Please sign in to comment.