Skip to content

Commit 752a6a6

Browse files
committed
feat(bump): version_files now support glob patterns (fix commitizen-tools#1067)
1 parent 3015a76 commit 752a6a6

File tree

8 files changed

+92
-57
lines changed

8 files changed

+92
-57
lines changed

commitizen/bump.py

+28-11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import re
55
from collections import OrderedDict
6+
from glob import iglob
67
from string import Template
78
from typing import cast
89

@@ -53,23 +54,20 @@ def update_version_in_files(
5354
*,
5455
check_consistency: bool = False,
5556
encoding: str = encoding,
56-
) -> None:
57+
) -> list[str]:
5758
"""Change old version to the new one in every file given.
5859
5960
Note that this version is not the tag formatted one.
6061
So for example, your tag could look like `v1.0.0` while your version in
6162
the package like `1.0.0`.
63+
64+
Returns the list of updated files.
6265
"""
6366
# TODO: separate check step and write step
64-
for location in files:
65-
drive, tail = os.path.splitdrive(location)
66-
path, _, regex = tail.partition(":")
67-
filepath = drive + path
68-
if not regex:
69-
regex = _version_to_regex(current_version)
70-
67+
updated = []
68+
for path, regex in files_and_regexs(files, current_version):
7169
current_version_found, version_file = _bump_with_regex(
72-
filepath,
70+
path,
7371
current_version,
7472
new_version,
7573
regex,
@@ -78,14 +76,33 @@ def update_version_in_files(
7876

7977
if check_consistency and not current_version_found:
8078
raise CurrentVersionNotFoundError(
81-
f"Current version {current_version} is not found in {location}.\n"
79+
f"Current version {current_version} is not found in {path}.\n"
8280
"The version defined in commitizen configuration and the ones in "
8381
"version_files are possibly inconsistent."
8482
)
8583

8684
# Write the file out again
87-
with smart_open(filepath, "w", encoding=encoding) as file:
85+
with smart_open(path, "w", encoding=encoding) as file:
8886
file.write(version_file)
87+
updated.append(path)
88+
return updated
89+
90+
91+
def files_and_regexs(patterns: list[str], version: str) -> list[tuple[str, str]]:
92+
"""
93+
Resolve all distinct files with their regexp from a list of glob patterns with optional regexp
94+
"""
95+
out = []
96+
for pattern in patterns:
97+
drive, tail = os.path.splitdrive(pattern)
98+
path, _, regex = tail.partition(":")
99+
filepath = drive + path
100+
if not regex:
101+
regex = _version_to_regex(version)
102+
103+
for path in iglob(filepath):
104+
out.append((path, regex))
105+
return sorted(list(set(out)))
89106

90107

91108
def _bump_with_regex(

commitizen/commands/bump.py

+24-41
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
from __future__ import annotations
22

3-
import os
43
import warnings
54
from logging import getLogger
65

76
import questionary
87

9-
from commitizen import bump, cmd, factory, git, hooks, out
8+
from commitizen import bump, factory, git, hooks, out
109
from commitizen.commands.changelog import Changelog
1110
from commitizen.config import BaseConfig
1211
from commitizen.exceptions import (
@@ -286,56 +285,39 @@ def __call__(self) -> None: # noqa: C901
286285
"The commits found are not eligible to be bumped"
287286
)
288287

288+
files: list[str] = []
289289
if self.changelog:
290+
args = {
291+
"unreleased_version": new_tag_version,
292+
"template": self.template,
293+
"extras": self.extras,
294+
"incremental": True,
295+
"dry_run": dry_run,
296+
}
290297
if self.changelog_to_stdout:
291-
changelog_cmd = Changelog(
292-
self.config,
293-
{
294-
"unreleased_version": new_tag_version,
295-
"template": self.template,
296-
"extras": self.extras,
297-
"incremental": True,
298-
"dry_run": True,
299-
},
300-
)
298+
changelog_cmd = Changelog(self.config, {**args, "dry_run": True})
301299
try:
302300
changelog_cmd()
303301
except DryRunExit:
304302
pass
305-
changelog_cmd = Changelog(
306-
self.config,
307-
{
308-
"unreleased_version": new_tag_version,
309-
"incremental": True,
310-
"dry_run": dry_run,
311-
"template": self.template,
312-
"extras": self.extras,
313-
"file_name": self.file_name,
314-
},
315-
)
303+
304+
args["file_name"] = self.file_name
305+
changelog_cmd = Changelog(self.config, args)
316306
changelog_cmd()
317-
file_names = []
318-
for file_name in version_files:
319-
drive, tail = os.path.splitdrive(file_name)
320-
path, _, regex = tail.partition(":")
321-
path = drive + path if path != "" else drive + regex
322-
file_names.append(path)
323-
git_add_changelog_and_version_files_command = (
324-
f"git add {changelog_cmd.file_name} "
325-
f"{' '.join(name for name in file_names)}"
326-
)
327-
c = cmd.run(git_add_changelog_and_version_files_command)
307+
files.append(changelog_cmd.file_name)
328308

329309
# Do not perform operations over files or git.
330310
if dry_run:
331311
raise DryRunExit()
332312

333-
bump.update_version_in_files(
334-
str(current_version),
335-
str(new_version),
336-
version_files,
337-
check_consistency=self.check_consistency,
338-
encoding=self.encoding,
313+
files.extend(
314+
bump.update_version_in_files(
315+
str(current_version),
316+
str(new_version),
317+
version_files,
318+
check_consistency=self.check_consistency,
319+
encoding=self.encoding,
320+
)
339321
)
340322

341323
provider.set_version(str(new_version))
@@ -358,12 +340,13 @@ def __call__(self) -> None: # noqa: C901
358340
raise ExpectedExit()
359341

360342
# FIXME: check if any changes have been staged
343+
git.add(*files)
361344
c = git.commit(message, args=self._get_commit_args())
362345
if self.retry and c.return_code != 0 and self.changelog:
363346
# Maybe pre-commit reformatted some files? Retry once
364347
logger.debug("1st git.commit error: %s", c.err)
365348
logger.info("1st commit attempt failed; retrying once")
366-
cmd.run(git_add_changelog_and_version_files_command)
349+
git.add(*files)
367350
c = git.commit(message, args=self._get_commit_args())
368351
if c.return_code != 0:
369352
err = c.err.strip() or c.out

commitizen/commands/changelog.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from difflib import SequenceMatcher
55
from operator import itemgetter
66
from pathlib import Path
7-
from typing import Callable
7+
from typing import Callable, cast
88

99
from commitizen import bump, changelog, defaults, factory, git, out
1010

@@ -38,8 +38,8 @@ def __init__(self, config: BaseConfig, args):
3838
self.start_rev = args.get("start_rev") or self.config.settings.get(
3939
"changelog_start_rev"
4040
)
41-
self.file_name = args.get("file_name") or self.config.settings.get(
42-
"changelog_file"
41+
self.file_name = args.get("file_name") or cast(
42+
str, self.config.settings.get("changelog_file")
4343
)
4444
if not isinstance(self.file_name, str):
4545
raise NotAllowed(

commitizen/git.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ def tag(
9898
return c
9999

100100

101-
def add(args: str = "") -> cmd.Command:
102-
c = cmd.run(f"git add {args}")
101+
def add(*args: str) -> cmd.Command:
102+
c = cmd.run(f"git add {' '.join(args)}")
103103
return c
104104

105105

docs/bump.md

+4
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,10 @@ In the example above, we can see the reference `"setup.py:version"`.
474474
This means that it will find a file `setup.py` and will only make a change
475475
in a line containing the `version` substring.
476476
477+
!!! note
478+
Files can be specified using relative (to the execution) paths, absolute paths
479+
or glob patterns.
480+
477481
---
478482
479483
### `bump_message`

tests/commands/test_bump_command.py

+10
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,16 @@ def test_bump_with_git_to_stdout_arg(mocker: MockFixture, capsys, changelog_path
812812
""",
813813
id="version in __init__.py with regex",
814814
),
815+
pytest.param(
816+
"pyproject.toml",
817+
"*.toml:^version",
818+
"""
819+
[tool.poetry]
820+
name = "my_package"
821+
version = "0.1.0"
822+
""",
823+
id="version in pyproject.toml with glob and regex",
824+
),
815825
],
816826
)
817827
@pytest.mark.parametrize(

tests/test_bump_update_version_in_files.py

+18
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,21 @@ def test_multiplt_versions_to_bump(
207207
bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8")
208208
with open(multiple_versions_to_update_poetry_lock, encoding="utf-8") as f:
209209
file_regression.check(f.read(), extension=".toml")
210+
211+
212+
def test_update_version_in_globbed_files(commitizen_config_file, file_regression):
213+
old_version = "1.2.3"
214+
new_version = "2.0.0"
215+
other = commitizen_config_file.dirpath("other.toml")
216+
print(commitizen_config_file, other)
217+
copyfile(commitizen_config_file, other)
218+
219+
# Prepend full ppath as test assume absolute paths or cwd-relative
220+
version_files = [commitizen_config_file.dirpath("*.toml")]
221+
222+
bump.update_version_in_files(
223+
old_version, new_version, version_files, encoding="utf-8"
224+
)
225+
226+
for file in commitizen_config_file, other:
227+
file_regression.check(file.read_text("utf-8"), extension=".toml")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[tool.poetry]
2+
name = "commitizen"
3+
version = "2.0.0"

0 commit comments

Comments
 (0)