Repository: relekang/python-semantic-release
Pull request #495 (2022-08-23 21:00:36)

fix: account for trailing newlines in commit messages
Resolves issue #490

bernardcooke53: Note this is a bit brittle - but getting this out & preventing countless infinite loops in automated CI seems like a higher priority. Will need to check that commit messages are stripped for comparison. Changing in `get_commit_log` seems to break tests for changelog generation, so better to strip it at the comparison site for now imo.

The bug causing #490 is that in git logs, commit messages have a trailing newline, which isn't accounted for in the message built up from config.

Have added some extra test cases to check for this.

@d-ryzhikov - hopefully this doesn't break the testing/use case you had for implementing [your PR](https://github.com/relekang/python-semantic-release/pull/488)?
@zjgoodman - does this fix #490 for you?
danth: Going to merge this straight away so that we don't affect any more repositories with the infinite looping.

Patch:
diff --git a/semantic_release/history/logs.py b/semantic_release/history/logs.py
index 3e37dad..110c3a5 100644
--- a/semantic_release/history/logs.py
+++ b/semantic_release/history/logs.py
@@ -38,7 +38,9 @@ def evaluate_version_bump(current_version: str, force: str = None) -> Optional[s
 
     current_version_commit = get_formatted_commit(current_version)
     for _hash, commit_message in get_commit_log(current_version):
-        if commit_message == current_version_commit:
+        # 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(
@@ -115,7 +117,12 @@ def generate_changelog(
                 )
                 found_the_release = True
 
-        if from_version_commit and commit_message == from_version_commit:
+        # 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


Test patch:
diff --git a/tests/history/__init__.py b/tests/history/__init__.py
index d4cbf04..5cf16e4 100644
--- a/tests/history/__init__.py
+++ b/tests/history/__init__.py
@@ -39,11 +39,11 @@ MINOR_AND_PATCH_COMMIT_MESSAGES = [MINOR, PATCH]
 PATCH_COMMIT_MESSAGES = [PATCH, PATCH]
 MAJOR_LAST_RELEASE_MINOR_AFTER = [
     MINOR,
-    ("22", "1.1.0\n\nAutomatically generated by python-semantic-release"),
+    ("22", "1.1.0\n\nAutomatically generated by python-semantic-release\n"),
     MAJOR,
 ]
 MAJOR_MENTIONING_LAST_VERSION = [
     MAJOR_MENTIONING_1_0_0,
-    ("22", "1.0.0\n\nAutomatically generated by python-semantic-release"),
+    ("22", "1.0.0\n\nAutomatically generated by python-semantic-release\n"),
     MAJOR,
 ]
diff --git a/tests/history/test_version_bump.py b/tests/history/test_version_bump.py
index 4f9d6c1..b168b85 100644
--- a/tests/history/test_version_bump.py
+++ b/tests/history/test_version_bump.py
@@ -107,6 +107,44 @@ def test_should_search_for_release_with_commit_subject_pattern():
     assert evaluate_version_bump("1.0.0") == "minor"
 
 
+@mock.patch(
+    "semantic_release.history.config.get",
+    wrapped_config_get(
+        commit_subject="chore(release): {version}",
+        commit_message="Custom message",
+    ),
+)
+@mock.patch(
+    "semantic_release.history.logs.get_commit_log",
+    lambda *a, **kw: [
+        MINOR,
+        ("777", "chore(release): 1.0.0\n\nCustom message \n"),
+        MAJOR,
+    ],
+)
+def test_should_search_for_release_with_commit_subject_pattern_including_whitespace():
+    assert evaluate_version_bump("1.0.0") == "minor"
+
+
+@mock.patch(
+    "semantic_release.history.config.get",
+    wrapped_config_get(
+        commit_subject="chore(release): {version}",
+        commit_message="Custom message ",
+    ),
+)
+@mock.patch(
+    "semantic_release.history.logs.get_commit_log",
+    lambda *a, **kw: [
+        MINOR,
+        ("777", "chore(release): 1.0.0\n\nCustom message\n"),
+        MAJOR,
+    ],
+)
+def test_should_search_for_release_with_commit_subject_pattern_including_whitespace_in_configuration():
+    assert evaluate_version_bump("1.0.0") == "minor"
+
+
 @mock.patch(
     "semantic_release.history.config.get",
     wrapped_config_get(
@@ -120,3 +158,18 @@ def test_should_search_for_release_with_commit_subject_pattern():
 )
 def test_should_not_search_for_release_without_commit_subject_pattern():
     assert evaluate_version_bump("1.0.0") == "major"
+
+
+@mock.patch(
+    "semantic_release.history.config.get",
+    wrapped_config_get(
+        commit_subject="chore(release): {version}",
+        commit_message="Custom message",
+    ),
+)
+@mock.patch(
+    "semantic_release.history.logs.get_commit_log",
+    lambda *a, **kw: [MINOR, ("777", "1.0.0\n"), MAJOR],
+)
+def test_should_not_search_for_release_without_commit_subject_pattern_including_whitespace():
+    assert evaluate_version_bump("1.0.0") == "major"