Skip to content

Commit f9f7f96

Browse files
committed
Add file filtering based on valid and invalid bumps
This commit introduces the ability to filter files based on whether the specified bump type is valid or not. It adds `valid_bumps` and `invalid_bumps` lists in the file configurations and adjusts the bumping process to consider these configurations. Tests are updated to reflect these new handling of valid and invalid bumps.
1 parent 34e4dc1 commit f9f7f96

File tree

3 files changed

+180
-114
lines changed

3 files changed

+180
-114
lines changed

bumpversion/bump.py

+6
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ def do_bump(
9595
ctx = get_context(config, version, next_version)
9696

9797
configured_files = resolve_file_config(config.files_to_modify, config.version_config)
98+
99+
if version_part:
100+
# filter the files that are not valid for this bump
101+
configured_files = [file for file in configured_files if version_part in file.file_change.valid_bumps]
102+
configured_files = [file for file in configured_files if version_part not in file.file_change.invalid_bumps]
103+
98104
modify_files(configured_files, version, next_version, ctx, dry_run)
99105
if config_file and config_file.suffix in {".cfg", ".ini"}:
100106
update_ini_config_file(config_file, config.current_version, next_version_str, dry_run) # pragma: no-coverage

bumpversion/files.py

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ def __init__(
9090
filename=file_change.filename,
9191
glob=file_change.glob,
9292
key_path=file_change.key_path,
93+
valid_bumps=file_change.valid_bumps,
94+
invalid_bumps=file_change.invalid_bumps,
9395
)
9496
self.version_config = VersionConfig(
9597
self.file_change.parse,

tests/test_bump.py

+172-114
Original file line numberDiff line numberDiff line change
@@ -18,138 +18,196 @@ def mock_context():
1818
return {"current_version": "1.2.3", "new_version": "1.2.4"}
1919

2020

21-
def test_get_next_version_with_new_version():
22-
"""Passing a new version should return that version."""
23-
# Arrange
24-
config, version_config, current_version = get_config_data({"current_version": "0.1.0"})
25-
version_part = "patch"
26-
new_version = "1.2.3"
27-
expected_next_version = version_config.parse("1.2.3")
28-
29-
# Act
30-
actual_next_version = bump.get_next_version(current_version, config, version_part, new_version)
21+
class TestGetNextVersion:
22+
"""Tests for the get_next_version function."""
3123

32-
# Assert
33-
assert actual_next_version == expected_next_version
34-
35-
36-
def test_get_next_version_with_version_part():
37-
"""If a version part is provided, it should return the increment of that part."""
38-
# Arrange
39-
config, version_config, current_version = get_config_data({"current_version": "0.1.0"})
40-
version_part = "major"
41-
new_version = None
42-
expected_next_version = version_config.parse("1.0.0")
24+
def test_passing_a_new_version_should_override_a_version_part(self):
25+
"""Passing a new version should return that version."""
26+
# Arrange
27+
config, version_config, current_version = get_config_data({"current_version": "0.1.0"})
28+
version_part = "patch"
29+
new_version = "1.2.3"
30+
expected_next_version = version_config.parse("1.2.3")
4331

44-
# Act
45-
actual_next_version = bump.get_next_version(current_version, config, version_part, new_version)
46-
47-
# Assert
48-
assert actual_next_version == expected_next_version
49-
50-
51-
def test_get_next_version_with_no_arguments():
52-
# Arrange
53-
current_version = MagicMock()
54-
config = MagicMock()
55-
version_part = None
56-
new_version = None
32+
# Act
33+
actual_next_version = bump.get_next_version(current_version, config, version_part, new_version)
5734

58-
# Act/Assert
59-
with pytest.raises(ConfigurationError):
60-
bump.get_next_version(current_version, config, version_part, new_version)
35+
# Assert
36+
assert actual_next_version == expected_next_version
6137

38+
def test_passing_version_part_should_increment_part(self):
39+
"""If a version part is provided, it should return the increment of that part."""
40+
# Arrange
41+
config, version_config, current_version = get_config_data({"current_version": "0.1.0"})
42+
version_part = "major"
43+
new_version = None
44+
expected_next_version = version_config.parse("1.0.0")
6245

63-
@patch("bumpversion.files.modify_files")
64-
@patch("bumpversion.bump.update_config_file")
65-
def test_do_bump_with_version_part(mock_update_config_file, mock_modify_files):
66-
# Arrange
67-
version_part = "major"
68-
new_version = None
69-
config, version_config, current_version = get_config_data(
70-
{"current_version": "1.2.3", "files": [{"filename": "foo.txt"}, {"filename": "bar.txt"}]}
71-
)
72-
config.scm_info = SCMInfo()
73-
dry_run = False
46+
# Act
47+
actual_next_version = bump.get_next_version(current_version, config, version_part, new_version)
48+
49+
# Assert
50+
assert actual_next_version == expected_next_version
51+
52+
def test_passing_no_arguments_raises_error(self):
53+
# Arrange
54+
current_version = MagicMock()
55+
config = MagicMock()
56+
version_part = None
57+
new_version = None
58+
59+
# Act/Assert
60+
with pytest.raises(ConfigurationError):
61+
bump.get_next_version(current_version, config, version_part, new_version)
62+
63+
64+
class TestDoBump:
65+
"""Tests for the do_bump function."""
66+
67+
@patch("bumpversion.files.modify_files")
68+
@patch("bumpversion.bump.update_config_file")
69+
def test_passing_version_part_increments_part(self, mock_update_config_file, mock_modify_files):
70+
# Arrange
71+
version_part = "major"
72+
new_version = None
73+
config, version_config, current_version = get_config_data(
74+
{
75+
"current_version": "1.2.3",
76+
"files": [{"filename": "foo.txt"}, {"filename": "bar.txt"}],
77+
}
78+
)
79+
config.scm_info = SCMInfo()
80+
dry_run = False
7481

75-
# Act
76-
bump.do_bump(version_part, new_version, config, dry_run=dry_run)
82+
# Act
83+
bump.do_bump(version_part, new_version, config, dry_run=dry_run)
7784

78-
# Assert
79-
mock_modify_files.assert_called_once()
80-
mock_update_config_file.assert_called_once()
81-
assert {f.file_change.filename for f in mock_modify_files.call_args[0][0]} == {
82-
"foo.txt",
83-
"bar.txt",
84-
}
85-
assert mock_update_config_file.call_args[0][0] is None
86-
assert mock_update_config_file.call_args[0][1] == config
87-
assert mock_update_config_file.call_args[0][2] == current_version
88-
assert mock_update_config_file.call_args[0][3] == current_version.bump(version_part)
89-
assert mock_update_config_file.call_args[0][5] is False
90-
91-
92-
@patch("bumpversion.files.modify_files")
93-
@patch("bumpversion.bump.update_config_file")
94-
def test_do_bump_with_new_version(mock_update_config_file, mock_modify_files):
95-
# Arrange
96-
version_part = None
97-
new_version = "2.0.0"
98-
config, version_config, current_version = get_config_data(
99-
{
100-
"current_version": "1.2.3",
85+
# Assert
86+
mock_modify_files.assert_called_once()
87+
mock_update_config_file.assert_called_once()
88+
assert {f.file_change.filename for f in mock_modify_files.call_args[0][0]} == {
89+
"foo.txt",
90+
"bar.txt",
10191
}
102-
)
103-
config.scm_info = SCMInfo()
104-
dry_run = True
105-
106-
# Act
107-
bump.do_bump(version_part, new_version, config, dry_run=dry_run)
108-
109-
# Assert
110-
mock_modify_files.assert_called_once()
111-
assert mock_modify_files.call_args[0][0] == []
112-
assert mock_modify_files.call_args[0][1] == current_version
113-
assert mock_modify_files.call_args[0][2] == version_config.parse(new_version)
92+
assert mock_update_config_file.call_args[0][0] is None
93+
assert mock_update_config_file.call_args[0][1] == config
94+
assert mock_update_config_file.call_args[0][2] == current_version
95+
assert mock_update_config_file.call_args[0][3] == current_version.bump(version_part)
96+
assert mock_update_config_file.call_args[0][5] is False
97+
98+
@patch("bumpversion.files.modify_files")
99+
@patch("bumpversion.bump.update_config_file")
100+
def test_passing_new_version_sets_version(self, mock_update_config_file, mock_modify_files):
101+
# Arrange
102+
version_part = None
103+
new_version = "2.0.0"
104+
config, version_config, current_version = get_config_data(
105+
{
106+
"current_version": "1.2.3",
107+
}
108+
)
109+
config.scm_info = SCMInfo()
110+
dry_run = True
114111

115-
mock_update_config_file.assert_called_once()
116-
assert mock_update_config_file.call_args[0][0] is None
117-
assert mock_update_config_file.call_args[0][1] == config
118-
assert mock_update_config_file.call_args[0][2] == current_version
119-
assert mock_update_config_file.call_args[0][3] == version_config.parse(new_version)
120-
assert mock_update_config_file.call_args[0][5] is dry_run
112+
# Act
113+
bump.do_bump(version_part, new_version, config, dry_run=dry_run)
121114

115+
# Assert
116+
mock_modify_files.assert_called_once()
117+
assert mock_modify_files.call_args[0][0] == []
118+
assert mock_modify_files.call_args[0][1] == current_version
119+
assert mock_modify_files.call_args[0][2] == version_config.parse(new_version)
120+
121+
mock_update_config_file.assert_called_once()
122+
assert mock_update_config_file.call_args[0][0] is None
123+
assert mock_update_config_file.call_args[0][1] == config
124+
assert mock_update_config_file.call_args[0][2] == current_version
125+
assert mock_update_config_file.call_args[0][3] == version_config.parse(new_version)
126+
assert mock_update_config_file.call_args[0][5] is dry_run
127+
128+
@patch("bumpversion.files.modify_files")
129+
@patch("bumpversion.bump.update_config_file")
130+
def test_when_new_equals_current_nothing_happens(self, mock_update_config_file, mock_modify_files, tmp_path: Path):
131+
"""When the new version is the same as the current version, nothing should happen."""
132+
133+
# Arrange
134+
version_part = None
135+
new_version = "1.2.3"
136+
137+
with inside_dir(tmp_path):
138+
config, version_config, current_version = get_config_data({"current_version": "1.2.3"})
139+
# Act
140+
bump.do_bump(version_part, new_version, config)
141+
142+
# Assert
143+
mock_modify_files.assert_not_called()
144+
mock_update_config_file.assert_not_called()
145+
146+
def test_passing_no_arguments_raises_error(self):
147+
# Arrange
148+
version_part = None
149+
new_version = None
150+
config, version_config, current_version = get_config_data({"current_version": "1.2.3"})
151+
config.scm_info = SCMInfo()
152+
dry_run = False
153+
154+
# Act/Assert
155+
with pytest.raises(ConfigurationError):
156+
bump.do_bump(version_part, new_version, config, dry_run=dry_run)
157+
158+
@patch("bumpversion.files.modify_files")
159+
@patch("bumpversion.bump.update_config_file")
160+
def test_includes_files_with_valid_bumps(self, mock_update_config_file, mock_modify_files):
161+
"""Files that have valid_bumps are included when those bumps happen."""
162+
# Arrange
163+
new_version = None
164+
config, version_config, current_version = get_config_data(
165+
{
166+
"current_version": "1.2.3",
167+
"files": [{"filename": "foo.txt", "valid_bumps": ["major", "minor"]}, {"filename": "bar.txt"}],
168+
}
169+
)
170+
config.scm_info = SCMInfo()
171+
dry_run = False
122172

123-
@patch("bumpversion.files.modify_files")
124-
@patch("bumpversion.bump.update_config_file")
125-
def test_do_bump_when_new_equals_current(mock_update_config_file, mock_modify_files, tmp_path: Path):
126-
"""When the new version is the same as the current version, nothing should happen."""
173+
# Act
174+
bump.do_bump("patch", new_version, config, dry_run=dry_run)
127175

128-
# Arrange
129-
version_part = None
130-
new_version = "1.2.3"
176+
# Assert
177+
assert {f.file_change.filename for f in mock_modify_files.call_args[0][0]} == {"bar.txt"}
131178

132-
with inside_dir(tmp_path):
133-
config, version_config, current_version = get_config_data({"current_version": "1.2.3"})
134179
# Act
135-
bump.do_bump(version_part, new_version, config)
180+
bump.do_bump("minor", new_version, config, dry_run=dry_run)
181+
182+
# Assert
183+
assert {f.file_change.filename for f in mock_modify_files.call_args[0][0]} == {"foo.txt", "bar.txt"}
184+
185+
@patch("bumpversion.files.modify_files")
186+
@patch("bumpversion.bump.update_config_file")
187+
def test_excludes_files_with_invalid_bumps(self, mock_update_config_file, mock_modify_files):
188+
"""Files that have invalid_bumps are excluded when those bumps happen."""
189+
# Arrange
190+
new_version = None
191+
config, version_config, current_version = get_config_data(
192+
{
193+
"current_version": "1.2.3",
194+
"files": [{"filename": "foo.txt", "invalid_bumps": ["patch"]}, {"filename": "bar.txt"}],
195+
}
196+
)
197+
config.scm_info = SCMInfo()
198+
dry_run = False
136199

137-
# Assert
138-
mock_modify_files.assert_not_called()
139-
mock_update_config_file.assert_not_called()
200+
# Act
201+
bump.do_bump("patch", new_version, config, dry_run=dry_run)
140202

203+
# Assert
204+
assert {f.file_change.filename for f in mock_modify_files.call_args[0][0]} == {"bar.txt"}
141205

142-
def test_do_bump_with_no_arguments():
143-
# Arrange
144-
version_part = None
145-
new_version = None
146-
config, version_config, current_version = get_config_data({"current_version": "1.2.3"})
147-
config.scm_info = SCMInfo()
148-
dry_run = False
206+
# Act
207+
bump.do_bump("minor", new_version, config, dry_run=dry_run)
149208

150-
# Act/Assert
151-
with pytest.raises(ConfigurationError):
152-
bump.do_bump(version_part, new_version, config, dry_run=dry_run)
209+
# Assert
210+
assert {f.file_change.filename for f in mock_modify_files.call_args[0][0]} == {"foo.txt", "bar.txt"}
153211

154212

155213
def test_commit_and_tag_with_no_tool():

0 commit comments

Comments
 (0)