Repository: relekang/python-semantic-release
Pull request #249 (2020-06-29 16:32:56)

fix: commit all files with bumped versions
Fix a bug in relating to #246 which was preventing semantic release from making commits in projects with multiple version variables/patterns.

This commit fixes the bug, adds tests, and improves logging output.


Patch:
diff --git a/semantic_release/history/__init__.py b/semantic_release/history/__init__.py
index a0e1346..2f8c4bf 100644
--- a/semantic_release/history/__init__.py
+++ b/semantic_release/history/__init__.py
@@ -68,14 +68,14 @@ class VersionPattern:
         should be the same version in each place), but it falls on the caller 
         to check for this condition.
         """
-        logger.debug(
-            f"Looking for current version in {self.path}, pattern: {self.pattern}"
-        )
-
         with open(self.path, "r") as f:
             content = f.read()
 
         versions = {m.group(1) for m in re.finditer(self.pattern, content)}
+
+        logger.debug(
+                f"Parsing current version: path={self.path!r} pattern={self.pattern!r} num_matches={len(versions)}"
+        )
         return versions
 
     def replace(self, new_version: str):
@@ -87,10 +87,12 @@ class VersionPattern:
 
         :param new_version: The new version number as a string
         """
+        n = 0
         with open(self.path, "r") as f:
             old_content = f.read()
 
         def swap_version(m):
+            nonlocal n; n += 1
             s = m.string
             i, j = m.span()
             ii, jj = m.span(1)
@@ -98,6 +100,10 @@ class VersionPattern:
 
         new_content = re.sub(self.pattern, swap_version, old_content)
 
+        logger.debug(
+                f"Writing new version number: path={self.path!r} pattern={self.pattern!r} num_matches={n!r}"
+        )
+
         with open(self.path, mode="w") as f:
             f.write(new_content)
 
diff --git a/semantic_release/vcs_helpers.py b/semantic_release/vcs_helpers.py
index 60d7ad5..b977124 100644
--- a/semantic_release/vcs_helpers.py
+++ b/semantic_release/vcs_helpers.py
@@ -131,6 +131,8 @@ def commit_new_version(version: str):
 
     :param version: Version number to be used in the commit message.
     """
+    from .history import load_version_patterns
+
     commit_subject = config.get("commit_subject")
     message = commit_subject.format(version=version)
 
@@ -142,11 +144,10 @@ def commit_new_version(version: str):
 
     commit_author = config.get("commit_author", "semantic-release <semantic-release>",)
 
-    version_file = config.get("version_variable").split(":")[0]
-    # get actual path to filename, to allow running cmd from subdir of git root
-    version_filepath = PurePath(os.getcwd(), version_file).relative_to(repo.working_dir)
+    for pattern in load_version_patterns():
+        git_path = PurePath(os.getcwd(), pattern.path).relative_to(repo.working_dir)
+        repo.git.add(str(git_path))
 
-    repo.git.add(str(version_filepath))
     return repo.git.commit(m=message, author=commit_author)
 
 


Test patch:
diff --git a/tests/test_vcs_helpers.py b/tests/test_vcs_helpers.py
index dd3c469..210eb8f 100644
--- a/tests/test_vcs_helpers.py
+++ b/tests/test_vcs_helpers.py
@@ -19,7 +19,7 @@ from semantic_release.vcs_helpers import (
     tag_new_version,
 )
 
-from . import mock
+from . import mock, wrapped_config_get
 
 
 @pytest.fixture
@@ -37,33 +37,72 @@ def test_first_commit_is_not_initial_commit():
     assert next(get_commit_log()) != "Initial commit"
 
 
-def test_add_and_commit(mock_git):
-    commit_new_version("1.0.0")
-    pathfile = os.path.join("semantic_release", "__init__.py")
-    mock_git.add.assert_called_once_with(pathfile)
-    mock_git.commit.assert_called_once_with(
-        m="1.0.0\n\nAutomatically generated by python-semantic-release",
-        author="semantic-release <semantic-release>",
+@pytest.mark.parametrize(
+        'params', [
+
+            # Basic usage:
+            dict(
+                version='1.0.0',
+                config = dict(
+                    version_variable='path:---',
+                ),
+                add_paths = [
+                    'path',
+                ],
+                commit_args = dict(
+                    m="1.0.0\n\nAutomatically generated by python-semantic-release",
+                    author="semantic-release <semantic-release>",
+                ),
+            ),
+
+            # With author:
+            dict(
+                version='1.0.0',
+                config = dict(
+                    version_variable='path:---',
+                    commit_author='Alice <alice@example.com>',
+                ),
+                add_paths = [
+                    'path',
+                ],
+                commit_args = dict(
+                    m="1.0.0\n\nAutomatically generated by python-semantic-release",
+                    author='Alice <alice@example.com>',
+                ),
+            ),
+
+            # With multiple version paths:
+            dict(
+                version='1.0.0',
+                config = dict(
+                    version_variable=[
+                        'path1:---',
+                        'path2:---',
+                    ]
+                ),
+                add_paths = [
+                    'path1',
+                    'path2',
+                ],
+                commit_args = dict(
+                    m="1.0.0\n\nAutomatically generated by python-semantic-release",
+                    author="semantic-release <semantic-release>",
+                ),
+            ),
+        ]
+)
+def test_add_and_commit(mock_git, mocker, params):
+    mocker.patch(
+            "semantic_release.vcs_helpers.config.get",
+            wrapped_config_get(**params['config'])
     )
 
+    commit_new_version(params['version'])
 
-def test_add_and_commit_with_author(mocker, mock_git):
-    orig = config.get
-
-    def wrapped_config_get(*args, **kwargs):
-        if args[0] == "commit_author":
-            return "foo <bar@foo.com>"
+    for path in params['add_paths']:
+        mock_git.add.assert_any_call(path)
 
-        return orig(*args, **kwargs)
-
-    mocker.patch("semantic_release.vcs_helpers.config.get", wrapped_config_get)
-
-    commit_new_version("1.0.0")
-
-    mock_git.commit.assert_called_once_with(
-        m="1.0.0\n\nAutomatically generated by python-semantic-release",
-        author="foo <bar@foo.com>",
-    )
+    mock_git.commit.assert_called_once_with(**params['commit_args'])
 
 
 def test_tag_new_version(mock_git):