Skip to content

Commit 500ecd3

Browse files
committed
Refactor SCM info retrieval and config file update checks
Replaced ChainMap with MutableMapping in function signatures and types. Enhanced SCM info handling by splitting code into dedicated methods for commit and revision info retrieval. Added logic to prevent config file updates when the file is outside the repo and implemented corresponding test.
1 parent 25670d0 commit 500ecd3

File tree

4 files changed

+96
-14
lines changed

4 files changed

+96
-14
lines changed

bumpversion/bump.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import shlex
44
from pathlib import Path
5-
from typing import TYPE_CHECKING, ChainMap, List, Optional
5+
from typing import TYPE_CHECKING, List, MutableMapping, Optional
66

77
if TYPE_CHECKING: # pragma: no-coverage
88
from bumpversion.files import ConfiguredFile
@@ -117,7 +117,7 @@ def commit_and_tag(
117117
config: Config,
118118
config_file: Optional[Path],
119119
configured_files: List["ConfiguredFile"],
120-
ctx: ChainMap,
120+
ctx: MutableMapping,
121121
dry_run: bool = False,
122122
) -> None:
123123
"""

bumpversion/config/files.py

+5
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ def update_config_file(
129129
logger.info("\n%sProcessing config file: %s", logger.indent_str, config_file)
130130
logger.indent()
131131
config_path = Path(config_file)
132+
133+
if config.scm_info.tool and not config.scm_info.path_in_repo(config_path):
134+
logger.info("\n%sConfiguration file is outside of the repo. Not going to change.", logger.indent_str)
135+
return
136+
132137
if config_path.suffix != ".toml":
133138
logger.info("You must have a `.toml` suffix to update the config file: %s.", config_path)
134139
return

bumpversion/scm.py

+52-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from dataclasses import dataclass
77
from pathlib import Path
88
from tempfile import NamedTemporaryFile
9-
from typing import TYPE_CHECKING, ClassVar, List, MutableMapping, Optional, Type, Union
9+
from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, MutableMapping, Optional, Type, Union
1010

1111
from bumpversion.ui import get_indented_logger
1212
from bumpversion.utils import extract_regex_flags, run_command
@@ -51,7 +51,7 @@ def __repr__(self):
5151
def path_in_repo(self, path: Union[Path, str]) -> bool:
5252
"""Return whether a path is inside this repository."""
5353
if self.repository_root is None:
54-
return False
54+
return True
5555

5656
return Path(path).is_relative_to(self.repository_root)
5757

@@ -263,15 +263,36 @@ def latest_tag_info(cls, tag_name: str, parse_pattern: str) -> SCMInfo:
263263
if not cls.is_usable():
264264
return SCMInfo()
265265

266-
info = SCMInfo(tool=cls)
266+
info: Dict[str, Any] = {"tool": cls}
267267

268268
try:
269269
# git-describe doesn't update the git-index, so we do that
270270
run_command(["git", "update-index", "--refresh", "-q"])
271271
except subprocess.CalledProcessError as e:
272272
logger.debug("Error when running git update-index: %s", e.stderr)
273273

274+
commit_info = cls._commit_info(parse_pattern, tag_name)
275+
rev_info = cls._revision_info()
276+
info |= commit_info
277+
info |= rev_info
278+
279+
return SCMInfo(**info)
280+
281+
@classmethod
282+
def _commit_info(cls, parse_pattern: str, tag_name: str) -> dict:
283+
"""
284+
Get the commit info for the repo.
285+
286+
Args:
287+
parse_pattern: The regular expression pattern used to parse the version from the tag.
288+
tag_name: The tag name format used to locate the latest tag.
289+
290+
Returns:
291+
A dictionary containing information about the latest commit.
292+
"""
274293
tag_pattern = tag_name.replace("{new_version}", "*")
294+
info = dict.fromkeys(["dirty", "commit_sha", "distance_to_latest_tag", "current_version"])
295+
info["distance_to_latest_tag"] = 0
275296
try:
276297
# get info about the latest tag in git
277298
git_cmd = [
@@ -286,28 +307,45 @@ def latest_tag_info(cls, tag_name: str, parse_pattern: str) -> SCMInfo:
286307
result = run_command(git_cmd)
287308
describe_out = result.stdout.strip().split("-")
288309
if describe_out[-1].strip() == "dirty":
289-
info.dirty = True
310+
info["dirty"] = True
290311
describe_out.pop()
291312
else:
292-
info.dirty = False
313+
info["dirty"] = False
293314

294-
info.commit_sha = describe_out.pop().lstrip("g")
295-
info.distance_to_latest_tag = int(describe_out.pop())
315+
info["commit_sha"] = describe_out.pop().lstrip("g")
316+
info["distance_to_latest_tag"] = int(describe_out.pop())
296317
version = cls.get_version_from_tag("-".join(describe_out), tag_name, parse_pattern)
297-
info.current_version = version or "-".join(describe_out).lstrip("v")
318+
info["current_version"] = version or "-".join(describe_out).lstrip("v")
298319
except subprocess.CalledProcessError as e:
299320
logger.debug("Error when running git describe: %s", e.stderr)
300321

322+
return info
323+
324+
@classmethod
325+
def _revision_info(cls) -> dict:
326+
"""
327+
Returns a dictionary containing revision information.
328+
329+
If an error occurs while running the git command, the dictionary values will be set to None.
330+
331+
Returns:
332+
A dictionary with the following keys:
333+
- branch_name: The name of the current branch.
334+
- short_branch_name: A 20 lowercase characters of the branch name with special characters removed.
335+
- repository_root: The root directory of the Git repository.
336+
"""
337+
info = dict.fromkeys(["branch_name", "short_branch_name", "repository_root"])
338+
301339
try:
302340
git_cmd = ["git", "rev-parse", "--show-toplevel", "--abbrev-ref", "HEAD"]
303341
result = run_command(git_cmd)
304342
lines = [line.strip() for line in result.stdout.split("\n")]
305343
repository_root = Path(lines[0])
306344
branch_name = lines[1]
307345
short_branch_name = re.sub(r"([^a-zA-Z0-9]*)", "", branch_name).lower()[:20]
308-
info.branch_name = branch_name
309-
info.short_branch_name = short_branch_name
310-
info.repository_root = repository_root
346+
info["branch_name"] = branch_name
347+
info["short_branch_name"] = short_branch_name
348+
info["repository_root"] = repository_root
311349
except subprocess.CalledProcessError as e:
312350
logger.debug("Error when running git rev-parse: %s", e.stderr)
313351

@@ -316,7 +354,9 @@ def latest_tag_info(cls, tag_name: str, parse_pattern: str) -> SCMInfo:
316354
@classmethod
317355
def add_path(cls, path: Union[str, Path]) -> None:
318356
"""Add a path to the VCS."""
319-
run_command(["git", "add", "--update", str(path)])
357+
info = SCMInfo(**cls._revision_info())
358+
if info.path_in_repo(path):
359+
run_command(["git", "add", "--update", str(path)])
320360

321361
@classmethod
322362
def tag(cls, name: str, sign: bool = False, message: Optional[str] = None) -> None:

tests/test_cli/test_bump.py

+37
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,40 @@ def test_current_version_not_required_in_config(tmp_path, fixtures_path, runner)
346346

347347
assert result.exit_code == 0
348348
assert config_file.read_text() == config_contents
349+
350+
351+
def test_remote_config_doesnt_update(tmp_path, git_repo, runner):
352+
"""The current version is not required in the configuration."""
353+
354+
config_file = tmp_path / ".bumpversion.toml"
355+
config_contents = '[tool.bumpversion]\nallow_dirty = true\n\n[[tool.bumpversion.files]]\nfilename = "readme.md"\n'
356+
config_file.write_text(
357+
config_contents,
358+
encoding="utf-8",
359+
)
360+
myfile = git_repo / "readme.md"
361+
myfile.write_text("version = 1.0.0\n", encoding="utf-8")
362+
363+
# Act
364+
with inside_dir(git_repo):
365+
result: Result = runner.invoke(
366+
cli.cli,
367+
[
368+
"bump",
369+
"-vv",
370+
"--current-version",
371+
"1.0.0",
372+
f"--config-file={config_file}",
373+
"minor",
374+
],
375+
)
376+
377+
# Assert
378+
if result.exit_code != 0:
379+
print("Here is the output:")
380+
print(result.output)
381+
print(traceback.print_exception(result.exc_info[1]))
382+
383+
assert result.exit_code == 0
384+
assert config_file.read_text() == config_contents
385+
assert myfile.read_text() == "version = 1.1.0\n"

0 commit comments

Comments
 (0)