Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/119 add ability to define rls from which to detect changes #121

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .github/workflows/release_draft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ on:
tag-name:
description: 'Name of git tag to be created, and then draft release created. Syntax: "v[0-9]+.[0-9]+.[0-9]+".'
required: true
from-tag-name:
description: 'Name of git tag from which to detect changes. Syntax: "v[0-9]+.[0-9]+.[0-9]+".'
dk1844 marked this conversation as resolved.
Show resolved Hide resolved
required: false

jobs:
release-draft:
Expand All @@ -37,20 +40,32 @@ jobs:

- name: Check format of received tag
id: check-version-tag
uses: AbsaOSS/version-tag-check@v0.2.0
uses: AbsaOSS/version-tag-check@v0.3.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
github-repository: ${{ github.repository }}
version-tag: ${{ github.event.inputs.tag-name }}

- name: Check format of received from tag
if: ${{ github.event.inputs.from-tag-name }}
id: check-version-from-tag
uses: AbsaOSS/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
github-repository: ${{ github.repository }}
version-tag: ${{ github.event.inputs.from-tag-name }}
should-exist: true

- name: Generate Release Notes
id: generate_release_notes
uses: AbsaOSS/generate-release-notes@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag-name: ${{ github.event.inputs.tag-name }}
from-tag-name: ${{ github.event.inputs.from-tag-name }}
chapters: '[
{"title": "No entry 🚫", "label": "duplicate"},
{"title": "No entry 🚫", "label": "invalid"},
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ inputs:
description: 'Allow duplicity of issue lines in chapters. Scopes: custom, service, both, none.'
required: false
default: 'both'
from-tag-name:
description: 'The tag name of the previous release to use as a start reference point for the current release notes.'
required: false
default: ''
duplicity-icon:
description: 'Icon to be used for duplicity warning. Icon is placed before the record line.'
required: false
Expand Down Expand Up @@ -115,6 +119,7 @@ runs:
INPUT_GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }}
INPUT_TAG_NAME: ${{ inputs.tag-name }}
INPUT_CHAPTERS: ${{ inputs.chapters }}
INPUT_FROM_TAG_NAME: ${{ inputs.from-tag-name }}
INPUT_DUPLICITY_SCOPE: ${{ inputs.duplicity-scope }}
INPUT_DUPLICITY_ICON: ${{ inputs.duplicity-icon }}
INPUT_WARNINGS: ${{ inputs.warnings }}
Expand Down
18 changes: 16 additions & 2 deletions examples/release_draft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
tag-name:
description: 'Name of git tag to be created, and then draft release created. Syntax: "v[0-9]+.[0-9]+.[0-9]+".'
required: true
from-tag-name:
description: 'Name of git tag from which to detect changes. Syntax: "v[0-9]+.[0-9]+.[0-9]+".'
dk1844 marked this conversation as resolved.
Show resolved Hide resolved
required: false

jobs:
release-draft:
Expand All @@ -23,21 +26,32 @@ jobs:

- name: Check format of received tag
id: check-version-tag
uses: AbsaOSS/version-tag-check@v0.1.0
uses: AbsaOSS/version-tag-check@v0.3.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
github-repository: ${{ github.repository }}
branch: 'master'
version-tag: ${{ github.event.inputs.tag-name }}

- name: Check format of received from tag
if: ${{ github.event.inputs.from-tag-name }}
id: check-version-from-tag
uses: AbsaOSS/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
github-repository: ${{ github.repository }}
version-tag: ${{ github.event.inputs.from-tag-name }}
should-exist: true

- name: Generate Release Notes
id: generate_release_notes
uses: AbsaOSS/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag-name: ${{ github.event.inputs.tag-name }}
from-tag-name: ${{ github.event.inputs.from-tag-name }}
chapters: '[
{"title": "No entry 🚫", "label": "duplicate"},
{"title": "No entry 🚫", "label": "invalid"},
Expand Down
32 changes: 27 additions & 5 deletions release_notes_generator/action_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
RELEASE_NOTES_TITLE,
RELEASE_NOTE_TITLE_DEFAULT,
SUPPORTED_ROW_FORMAT_KEYS,
FROM_TAG_NAME,
)
from release_notes_generator.utils.enums import DuplicityScopeEnum
from release_notes_generator.utils.gh_action import get_action_input
Expand Down Expand Up @@ -81,6 +82,21 @@ def get_tag_name() -> str:
"""
return get_action_input(TAG_NAME)

@staticmethod
def get_from_tag_name() -> str:
"""
Get the from-tag name from the action inputs.
"""
return get_action_input(FROM_TAG_NAME, default="")

@staticmethod
def is_from_tag_name_defined() -> bool:
"""
Check if the from-tag name is defined in the action inputs.
"""
value = ActionInputs.get_from_tag_name()
return value.strip() != ""

@staticmethod
def get_chapters_json() -> str:
"""
Expand Down Expand Up @@ -120,8 +136,9 @@ def get_skip_release_notes_labels() -> str:
"""
Get the skip release notes label from the action inputs.
"""
user_choice = [item.strip() for item in get_action_input(SKIP_RELEASE_NOTES_LABELS, "").split(",")]
if len(user_choice) > 0:
user_input = get_action_input(SKIP_RELEASE_NOTES_LABELS, "")
user_choice = [item.strip() for item in user_input.split(",")] if user_input else []
if user_choice:
return user_choice
return ["skip-release-notes"]

Expand Down Expand Up @@ -222,6 +239,10 @@ def validate_inputs() -> None:
if not isinstance(tag_name, str) or not tag_name.strip():
errors.append("Tag name must be a non-empty string.")

from_tag_name = ActionInputs.get_from_tag_name()
if not isinstance(from_tag_name, str):
errors.append("From tag name must be a string.")

chapters_json = ActionInputs.get_chapters_json()
try:
json.loads(chapters_json)
Expand Down Expand Up @@ -294,9 +315,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}}}", "")
Expand Down
1 change: 0 additions & 1 deletion release_notes_generator/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
logger = logging.getLogger(__name__)


# TODO - reduce to function only after implementing the features. Will be supported more build ways?
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved
# pylint: disable=too-few-public-methods
class ReleaseNotesBuilder:
"""
Expand Down
47 changes: 40 additions & 7 deletions release_notes_generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
"""

import logging
import sys

from typing import Optional
from github import Github
from github.GitRelease import GitRelease
from github.Repository import Repository

from release_notes_generator.model.custom_chapters import CustomChapters
from release_notes_generator.model.record import Record
Expand Down Expand Up @@ -72,15 +75,13 @@ def generate(self) -> Optional[str]:

@return: The generated release notes as a string, or None if the repository could not be found.
"""
# get the repository
repo = self._safe_call(self.github_instance.get_repo)(ActionInputs.get_github_repository())
if repo is None:
return None

rls = self._safe_call(repo.get_latest_release)()
if rls is None:
logger.info("Latest release not found for %s. 1st release for repository!", repo.full_name)
else:
logger.debug("RLS created_at: %s, published_at: %s", rls.created_at, rls.published_at)
# get the latest release
rls = self.get_latest_release(repo)
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved

# default is repository creation date if no releases OR created_at of latest release
since = rls.created_at if rls else repo.created_at
Expand All @@ -97,12 +98,12 @@ def generate(self) -> Optional[str]:

# filter out closed Issues before the date
issues = list(
filter(lambda issue: issue.closed_at is not None and issue.closed_at > since, list(issues_all))
filter(lambda issue: issue.closed_at is not None and issue.closed_at >= since, list(issues_all))
)
logger.debug("Count of issues reduced from %d to %d", len(list(issues_all)), len(issues))

# filter out merged PRs and commits before the date
pulls = list(filter(lambda pull: pull.merged_at is not None and pull.merged_at > since, list(pulls_all)))
pulls = list(filter(lambda pull: pull.merged_at is not None and pull.merged_at >= since, list(pulls_all)))
logger.debug("Count of pulls reduced from %d to %d", len(list(pulls_all)), len(pulls))

commits = list(filter(lambda commit: commit.commit.author.date > since, list(commits_all)))
Expand All @@ -125,3 +126,35 @@ def generate(self) -> Optional[str]:
)

return release_notes_builder.build()

def get_latest_release(self, repo: Repository) -> Optional[GitRelease]:
dk1844 marked this conversation as resolved.
Show resolved Hide resolved
"""
Get the latest release of the repository.

@param repo: The repository to get the latest release from.
@return: The latest release of the repository, or None if no releases are found.
"""
if ActionInputs.is_from_tag_name_defined():
logger.info("Getting latest release by from-tag name %s", ActionInputs.get_tag_name())
rls = self._safe_call(repo.get_release)(ActionInputs.get_from_tag_name())
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved

if rls is None:
logger.info("Latest release not found for received tag %s. Ending!", ActionInputs.get_from_tag_name())
sys.exit(1)

else:
logger.info("Getting latest release by time.")
rls = self._safe_call(repo.get_latest_release)()
MobiTikula marked this conversation as resolved.
Show resolved Hide resolved

if rls is None:
logger.info("Latest release not found for %s. 1st release for repository!", repo.full_name)

if rls is not None:
logger.debug(
"Latest release with tag:'%s' created_at: %s, published_at: %s",
rls.tag_name,
rls.created_at,
rls.published_at,
)

return rls
2 changes: 1 addition & 1 deletion release_notes_generator/record/record_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def create_record_for_issue(r: Repository, i: Issue) -> None:
@return: None
"""
# check for skip labels presence and skip when detected
issue_labels = [label.name for label in issue.labels]
issue_labels = [label.name for label in i.labels]
skip_record = any(item in issue_labels for item in ActionInputs.get_skip_release_notes_labels())
records[i.number] = Record(r, i, skip=skip_record)

Expand Down
1 change: 1 addition & 0 deletions release_notes_generator/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
GITHUB_REPOSITORY = "GITHUB_REPOSITORY"
GITHUB_TOKEN = "github-token"
TAG_NAME = "tag-name"
FROM_TAG_NAME = "from-tag-name"
CHAPTERS = "chapters"
DUPLICITY_SCOPE = "duplicity-scope"
DUPLICITY_ICON = "duplicity-icon"
Expand Down
4 changes: 4 additions & 0 deletions release_notes_generator/utils/pull_reuqest_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
# limitations under the License.
#

"""
This module contains the main script for the Release Notes Generator GH Action.
"""

MobiTikula marked this conversation as resolved.
Show resolved Hide resolved
import re

from github.PullRequest import PullRequest
Expand Down
16 changes: 16 additions & 0 deletions tests/test_action_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
success_case = {
"get_github_repository": "owner/repo_name",
"get_tag_name": "tag_name",
"get_from_tag_name": "from_tag_name",
"get_chapters_json": '{"chapter": "content"}',
"get_duplicity_scope": "custom",
"get_duplicity_icon": "🔁",
Expand All @@ -31,12 +32,14 @@
"get_skip_release_notes_labels": ["skip"],
"get_print_empty_chapters": True,
"get_verbose": True,
"get_release_notes_title": "Success value",
}

failure_cases = [
("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_from_tag_name", 1, "From tag name must be a 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."),
Expand All @@ -46,6 +49,7 @@
("get_duplicity_icon", "Oj", "Duplicity icon must be a non-empty string and have a length of 1."),
("get_row_format_issue", "", "Issue row format must be a non-empty string."),
("get_row_format_pr", "", "PR Row format must be a non-empty string."),
("get_release_notes_title", "", "Release Notes title must be a non-empty string and have non-zero length."),
]


Expand Down Expand Up @@ -123,6 +127,10 @@ def test_get_skip_release_notes_label(mocker):
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="skip-release-notes")
assert ActionInputs.get_skip_release_notes_labels() == ["skip-release-notes"]

def test_get_skip_release_notes_label_not_defined(mocker):
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="")
assert ActionInputs.get_skip_release_notes_labels() == ["skip-release-notes"]

MobiTikula marked this conversation as resolved.
Show resolved Hide resolved
def test_get_skip_release_notes_labels(mocker):
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="skip-release-notes, another-skip-label")
assert ActionInputs.get_skip_release_notes_labels() == ["skip-release-notes", "another-skip-label"]
Expand Down Expand Up @@ -200,3 +208,11 @@ def test_clean_row_format_invalid_keywords_nested_braces():
actual_format = ActionInputs._detect_row_format_invalid_keywords(row_format, clean=True)
assert expected_format == actual_format


def test_release_notes_title_default():
assert ActionInputs.get_release_notes_title() == "[Rr]elease [Nn]otes:"


def test_release_notes_title_custom(mocker):
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="Custom Title")
assert ActionInputs.get_release_notes_title() == "Custom Title"
Loading
Loading