Skip to content

Commit

Permalink
Merge pull request #197 from callowayproject/189-conditional-search-a…
Browse files Browse the repository at this point in the history
…nd-replace-blocks

189 conditional search and replace blocks
  • Loading branch information
coordt authored Jun 14, 2024
2 parents 458970e + 2224808 commit 6374aa7
Show file tree
Hide file tree
Showing 14 changed files with 354 additions and 193 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,7 @@ reports
*.env
todo.txt
temp.*

.python-version
requirements-dev.lock
requirements.lock
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Expand Down
6 changes: 6 additions & 0 deletions bumpversion/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ def do_bump(
ctx = get_context(config, version, next_version)

configured_files = resolve_file_config(config.files_to_modify, config.version_config)

if version_part:
# filter the files that are not valid for this bump
configured_files = [file for file in configured_files if version_part in file.file_change.include_bumps]
configured_files = [file for file in configured_files if version_part not in file.file_change.exclude_bumps]

modify_files(configured_files, version, next_version, ctx, dry_run)
if config_file and config_file.suffix in {".cfg", ".ini"}:
update_ini_config_file(config_file, config.current_version, next_version_str, dry_run) # pragma: no-coverage
Expand Down
21 changes: 14 additions & 7 deletions bumpversion/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@
}


def set_config_defaults(parsed_config: dict[str, Any], **overrides: Any) -> dict[str, Any]:
"""Apply the defaults to the parsed config."""
config_dict = DEFAULTS.copy()

# We want to strip out unrecognized key-values to avoid inadvertent issues
config_dict.update({key: val for key, val in parsed_config.items() if key in DEFAULTS.keys()})

allowed_overrides = set(DEFAULTS.keys())
config_dict.update({key: val for key, val in overrides.items() if key in allowed_overrides})

return config_dict


def get_configuration(config_file: Union[str, Path, None] = None, **overrides: Any) -> Config:
"""
Return the configuration based on any configuration files and overrides.
Expand All @@ -54,14 +67,8 @@ def get_configuration(config_file: Union[str, Path, None] = None, **overrides: A
logger.info("Reading configuration")
logger.indent()

config_dict = DEFAULTS.copy()
parsed_config = read_config_file(config_file) if config_file else {}

# We want to strip out unrecognized key-values to avoid inadvertent issues
config_dict.update({key: val for key, val in parsed_config.items() if key in DEFAULTS.keys()})

allowed_overrides = set(DEFAULTS.keys())
config_dict.update({key: val for key, val in overrides.items() if key in allowed_overrides})
config_dict = set_config_defaults(parsed_config, **overrides)

# Set any missing version parts
config_dict["parts"] = get_all_part_configs(config_dict)
Expand Down
5 changes: 4 additions & 1 deletion bumpversion/config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ class FileChange(BaseModel):
ignore_missing_file: bool
filename: Optional[str] = None
glob: Optional[str] = None # Conflicts with filename. If both are specified, glob wins
glob_exclude: Optional[List[str]] = None
glob_exclude: Optional[tuple] = None
key_path: Optional[str] = None # If specified, and has an appropriate extension, will be treated as a data file
include_bumps: Optional[tuple] = None
exclude_bumps: tuple = Field(default_factory=tuple)

def __hash__(self):
"""Return a hash of the model."""
Expand Down Expand Up @@ -120,6 +122,7 @@ def add_files(self, filename: Union[str, List[str]]) -> None:
regex=self.regex,
ignore_missing_version=self.ignore_missing_version,
ignore_missing_file=self.ignore_missing_files,
include_bumps=tuple(self.parts.keys()),
)
)
self.files = list(files)
Expand Down
3 changes: 2 additions & 1 deletion bumpversion/config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def get_all_file_configs(config_dict: dict) -> List[FileChange]:
"ignore_missing_version": config_dict["ignore_missing_version"],
"ignore_missing_file": config_dict["ignore_missing_files"],
"regex": config_dict["regex"],
"include_bumps": tuple(config_dict["parts"]),
}
files = [{k: v for k, v in filecfg.items() if v is not None} for filecfg in config_dict["files"]]
for f in files:
Expand Down Expand Up @@ -59,7 +60,7 @@ def resolve_glob_files(file_cfg: FileChange) -> List[FileChange]:
A list of resolved file configurations according to the pattern.
"""
files: List[FileChange] = []
exclude = file_cfg.glob_exclude or []
exclude = file_cfg.glob_exclude or ()
glob_flags = glob.GLOBSTAR | glob.FORCEUNIX | glob.SPLIT
for filename_glob in glob.glob(file_cfg.glob, flags=glob_flags, exclude=exclude):
new_file_cfg = file_cfg.model_copy()
Expand Down
2 changes: 2 additions & 0 deletions bumpversion/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ def __init__(
filename=file_change.filename,
glob=file_change.glob,
key_path=file_change.key_path,
include_bumps=file_change.include_bumps,
exclude_bumps=file_change.exclude_bumps,
)
self.version_config = VersionConfig(
self.file_change.parse,
Expand Down
74 changes: 55 additions & 19 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ By using a configuration file, you no longer need to specify those options on th

## Global Configuration

The general configuration is grouped in a `[tool.bumpversion]` or `[bumpversion]` section, depending on if it is a TOML or INI file respectfully.
The general configuration is grouped in a `[tool.bumpversion]` or `[bumpversion]` section, depending on if it is a TOML or INI file, respectfully.

### allow_dirty

Expand All @@ -46,7 +46,7 @@ The general configuration is grouped in a `[tool.bumpversion]` or `[bumpversion
: `BUMPVERSION_ALLOW_DIRTY`


Bump-my-version's default behavior is to abort if the working directory has uncommitted changes. This is to protect you from releasing unversioned files and/or overwriting unsaved changes.
Bump-my-version's default behavior is to abort if the working directory has uncommitted changes. This protects you from releasing unversioned files and overwriting unsaved changes.

### commit

Expand All @@ -68,7 +68,7 @@ Bump-my-version's default behavior is to abort if the working directory has unco

Whether to create a commit using git or Mercurial.

If you have pre-commit hooks, you might also want to add an option to [`commit_args`](configuration.md#commit_args) to disable your pre-commit hooks. For Git use `--no-verify` and use `--config hooks.pre-commit=` for Mercurial.
If you have pre-commit hooks, add an option to [`commit_args`](configuration.md#commit_args) to turn off your pre-commit hooks. For Git, use `--no-verify` and use `--config hooks.pre-commit=` for Mercurial.

### commit_args

Expand All @@ -91,7 +91,7 @@ If you have pre-commit hooks, you might also want to add an option to [`commit_a

Extra arguments to pass to commit command. This is only used when the [`commit`](configuration.md#commit) option is set to `True`.

If you have pre-commit hooks, you might also want to add an option to disable your pre-commit hooks. For Git use `--no-verify` and use `--config hooks.pre-commit=` for Mercurial.
If you have pre-commit hooks, add an option to turn off your pre-commit hooks. For Git, use `--no-verify` and use `--config hooks.pre-commit=` for Mercurial.

### current_version

Expand Down Expand Up @@ -196,7 +196,7 @@ This string is templated using the [Python Format String Syntax](https://docs.py
environment var
: `BUMPVERSION_PARSE`

This is the default regular expression (using [Python regular expression syntax](https://docs.python.org/3/library/re.html#regular-expression-syntax)) for finding and parsing the version string into its components. Individual part or file configurations may override this.
This is the default regular expression ([Python regular expression syntax](https://docs.python.org/3/library/re.html#regular-expression-syntax)) for finding and parsing the version string into its components. Individual part or file configurations may override this.

The regular expression must be able to parse all strings produced by the configured [`serialize`](configuration.md#serialize) value. Named matching groups ("`(?P<name>...)`") indicate the version part the matched value belongs to.

Expand Down Expand Up @@ -259,9 +259,9 @@ This is the template to create the string that will replace the current version
environment var
: `BUMPVERSION_SEARCH`

This is the template string how to search for the string to be replaced in the file. Individual file configurations may override this. This can span multiple lines, and is templated using [Python Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax). The [formatting context reference](formatting-context.md) describes the available variables.
This is the template string for searching. It is rendered using the [formatting context](formatting-context.md) for searching in the file. Individual file configurations may override this. This can span multiple lines and is templated using [Python Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax). The [formatting context reference](formatting-context.md) describes the available variables.

This is useful if there is the remotest possibility that the current version number might be present multiple times in the file and you mean to only bump one of the occurrences.
This is useful if there is the remotest possibility that the current version number might be present multiple times in the file and you mean to bump only one of the occurrences.

### serialize

Expand Down Expand Up @@ -361,11 +361,11 @@ If you are using `git`, don't forget to `git-push` with the `--tags` flag when y
environment var
: `BUMPVERSION_TAG_MESSAGE`

The tag message template to use when creating a tag, when [`tag`](configuration.md#tag) is `True`
The tag message template to use when creating a tag when [`tag`](configuration.md#tag) is `True`

This string is templated using the [Python Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax). The [formatting context reference](formatting-context.md) describes the available variables.

Bump My Version creates an *annotated* tag in Git by default. To disable this and create a *lightweight* tag, you must explicitly set an empty `tag_message` value.
Bump My Version creates an *annotated* tag in Git by default. To turn this off and create a *lightweight* tag, you must explicitly set an empty `tag_message` value.

### tag_name

Expand All @@ -386,7 +386,7 @@ Bump My Version creates an *annotated* tag in Git by default. To disable this an
environment var
: `BUMPVERSION_TAG_NAME`

The name template used to render the tag, when [`tag`](configuration.md#tag) is `True`.
The template used to render the tag when [`tag`](configuration.md#tag) is `True`.

This string is templated using the [Python Format String Syntax](https://docs.python.org/3/library/string.html#format-string-syntax). The [formatting context reference](formatting-context.md) describes the available variables.

Expand Down Expand Up @@ -465,7 +465,7 @@ An explicit list of all values to iterate through when bumping this part. An emp
type
: string

When the version part matches this value it is considered optional when serializing the final version string.
When the version part matches this value, it is considered optional when serializing the final version string.

!!! note

Expand Down Expand Up @@ -514,7 +514,7 @@ independent of the other parts. It is useful when you have a build number in you

default
: `False` (`True` if `calver_format` is set)

type
: boolean

Expand Down Expand Up @@ -581,7 +581,7 @@ The name of the file to modify.

!!! note

‡ This is only used with TOML configuration, and is only required if [`glob`](#glob) is _not_ specified. INI-style configuration files specify the file name as part of the grouping.
‡ This is only used with TOML configuration and is only required if [`glob`](#glob) is _not_ specified. INI-style configuration files specify the file name as part of the grouping.


### glob
Expand All @@ -590,6 +590,7 @@ The name of the file to modify.
required
: **Yes‡**


default
: empty

Expand All @@ -613,7 +614,7 @@ The glob pattern specifying the files to modify.

type
: list of string

A list of glob patterns to exclude from the files found via the `glob` parameter. Does nothing if `filename` is specified.


Expand Down Expand Up @@ -670,7 +671,7 @@ This is an override to the default template string how to search for the string
: No

default
: the valued configured in the global `regex` field
: the value configured in the global `regex` field

type
: boolean
Expand Down Expand Up @@ -722,13 +723,47 @@ If `True`, don't fail if the version string to be replaced is not found in the f

if `True`, don't fail if the configured file is missing.

### include_bumps

::: field-list

required
: No

default
: all version components

type
: list of strings

The `include_bumps` file configuration allows you to control when bump-my-version includes this file for changes. Its alternative is the `exclude_bumps` configuration. When a `bump <version component>` command is issued, this file is changed only if the version component is in this list and not in [`exclude_bumps`](#exclude_bumps). The [parse](#parse) configuration defines version components.

The default value, or an empty list, includes all version components.

### exclude_bumps

::: field-list

required
: No
default
: `[]`
type
: list of strings

The `exclude_bumps` file configuration allows you to control when bump-my-version excludes this file for changes. Its alternative is the `include_bumps` configuration. When a `bump <version component>` command is issued, this file is only changed if the version component is *not in this list.* The [parse](#parse) configuration defines version components.

The default value does not exclude anything.

### Examples

=== "TOML"

TOML allows us to specify the files using an [array of tables.](https://toml.io/en/v1.0.0#array-of-tables) TOML configuration files add two configuration fields to each file configuration: `filename` and `glob`. These fields are mutually exclusive: if you specify a value for both, only the `glob` value is used.
TOML allows us to specify the files using an [array of tables.](https://toml.io/en/v1.0.0#array-of-tables) TOML configuration adds two fields to each file configuration: `filename` and `glob`. These fields are mutually exclusive: if you specify a value for both, only the `glob` value is used.

For example, to change `coolapp/__init__.py` with the defaults, and alter `CHANGELOG.md` in twice:
For example, to change `coolapp/__init__.py` with the defaults and alter `CHANGELOG.md` twice:

```toml
[[tool.bumpversion.files]]
Expand All @@ -753,9 +788,10 @@ if `True`, don't fail if the configured file is missing.
!!! note

The configuration file format requires each section header to be unique. If you want to process a certain file multiple times, you may append a description between parens to the `file` keyword: `[bumpversion:file (special one):…]`.




For example, to change `coolapp/__init__.py` with the defaults, and alter `CHANGELOG.md` in twice:
For example, to change `coolapp/__init__.py` with the defaults and alter `CHANGELOG.md` twice:

```ini
[bumpversion:file:coolapp/__init__.py]
Expand Down
12 changes: 9 additions & 3 deletions tests/fixtures/basic_cfg_expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,41 @@
'commit_args': None,
'current_version': '1.0.0',
'excluded_paths': [],
'files': [{'filename': 'setup.py',
'files': [{'exclude_bumps': (),
'filename': 'setup.py',
'glob': None,
'glob_exclude': None,
'ignore_missing_file': False,
'ignore_missing_version': False,
'include_bumps': ('major', 'minor', 'patch', 'release'),
'key_path': None,
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
'regex': False,
'replace': '{new_version}',
'search': '{current_version}',
'serialize': ('{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}')},
{'filename': 'bumpversion/__init__.py',
{'exclude_bumps': (),
'filename': 'bumpversion/__init__.py',
'glob': None,
'glob_exclude': None,
'ignore_missing_file': False,
'ignore_missing_version': False,
'include_bumps': ('major', 'minor', 'patch', 'release'),
'key_path': None,
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
'regex': False,
'replace': '{new_version}',
'search': '{current_version}',
'serialize': ('{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}')},
{'filename': 'CHANGELOG.md',
{'exclude_bumps': (),
'filename': 'CHANGELOG.md',
'glob': None,
'glob_exclude': None,
'ignore_missing_file': False,
'ignore_missing_version': False,
'include_bumps': ('major', 'minor', 'patch', 'release'),
'key_path': None,
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
'regex': False,
Expand Down
Loading

0 comments on commit 6374aa7

Please sign in to comment.