Source code for semantic_release.history.logs

"""Logs
"""
import logging
from typing import Optional

from ..errors import UnknownCommitMessageStyleError
from ..helpers import LoggedFunction
from ..settings import config, current_commit_parser
from ..vcs_helpers import get_commit_log, get_formatted_commit

logger = logging.getLogger(__name__)

LEVELS = {
    0: None,
    1: "patch",
    2: "minor",
    3: "major",
}


[docs]@LoggedFunction(logger) def evaluate_version_bump(current_version: str, force: str = None) -> Optional[str]: """ Read git log since the last release to decide if we should make a major, minor or patch release. :param current_version: A string with the current version number. :param force: A string with the bump level that should be forced. :return: A string with either major, minor or patch if there should be a release. If no release is necessary, None will be returned. """ if force: return force bump = None changes = [] commit_count = 0 current_version_commit = get_formatted_commit(current_version) for _hash, commit_message in get_commit_log(current_version): # https://github.com/relekang/python-semantic-release/issues/490 - # commit messages (which we compare with ==) have a trailing newline if commit_message.strip() == current_version_commit.strip(): # Stop once we reach the current version # (we are looping in the order of newest -> oldest) logger.debug( f'"{commit_message}" is commit for {current_version}, breaking loop' ) break commit_count += 1 # Attempt to parse this commit using the currently-configured parser try: message = current_commit_parser()(commit_message) changes.append(message.bump) except UnknownCommitMessageStyleError as err: logger.debug(f"Ignoring UnknownCommitMessageStyleError: {err}") pass logger.debug(f"Commits found since last release: {commit_count}") if changes: # Select the largest required bump level from the commits we parsed level = max(changes) if level in LEVELS: bump = LEVELS[level] else: logger.warning(f"Unknown bump level {level}") if config.get("patch_without_tag") and commit_count > 0 and bump is None: bump = "patch" logger.debug("Changing bump level to patch based on config patch_without_tag") if ( not config.get("major_on_zero") and current_version.startswith("0.") and bump == "major" ): bump = "minor" logger.debug("Changing bump level to minor based on config major_on_zero") return bump
[docs]@LoggedFunction(logger) def generate_changelog( from_version: Optional[str], to_version: Optional[str] = None ) -> dict: """ Parse a changelog dictionary for the given version. :param from_version: The version before where the changelog starts. The changelog will be generated from the commit after this one. :param to_version: The last version included in the changelog. :return: A dict with changelog sections and commits """ # Additional sections will be added as new types are encountered changes: dict = {"breaking": []} found_the_release = to_version is None to_version_commit = to_version and get_formatted_commit(to_version) from_version_commit = from_version and get_formatted_commit(from_version) for _hash, commit_message in get_commit_log(from_version, to_version): if not found_the_release: # Skip until we find the last commit in this release # (we are looping in the order of newest -> oldest) if to_version_commit and commit_message != to_version_commit: continue else: logger.debug( f"Reached the start of {to_version}, beginning changelog generation" ) found_the_release = True # See https://github.com/relekang/python-semantic-release/issues/490 - # commit messages (which we compare with ==) have a trailing newline if ( from_version_commit and commit_message.strip() == from_version_commit.strip() ): # We reached the previous release logger.debug(f"{from_version} reached, ending changelog generation") break try: message = current_commit_parser()(commit_message) if message.type not in changes: logger.debug(f"Creating new changelog section for {message.type} ") changes[message.type] = list() # Capitalize the first letter of the message, leaving others as they were # (using str.capitalize() would make the other letters lowercase) formatted_message = ( message.descriptions[0][0].upper() + message.descriptions[0][1:] ) if config.get("changelog_capitalize") is False: formatted_message = message.descriptions[0] # By default, feat(x): description shows up in changelog with the # scope bolded, like: # # * **x**: description if config.get("changelog_scope") and message.scope: formatted_message = f"**{message.scope}:** {formatted_message}" changes[message.type].append((_hash, formatted_message)) if message.breaking_descriptions: # Copy breaking change descriptions into changelog for paragraph in message.breaking_descriptions: changes["breaking"].append((_hash, paragraph)) elif message.bump == 3: # Major, but no breaking descriptions, use commit subject instead changes["breaking"].append((_hash, message.descriptions[0])) except UnknownCommitMessageStyleError as err: logger.debug(f"Ignoring UnknownCommitMessageStyleError: {err}") return changes