noirbizarre/emotional

View on GitHub
emotional/gitlab.py

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
"""
See:
    - https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically
    - https://docs.gitlab.com/ee/user/markdown.html#gitlab-specific-references
"""

from __future__ import annotations

import re

from commitizen.git import GitCommit

from .config import EmotionalConfig

RE_ISSUE = re.compile(r"(?P<repository>(?P<owner>\w+)/(?P<project>\w+))?#(?P<issue>\d+)")

RE_ISSUE_URL = r"{config.gitlab_url}/(?P<owner>\w+)/(?P<project>\w+)/issues/(?P<issue>\d+)"
RE_CLOSING_PATTERN = (
    r"\b((?:"
    r"[Cc]los(?:e[sd]?|ing)"
    r"|\b[Ff]ix(?:e[sd]|ing)?"
    r"|\b[Rr]esolv(?:e[sd]?|ing)"
    r"|\b[Ii]mplement(?:s|ed|ing)?"
    r")(:?)"
    r"\s+"
    r"(?:"
    r"(?:issues? +)"
    r"?%{issue_ref}"
    r"(?:(?: *,? +and +| *,? *)?)|([A-Z][A-Z0-9_]+-\d+))+)"
)


def changelog_message_hook(
    config: EmotionalConfig, parsed_message: dict, commit: GitCommit
) -> dict:
    if config.gitlab is None:
        return parsed_message

    def urlize_issue(match: re.Match[str]) -> str:
        repository = match.group("repository") or config.gitlab
        label = match.group(0)
        issue = match.group("issue")
        return f"[{label}]({config.gitlab_url}/{repository}/issues/{issue})"

    for field in "message", "scope":
        value = parsed_message[field]
        if value:
            parsed_message[field] = RE_ISSUE.sub(urlize_issue, value)

    for field in "body", "footers":
        body = parsed_message.get(field)
        if body:
            for match in RE_ISSUE.finditer(body):
                parsed_message["message"] += f" {urlize_issue(match)}"

    return parsed_message