Skip to content

Commit

Permalink
Fix search and replace options for replace.
Browse files Browse the repository at this point in the history
- The `--search` and `--replace` options now completely override any other search and replace logic.

Fixes #34
  • Loading branch information
coordt committed Jul 11, 2023
1 parent 8ab8e5e commit 781e8d8
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 13 deletions.
4 changes: 1 addition & 3 deletions bumpversion/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,6 @@ def replace(
new_version=new_version,
parse=parse,
serialize=serialize or None,
search=search,
replace=replace,
commit=False,
tag=False,
sign_tags=False,
Expand Down Expand Up @@ -441,6 +439,6 @@ def replace(

ctx = get_context(config, version, next_version)

configured_files = resolve_file_config(config.files, config.version_config)
configured_files = resolve_file_config(config.files, config.version_config, search, replace)

modify_files(configured_files, version, next_version, ctx, dry_run)
36 changes: 26 additions & 10 deletions bumpversion/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import glob
import logging
from difflib import context_diff
from typing import List, MutableMapping
from typing import List, MutableMapping, Optional

from bumpversion.config import FileConfig
from bumpversion.exceptions import VersionNotFoundError
Expand All @@ -14,12 +14,18 @@
class ConfiguredFile:
"""A file to modify in a configured way."""

def __init__(self, file_cfg: FileConfig, version_config: VersionConfig) -> None:
def __init__(
self,
file_cfg: FileConfig,
version_config: VersionConfig,
search: Optional[str] = None,
replace: Optional[str] = None,
) -> None:
self.path = file_cfg.filename
self.parse = file_cfg.parse or version_config.parse_regex.pattern
self.serialize = file_cfg.serialize or version_config.serialize_formats
self.search = file_cfg.search or version_config.search
self.replace = file_cfg.replace or version_config.replace
self.search = search or file_cfg.search or version_config.search
self.replace = replace or file_cfg.replace or version_config.replace
self.version_config = VersionConfig(
self.parse, self.serialize, self.search, self.replace, version_config.part_configs
)
Expand Down Expand Up @@ -97,8 +103,10 @@ def replace_version(
file_content_before = f.read()
file_new_lines = f.newlines[0] if isinstance(f.newlines, tuple) else f.newlines

context["current_version"] = self.version_config.serialize(current_version, context)
context["new_version"] = self.version_config.serialize(new_version, context)
if current_version:
context["current_version"] = self.version_config.serialize(current_version, context)
if new_version:
context["new_version"] = self.version_config.serialize(new_version, context)

search_for = self.version_config.search.format(**context)
replace_with = self.version_config.replace.format(**context)
Expand Down Expand Up @@ -138,13 +146,17 @@ def __repr__(self) -> str:
return f"<bumpversion.ConfiguredFile:{self.path}>"


def resolve_file_config(files: List[FileConfig], version_config: VersionConfig) -> List[ConfiguredFile]:
def resolve_file_config(
files: List[FileConfig], version_config: VersionConfig, search: Optional[str] = None, replace: Optional[str] = None
) -> List[ConfiguredFile]:
"""
Resolve the files, searching and replacing values according to the FileConfig.
Args:
files: A list of file configurations
version_config: How the version should be changed
search: The search pattern to use instead of any configured search pattern
replace: The replace pattern to use instead of any configured replace pattern
Returns:
A list of ConfiguredFiles
Expand All @@ -154,7 +166,7 @@ def resolve_file_config(files: List[FileConfig], version_config: VersionConfig)
if file_cfg.glob:
configured_files.extend(get_glob_files(file_cfg, version_config))
else:
configured_files.append(ConfiguredFile(file_cfg, version_config))
configured_files.append(ConfiguredFile(file_cfg, version_config, search, replace))

return configured_files

Expand All @@ -181,13 +193,17 @@ def modify_files(
f.replace_version(current_version, new_version, context, dry_run)


def get_glob_files(file_cfg: FileConfig, version_config: VersionConfig) -> List[ConfiguredFile]:
def get_glob_files(
file_cfg: FileConfig, version_config: VersionConfig, search: Optional[str] = None, replace: Optional[str] = None
) -> List[ConfiguredFile]:
"""
Return a list of files that match the glob pattern.
Args:
file_cfg: The file configuration containing the glob pattern
version_config: The version configuration
search: The search pattern to use instead of any configured search pattern
replace: The replace pattern to use instead of any configured replace pattern
Returns:
A list of resolved files according to the pattern.
Expand All @@ -196,7 +212,7 @@ def get_glob_files(file_cfg: FileConfig, version_config: VersionConfig) -> List[
for filename_glob in glob.glob(file_cfg.glob, recursive=True):
new_file_cfg = file_cfg.copy()
new_file_cfg.filename = filename_glob
files.append(ConfiguredFile(new_file_cfg, version_config))
files.append(ConfiguredFile(new_file_cfg, version_config, search, replace))
return files


Expand Down
60 changes: 60 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for `bumpversion` package."""
import shutil
import subprocess
import traceback
from pathlib import Path

import pytest
Expand Down Expand Up @@ -429,3 +430,62 @@ def test_replace_specific_files(mocker, git_repo, fixtures_path):
configured_files = call_args[0]
assert len(configured_files) == 1
assert configured_files[0].path == "VERSION"


TEST_REPLACE_CONFIG = {
"tool": {
"bumpversion": {
"allow_dirty": True,
"commit": False,
"current_version": "2.17.7",
"files": [],
"message": "Bump version: {current_version} → {new_version}",
"parse": "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)",
"parts": {
"major": {},
"minor": {},
"patch": {},
},
"replace": "{new_version}",
"search": "{current_version}",
"serialize": ["{major}.{minor}.{patch}"],
"sign_tags": False,
"tag": False,
"tag_message": "Bump version: {current_version} → {new_version}",
"tag_name": "v{new_version}",
}
}
}


def test_replace_search_with_plain_string(tmp_path, fixtures_path):
"""Replace should not worry if the search or replace values have version info."""
from tomlkit import dumps

# Arrange
config_path = tmp_path / "pyproject.toml"
config_path.write_text(dumps(TEST_REPLACE_CONFIG))
doc_path = tmp_path / "docs.yaml"
doc_path.write_text("url: https://github.com/sampleuser/workflows/main/.github/update_mailmap.py")

runner: CliRunner = CliRunner()
with inside_dir(tmp_path):
result: Result = runner.invoke(
cli.cli,
[
"replace",
"--no-configured-files",
"--search",
"/workflows/main/",
"--replace",
"/workflows/v{current_version}/",
"./docs.yaml",
],
)

if result.exit_code != 0:
print("Here is the output:")
print(result.output)
print(traceback.print_exception(result.exc_info[1]))

assert result.exit_code == 0

0 comments on commit 781e8d8

Please sign in to comment.