Skip to content
Open
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
43 changes: 35 additions & 8 deletions commitizen/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,27 @@ def update_version_in_files(

for path, pattern in _resolve_files_and_regexes(version_files, current_version):
current_version_found = False
inconsistent_lines: list[tuple[int, str]] = []
bumped_lines = []

with open(path, encoding=encoding) as version_file:
for line in version_file:
bumped_line = (
line.replace(current_version, new_version)
if pattern.search(line)
else line
)

current_version_found = current_version_found or bumped_line != line
for lineno, line in enumerate(version_file, 1):
if pattern.search(line):
if current_version in line:
bumped_line = line.replace(current_version, new_version)
current_version_found = True
else:
# The version-files regex matched this line, but the
# current version isn't on it. If the line looks like
# it sets a version-shaped value, this is almost
# certainly an inconsistent source we'd otherwise miss
# silently (#595).
bumped_line = line
if check_consistency and _LIKELY_VERSION_VALUE_RE.search(line):
inconsistent_lines.append((lineno, line.rstrip("\r\n")))
else:
Comment thread
bearomorphism marked this conversation as resolved.
bumped_line = line

bumped_lines.append(bumped_line)

if check_consistency and not current_version_found:
Expand All @@ -102,6 +112,17 @@ def update_version_in_files(
"version_files are possibly inconsistent."
)

if check_consistency and inconsistent_lines:
details = "\n".join(f" line {n}: {text}" for n, text in inconsistent_lines)
raise CurrentVersionNotFoundError(
f"Found line(s) in {path} matching the version regex but "
f"holding a version other than {current_version}:\n"
f"{details}\n"
"This usually means another tool (e.g. poetry, pep621) is "
"tracking a different version. Either align them, narrow the "
"`version_files` regex, or drop `--check-consistency`."
)

bumped_version_file_content = "".join(bumped_lines)

# Write the file out again
Expand All @@ -112,6 +133,12 @@ def update_version_in_files(
return updated_files


# Lines that look like ``key = "1.2.3"`` / ``key: 1.2.3-rc.0`` etc. -- enough
# to catch the typical pyproject.toml ``[tool.poetry].version = "..."`` and
# ``[project].version = "..."`` cases handled by ``--check-consistency``.
_LIKELY_VERSION_VALUE_RE = re.compile(r"\d+\.\d+\.\d+(?:[\w.\-+]*)")


def _resolve_files_and_regexes(
patterns: Iterable[str], version: str
) -> Generator[tuple[str, re.Pattern], None, None]:
Expand Down
55 changes: 55 additions & 0 deletions tests/test_bump_update_version_in_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,61 @@ def test_update_version_in_files_with_check_consistency_true_failure(
assert expected_msg in str(excinfo.value)


def test_update_version_in_files_check_consistency_detects_sibling_version(tmp_path):
"""Regression test for #595: when a single file's version regex matches
multiple version-shaped values (typical pyproject.toml with
``[tool.poetry].version`` and ``[tool.commitizen].version``),
``--check-consistency`` should flag the lines whose version doesn't match
the current one instead of silently leaving the sibling out of date.
"""
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(
'[tool.poetry]\nversion = "2.5.7"\n\n[tool.commitizen]\nversion = "2.5.2"\n',
encoding="utf-8",
)

with pytest.raises(CurrentVersionNotFoundError) as excinfo:
bump.update_version_in_files(
current_version="2.5.2",
new_version="2.6.0",
version_files=[f"{pyproject}:version"],
check_consistency=True,
encoding="utf-8",
)

msg = str(excinfo.value)
assert "2.5.7" in msg
assert "2.5.2" in msg
# The original file must NOT have been rewritten when consistency fails.
# (We re-read it to confirm the bump didn't proceed.)
assert '"2.5.2"' in pyproject.read_text(encoding="utf-8")


def test_update_version_in_files_check_consistency_off_keeps_legacy_behaviour(
tmp_path,
):
"""Without ``check_consistency``, the old behaviour is preserved: only
the lines that contain the current version are updated, and sibling
versions are left alone (no exception)."""
pyproject = tmp_path / "pyproject.toml"
pyproject.write_text(
'[tool.poetry]\nversion = "2.5.7"\n\n[tool.commitizen]\nversion = "2.5.2"\n',
encoding="utf-8",
)

bump.update_version_in_files(
current_version="2.5.2",
new_version="2.6.0",
version_files=[f"{pyproject}:version"],
check_consistency=False,
encoding="utf-8",
)

rewritten = pyproject.read_text(encoding="utf-8")
assert '[tool.poetry]\nversion = "2.5.7"' in rewritten
assert '[tool.commitizen]\nversion = "2.6.0"' in rewritten


@pytest.mark.parametrize(
("encoding", "filename"),
[
Expand Down
Loading