Uh oh!
There was an error while loading. Please reload this page.
fix(bump): flag sibling version mismatches under --check-consistency - #1978
fix(bump): flag sibling version mismatches under --check-consistency#1978bearomorphism wants to merge 2 commits into
Conversation
When `version_files` regex matches multiple version-shaped values in the same file (the typical pyproject.toml pattern with both `[tool.poetry].version` / `[project].version` and `[tool.commitizen].version`), the previous behaviour silently rewrote only the line containing the current version and left the sibling out-of-sync. Under `--check-consistency`, also fail when a matching line holds a different version-shaped value. The error names the offending line so users can either align the sources, narrow the `version_files` regex or drop `--check-consistency`. The default (`check_consistency = False`) is unchanged: only lines containing `current_version` are rewritten, others are passed through. Closescommitizen-tools#595 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@## master #1978 +/- ##
=======================================
Coverage 98.23% 98.24% =======================================
Files 61 61 Lines 2779 2790 +11 =======================================
+ Hits 2730 2741 +11
Misses 49 49 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Pull request overview
This PR tightens cz bump --check-consistency by detecting “sibling” version values in the same file when a broad version_files regex matches multiple version-like assignments (e.g., pyproject.toml with both [tool.poetry].version and [tool.commitizen].version). It aims to prevent silent version mismatches by raising an actionable error that points to the offending line(s).
Changes:
- Refactors
update_version_in_filesto enumerate lines and collect regex-matching lines that appear to contain a different version (only used whencheck_consistency=True). - Adds a conservative
_LIKELY_VERSION_VALUE_REto identify version-shaped values for the sibling mismatch detection. - Adds regression tests covering both the new
--check-consistencyfailure behavior and the legacy non-consistency behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
commitizen/bump.py | Adds sibling-version mismatch detection and enhanced consistency error messaging during version file updates. |
tests/test_bump_update_version_in_files.py | Adds regression tests for sibling mismatch detection and confirms legacy behavior when consistency checking is off. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
… preserve trailing whitespace
* skip _LIKELY_VERSION_VALUE_RE.search and inconsistent_lines tracking when check_consistency=False, so the legacy fast path stays unchanged as the PR claimed
* use rstrip('\r\n') instead of rstrip() so the reported line content matches the actual file content (preserves meaningful trailing whitespace)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Closes#595.
Why
commitizen/bump.py:update_version_in_filesapplies a regex pattern to each line of every file inversion_files. When a broad pattern such aspyproject.toml:versionis used, the regex legitimately matches multipleversion =lines — one under[tool.commitizen]holding the current commitizen-managed version, and one under[tool.poetry](or[project]for PEP-621) holding a potentially out-of-sync value maintained by a different tool. The old inner loop (mastercommitizen/bump.py:88-95) replaced thecurrent_versionstring whereverpattern.search(line)was true and setcurrent_version_foundonly when the replacement changed the line. The loop never inspected lines that matched the pattern but did not containcurrent_version, so the[tool.poetry].versionline was silently skipped — the bump proceeded,--check-consistencyraised no error, and the file was left with two different version strings.The original reporter @gpongelli observed the mismatch in commitizen 2.34.0; maintainer @Lee-W confirmed the reproduction and noted that the regex-based approach made a proper same-file consistency check architecturally difficult. A follow-up comment from @woile noted the workaround (use a more specific regex such as
pyproject.toml:\[tool\.poetry\][.\s\D]*^version). The triage audit on #1964 confirmed the issue is still present in master (v4.15.1): a pyproject.toml with[tool.poetry].version = "2.5.7"and[tool.commitizen].version = "2.5.2"runningcz bump --check-consistency --yesexits 0, bumps commitizen's version to2.6.0, and leaves the poetry version at2.5.7— still mismatched and silent.This PR narrows the fix: when
check_consistency=True, any line that (a) matches the version-files regex and (b) does not containcurrent_versionbut (c) contains a semver-shaped value is collected as aninconsistent_linesentry. If any such entries exist after processing the file,CurrentVersionNotFoundErroris raised with a message that names the file path, the line number, and the offending version string so the user knows exactly which tool is out of sync. The default path (check_consistency=False) is completely unchanged.What changed
commitizen/bump.pyinconsistent_lines; add_LIKELY_VERSION_VALUE_REmodule-level constant; raiseCurrentVersionNotFoundErrorwith line-level details whencheck_consistency=Trueand sibling mismatches are foundtests/test_bump_update_version_in_files.pycheck_consistencybehaviour leaves sibling untouched without raisingHow it works
Two-phase consistency check. The existing check (master
commitizen/bump.py:98-103) validates that at least one line was updated — i.e.,current_versionwas found. The new check is a second gate that validates that no other line looks like it stores a different version. Both checks run after the full file is read, before any write occurs, so a failed consistency check always leaves the file on disk untouched._LIKELY_VERSION_VALUE_REis intentionally conservative. The regex\d+\.\d+\.\d+(?:[\w.\-+]*)matches the canonicalMAJOR.MINOR.PATCHsemver shape (plus optional pre-release and build-metadata suffixes). It deliberately does not match bare integers, Python import paths, comment prose, or genericversion =keywords without a version-shaped right-hand side. This prevents false positives from unrelated occurrences of the wordversionin configuration comments or code that the user's regex happens to match.Line numbers are 1-based in the error message.
enumerate(version_file, 1)is used so that the reported line number matches what editors andgrep -nreport. This makes the error message actionable without requiring the user to open the file and count from zero.check_consistency=Falsepath is byte-for-byte identical to the old behaviour. Theinconsistent_lineslist is only evaluated inside theif check_consistencyblock. No code runs on the fast path for users who do not pass--check-consistency.Why not a TOML-aware parser?@woile and @Lee-W discussed this in the issue thread: a format-specific parser (TOML, JSON, YAML) would add maintenance burden and break the general
version_files: any-filecontract. The regex approach is deliberately format-agnostic; the conservative_LIKELY_VERSION_VALUE_REsecondary filter is enough to catch the commonpyproject.tomlcase without committing to TOML semantics.Backward compatibility
check_consistency=False(the default) preserves the legacy behaviour exactly: only lines containingcurrent_versionare rewritten; sibling lines are left alone; no exception is raised.CurrentVersionNotFoundErrormessage format is a superset of the existing one — both state the file path and the fact that the version wasn't found; the new message additionally names the offending line.update_version_in_filestests and bump command tests pass unchanged.--check-consistencyis the existing flag whose scope is extended.Checklist
Was generative AI tooling used to co-author this PR?
Generated-by: Claude following the guidelines
Code Changes
uv run poe alllocally to ensure this change passes linter check and testsExpected Behavior
pyproject.tomlwith[tool.poetry].version = "2.5.7"and[tool.commitizen].version = "2.5.2",version_files = ["pyproject.toml:version"],cz bump --check-consistencyline 2: version = "2.5.7"as the inconsistent entry; file is not modifiedcz bump(no--check-consistency)2.5.7; no exception — legacy behaviour preservedversion =line in file,cz bump --check-consistencyinconsistent_linesis empty, no new errorversion_filesregex narrowed topyproject.toml:\[tool\.commitizen\]inconsistent_linesis empty regardless of--check-consistencySteps to Test This Pull Request
Additional Context
This fix was scoped during the issue audit in #1964, which confirmed the silent-pass behaviour is still present in master (v4.15.1). The implementation deliberately stays within the existing regex-based
version_filescontract rather than introducing format-specific parsing, consistent with the design discussion in the #595 thread (@woile, @Lee-W). Users whoseversion_filesregex is already specific enough to target only the commitizen-managed line are unaffected — their_LIKELY_VERSION_VALUE_REscan will find no inconsistent lines.