Skip to content

Commit

Permalink
feat(changelog)!: Merge similar commits within group (#197)
Browse files Browse the repository at this point in the history
When changelog contain commits like `Text (#123)` and `Text (#456)` in
some group (Features, Fixes, Refactoring, Other) _badabump_ now will
merge those commits into `Text (#123, #456)` line.

Fixes: #189
  • Loading branch information
playpauseandstop authored Nov 3, 2022
1 parent 41cb2b8 commit 3c4037e
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/actions/install_python_and_poetry/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ runs:
run: "pipx install --python='${{ steps.python.outputs.python-path }}' poetry==${{ inputs.poetry-version }}"

- name: "Cache venv"
uses: "actions/[email protected].10"
uses: "actions/[email protected].11"
with:
path: "./.venv/"
key: "venv-${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ hashFiles('poetry.lock') }}${{ inputs.cache-key-suffix }}"
2 changes: 1 addition & 1 deletion .github/actions/run_pre_commit/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ runs:
shell: "bash"

- name: "Cache mypy"
uses: "actions/[email protected].10"
uses: "actions/[email protected].11"
with:
path: "./.mypy_cache/"
key: "mypy-${{ runner.os }}-${{ inputs.python-version }}"
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/run_tox/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ runs:
shell: "bash"

- name: "Cache tox"
uses: "actions/[email protected].10"
uses: "actions/[email protected].11"
with:
path: "./.tox/"
key: "tox-${{ inputs.python-version }}"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
run: "poetry run python3 -m badabump --ci ${{ github.event.inputs.args }}"

- id: "token"
uses: "tibdex/[email protected]"
uses: "tibdex/[email protected].0"
with:
app_id: "${{ secrets.BADABUMP_APP_ID }}"
private_key: "${{ secrets.BADABUMP_APP_PRIVATE_KEY }}"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release_tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:

steps:
- id: "token"
uses: "tibdex/[email protected]"
uses: "tibdex/[email protected].0"
with:
app_id: "${{ secrets.BADABUMP_APP_ID }}"
private_key: "${{ secrets.BADABUMP_APP_PRIVATE_KEY }}"
Expand Down
67 changes: 58 additions & 9 deletions src/badabump/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
r"^(Closes|Fixes|Issue|Ref|Relates): (?P<issue>.+)$", re.M
)

FORMATTED_COMMIT_WITH_PR_RE = re.compile(
r"^(?P<formatted_commit>.*) \(\#(?P<pr_number>\d+)\)$"
)

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -202,16 +206,12 @@ def format_block(
return "\n\n".join((header, items))

def format_commits(commits: Iterator[ConventionalCommit]) -> str:
formatted_commits = []
for commit in commits:
formatted_commit = commit.format(
format_type, ignore_footer_urls=ignore_footer_urls
return "\n".join(
ul_li(item)
for item in prepare_formatted_commits(
commits, format_type, ignore_footer_urls=ignore_footer_urls
)
if formatted_commit in formatted_commits:
continue
formatted_commits.append(formatted_commit)

return "\n".join(ul_li(item) for item in formatted_commits)
)

features = format_block("Features:", self.feature_commits)
fixes = format_block("Fixes:", self.fix_commits)
Expand Down Expand Up @@ -279,6 +279,55 @@ def markdown_header(value: str, *, level: int) -> str:
return f'{"#" * level} {value}'


def prepare_formatted_commits(
commits: Iterator[ConventionalCommit],
format_type: FormatTypeEnum,
*,
ignore_footer_urls: bool,
) -> List[str]:
storage = []

for commit in commits:
# First, format commit as asked
formatted_commit = commit.format(
format_type, ignore_footer_urls=ignore_footer_urls
)

# Next, check whether it is already added to the storage. If previously
# added - do nothing
if formatted_commit in storage:
continue

# If not yet added, check if commit match regex "with PR"
with_pr_matched = FORMATTED_COMMIT_WITH_PR_RE.match(formatted_commit)
# If not matched - just append formatted commit to the storage
if not with_pr_matched:
storage.append(formatted_commit)
continue

# Now, attempt to match other commit in storage
with_pr_data = with_pr_matched.groupdict()
with_pr_prefix = with_pr_data["formatted_commit"]
other_idx = -1

for idx, other_commit in enumerate(storage):
if other_commit.startswith(with_pr_prefix):
other_idx = idx
break

# If such commit does not exist - just append formatted commit to the
# storage
if other_idx == -1:
storage.append(formatted_commit)
continue

# Otherwise, merge commit message of other commit and current commit
pr_number = with_pr_data["pr_number"]
storage[other_idx] = f"{storage[other_idx][:-1]}, #{pr_number})"

return storage


def rst_h1(value: str) -> str:
return rst_header(value, symbol="=")

Expand Down
62 changes: 37 additions & 25 deletions tests/test_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@
Fixes: DEV-1010
"""

DEFAULT_GIT_COMMITS = [
FEATURE_COMMIT,
FIX_COMMIT,
CI_BREAKING_COMMIT,
REFACTOR_COMMIT,
DOCS_SCOPE_COMMIT,
REFACTOR_COMMIT,
CI_BREAKING_COMMIT,
REFACTOR_COMMIT,
]


CHANGELOG_EMPTY = "No changes since last pre-release"

CHANGELOG_FILE_MD = """## Features:
Expand Down Expand Up @@ -121,10 +133,6 @@
UTCNOW = datetime.datetime.utcnow()


def test_changelog_duplicate_commits_with_prs():
...


@pytest.mark.parametrize(
"changelog_type, format_type, expected",
(
Expand Down Expand Up @@ -162,18 +170,7 @@ def test_changelog_empty(changelog_type, format_type, expected):
),
)
def test_changelog_format_file(format_type, is_pre_release, expected):
changelog = ChangeLog.from_git_commits(
[
FEATURE_COMMIT,
FIX_COMMIT,
CI_BREAKING_COMMIT,
REFACTOR_COMMIT,
DOCS_SCOPE_COMMIT,
REFACTOR_COMMIT,
CI_BREAKING_COMMIT,
REFACTOR_COMMIT,
]
)
changelog = ChangeLog.from_git_commits(DEFAULT_GIT_COMMITS)
content = changelog.format(
ChangeLogTypeEnum.changelog_file,
format_type,
Expand All @@ -192,15 +189,7 @@ def test_changelog_format_file(format_type, is_pre_release, expected):
),
)
def test_changelog_format_git(format_type, is_pre_release, expected):
changelog = ChangeLog.from_git_commits(
[
FEATURE_COMMIT,
FIX_COMMIT,
CI_BREAKING_COMMIT,
DOCS_SCOPE_COMMIT,
REFACTOR_COMMIT,
]
)
changelog = ChangeLog.from_git_commits(DEFAULT_GIT_COMMITS)
content = changelog.format(
ChangeLogTypeEnum.git_commit,
format_type,
Expand Down Expand Up @@ -247,6 +236,29 @@ def test_changelog_invalid_commit_non_strict_mode():
assert changelog.has_micro_change is True


def test_changelog_merge_similar_commits():
changelog = ChangeLog.from_git_commits(
[
"fix: Does not matter (#9999)",
f"{FIX_COMMIT} (#9000)",
f"{FIX_COMMIT} (#69)",
f"{FIX_COMMIT} (#42)",
]
)
content = changelog.format(
ChangeLogTypeEnum.changelog_file,
FormatTypeEnum.markdown,
is_pre_release=False,
)
assert (
content
== f"""## Fixes:
- {FIX_COMMIT[5:]} (#42, #69, #9000)
- Does not matter (#9999)"""
)


@pytest.mark.parametrize(
"fix_commit",
(
Expand Down

0 comments on commit 3c4037e

Please sign in to comment.