Skip to content

feat(commit-parser): enable parsers to flag commit to be ignored for changelog #1108

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

Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions src/semantic_release/changelog/release_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from git.objects.tag import TagObject

from semantic_release.commit_parser import ParseError
from semantic_release.commit_parser.token import ParsedCommit
from semantic_release.enums import LevelBump
from semantic_release.version.algorithm import tags_and_versions

Expand Down Expand Up @@ -136,6 +137,23 @@ def from_git_history(
)
continue

if (
isinstance(parse_result, ParsedCommit)
and not parse_result.include_in_changelog
):
log.info(
str.join(
" ",
[
"Excluding commit %s (%s) because parser determined",
"it should not included in the changelog",
],
),
commit.hexsha[:8],
commit_message.replace("\n", " ")[:20],
)
continue

if the_version is None:
log.info(
"[Unreleased] adding '%s' commit(%s) to list",
Expand Down
3 changes: 3 additions & 0 deletions src/semantic_release/commit_parser/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ParsedMessageResult(NamedTuple):
descriptions: tuple[str, ...]
breaking_descriptions: tuple[str, ...] = ()
linked_merge_request: str = ""
include_in_changelog: bool = True


class ParsedCommit(NamedTuple):
Expand All @@ -28,6 +29,7 @@ class ParsedCommit(NamedTuple):
breaking_descriptions: list[str]
commit: Commit
linked_merge_request: str = ""
include_in_changelog: bool = True

@property
def message(self) -> str:
Expand Down Expand Up @@ -60,6 +62,7 @@ def from_parsed_message_result(
breaking_descriptions=list(parsed_message_result.breaking_descriptions),
commit=commit,
linked_merge_request=parsed_message_result.linked_merge_request,
include_in_changelog=parsed_message_result.include_in_changelog,
)


Expand Down
85 changes: 85 additions & 0 deletions tests/e2e/cmd_changelog/test_changelog_custom_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from __future__ import annotations

import os
from typing import TYPE_CHECKING

import pytest
from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture

from semantic_release.changelog.context import ChangelogMode
from semantic_release.cli.commands.main import main

from tests.const import CHANGELOG_SUBCMD, MAIN_PROG_NAME
from tests.fixtures.repos import repo_w_no_tags_angular_commits
from tests.util import (
CustomAngularParserWithIgnorePatterns,
assert_successful_exit_code,
)

if TYPE_CHECKING:
from pathlib import Path

from click.testing import CliRunner

from tests.fixtures.example_project import UpdatePyprojectTomlFn, UseCustomParserFn
from tests.fixtures.git_repo import BuiltRepoResult, GetCommitDefFn


@pytest.mark.parametrize(
"repo_result", [lazy_fixture(repo_w_no_tags_angular_commits.__name__)]
)
def test_changelog_custom_parser_remove_from_changelog(
repo_result: BuiltRepoResult,
cli_runner: CliRunner,
update_pyproject_toml: UpdatePyprojectTomlFn,
use_custom_parser: UseCustomParserFn,
get_commit_def_of_angular_commit: GetCommitDefFn,
changelog_md_file: Path,
default_md_changelog_insertion_flag: str,
):
"""
Given when a changelog filtering custom parser is configured
When provided a commit message that matches the ignore syntax
Then the commit message is not included in the resulting changelog
"""
ignored_commit_def = get_commit_def_of_angular_commit(
"chore: do not include me in the changelog"
)

# Because we are in init mode, the insertion flag is not present in the changelog
# we must take it out manually because our repo generation fixture includes it automatically
with changelog_md_file.open(newline=os.linesep) as rfd:
# use os.linesep here because the insertion flag is os-specific
# but convert the content to universal newlines for comparison
expected_changelog_content = (
rfd.read()
.replace(f"{default_md_changelog_insertion_flag}{os.linesep}", "")
.replace("\r", "")
)

# Set the project configurations
update_pyproject_toml(
"tool.semantic_release.changelog.mode", ChangelogMode.INIT.value
)
use_custom_parser(
f"{CustomAngularParserWithIgnorePatterns.__module__}:{CustomAngularParserWithIgnorePatterns.__name__}"
)

# Setup: add the commit to be ignored
repo_result["repo"].git.commit(m=ignored_commit_def["msg"], a=True)

# Act
cli_cmd = [MAIN_PROG_NAME, CHANGELOG_SUBCMD]
result = cli_runner.invoke(main, cli_cmd[1:])

# Take measurement after action
actual_content = changelog_md_file.read_text()

# Evaluate
assert_successful_exit_code(result, cli_cmd)

# Verify that the changelog content does not include our commit
assert ignored_commit_def["desc"] not in actual_content

# Verify that the changelog content has not changed
assert expected_changelog_content == actual_content
27 changes: 25 additions & 2 deletions tests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@
from semantic_release.changelog.context import ChangelogMode, make_changelog_context
from semantic_release.changelog.release_history import ReleaseHistory
from semantic_release.commit_parser._base import CommitParser, ParserOptions
from semantic_release.commit_parser.token import ParsedCommit, ParseResult
from semantic_release.commit_parser.angular import AngularCommitParser
from semantic_release.commit_parser.token import (
ParsedCommit,
ParsedMessageResult,
ParseError,
ParseResult,
)
from semantic_release.enums import LevelBump

from tests.const import SUCCESS_EXIT_CODE
Expand All @@ -38,7 +44,6 @@
from git import Commit

from semantic_release.cli.config import RuntimeContext
from semantic_release.commit_parser.token import ParseError

_R = TypeVar("_R")

Expand Down Expand Up @@ -277,3 +282,21 @@ def parse(self, commit: Commit) -> ParsedCommit | ParseError:

class IncompleteCustomParser(CommitParser):
pass


class CustomAngularParserWithIgnorePatterns(AngularCommitParser):
def parse(self, commit: Commit) -> ParsedCommit | ParseError:
if not (parse_msg_result := super().parse_message(str(commit.message))):
return ParseError(commit, "Unable to parse commit")

return ParsedCommit.from_parsed_message_result(
commit,
ParsedMessageResult(
**{
**parse_msg_result._asdict(),
"include_in_changelog": bool(
not str(commit.message).startswith("chore")
),
}
),
)