Skip to content

Commit 9458851

Browse files
committed
Add valid_bumps and invalid_bumps to file configuration
Updated the configuration file model to support valid_bumps and invalid_bumps. This feature provides control over which version section updates can trigger file changes. Adjusted various test fixtures and cleaned up tests to match these changes. Also, some updates were made to the documentation accordingly.
1 parent 458970e commit 9458851

File tree

9 files changed

+168
-75
lines changed

9 files changed

+168
-75
lines changed

bumpversion/config/__init__.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@
3737
}
3838

3939

40+
def set_config_defaults(parsed_config: dict[str, Any], **overrides: Any) -> dict[str, Any]:
41+
"""Apply the defaults to the parsed config."""
42+
config_dict = DEFAULTS.copy()
43+
44+
# We want to strip out unrecognized key-values to avoid inadvertent issues
45+
config_dict.update({key: val for key, val in parsed_config.items() if key in DEFAULTS.keys()})
46+
47+
allowed_overrides = set(DEFAULTS.keys())
48+
config_dict.update({key: val for key, val in overrides.items() if key in allowed_overrides})
49+
50+
return config_dict
51+
52+
4053
def get_configuration(config_file: Union[str, Path, None] = None, **overrides: Any) -> Config:
4154
"""
4255
Return the configuration based on any configuration files and overrides.
@@ -54,14 +67,8 @@ def get_configuration(config_file: Union[str, Path, None] = None, **overrides: A
5467
logger.info("Reading configuration")
5568
logger.indent()
5669

57-
config_dict = DEFAULTS.copy()
5870
parsed_config = read_config_file(config_file) if config_file else {}
59-
60-
# We want to strip out unrecognized key-values to avoid inadvertent issues
61-
config_dict.update({key: val for key, val in parsed_config.items() if key in DEFAULTS.keys()})
62-
63-
allowed_overrides = set(DEFAULTS.keys())
64-
config_dict.update({key: val for key, val in overrides.items() if key in allowed_overrides})
71+
config_dict = set_config_defaults(parsed_config, **overrides)
6572

6673
# Set any missing version parts
6774
config_dict["parts"] = get_all_part_configs(config_dict)

bumpversion/config/models.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ class FileChange(BaseModel):
3333
ignore_missing_file: bool
3434
filename: Optional[str] = None
3535
glob: Optional[str] = None # Conflicts with filename. If both are specified, glob wins
36-
glob_exclude: Optional[List[str]] = None
36+
glob_exclude: Optional[tuple] = None
3737
key_path: Optional[str] = None # If specified, and has an appropriate extension, will be treated as a data file
38+
valid_bumps: Optional[tuple] = None
39+
invalid_bumps: Optional[tuple] = None
3840

3941
def __hash__(self):
4042
"""Return a hash of the model."""
@@ -120,6 +122,8 @@ def add_files(self, filename: Union[str, List[str]]) -> None:
120122
regex=self.regex,
121123
ignore_missing_version=self.ignore_missing_version,
122124
ignore_missing_file=self.ignore_missing_files,
125+
valid_bumps=tuple(self.parts.keys()),
126+
invalid_bumps=(),
123127
)
124128
)
125129
self.files = list(files)

bumpversion/config/utils.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def get_all_file_configs(config_dict: dict) -> List[FileChange]:
2222
"ignore_missing_version": config_dict["ignore_missing_version"],
2323
"ignore_missing_file": config_dict["ignore_missing_files"],
2424
"regex": config_dict["regex"],
25+
"valid_bumps": tuple(config_dict["parts"]),
26+
"invalid_bumps": (),
2527
}
2628
files = [{k: v for k, v in filecfg.items() if v is not None} for filecfg in config_dict["files"]]
2729
for f in files:
@@ -59,7 +61,7 @@ def resolve_glob_files(file_cfg: FileChange) -> List[FileChange]:
5961
A list of resolved file configurations according to the pattern.
6062
"""
6163
files: List[FileChange] = []
62-
exclude = file_cfg.glob_exclude or []
64+
exclude = file_cfg.glob_exclude or ()
6365
glob_flags = glob.GLOBSTAR | glob.FORCEUNIX | glob.SPLIT
6466
for filename_glob in glob.glob(file_cfg.glob, flags=glob_flags, exclude=exclude):
6567
new_file_cfg = file_cfg.model_copy()

docs/reference/configuration.md

+55-19
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ By using a configuration file, you no longer need to specify those options on th
2525

2626
## Global Configuration
2727

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

3030
### allow_dirty
3131

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

4848

49-
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.
49+
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.
5050

5151
### commit
5252

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

6969
Whether to create a commit using git or Mercurial.
7070

71-
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.
71+
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.
7272

7373
### commit_args
7474

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

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

94-
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.
94+
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.
9595

9696
### current_version
9797

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

199-
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.
199+
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.
200200

201201
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.
202202

@@ -259,9 +259,9 @@ This is the template to create the string that will replace the current version
259259
environment var
260260
: `BUMPVERSION_SEARCH`
261261

262-
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.
262+
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.
263263

264-
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.
264+
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.
265265

266266
### serialize
267267

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

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

366366
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.
367367

368-
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.
368+
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.
369369

370370
### tag_name
371371

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

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

391391
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.
392392

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

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

470470
!!! note
471471

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

515515
default
516516
: `False` (`True` if `calver_format` is set)
517-
517+
518518
type
519519
: boolean
520520

@@ -581,7 +581,7 @@ The name of the file to modify.
581581

582582
!!! note
583583

584-
‡ 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.
584+
‡ 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.
585585

586586

587587
### glob
@@ -590,6 +590,7 @@ The name of the file to modify.
590590
required
591591
: **Yes‡**
592592

593+
593594
default
594595
: empty
595596

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

614615
type
615616
: list of string
616-
617+
617618
A list of glob patterns to exclude from the files found via the `glob` parameter. Does nothing if `filename` is specified.
618619

619620

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

672673
default
673-
: the valued configured in the global `regex` field
674+
: the value configured in the global `regex` field
674675

675676
type
676677
: boolean
@@ -722,6 +723,40 @@ If `True`, don't fail if the version string to be replaced is not found in the f
722723

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

726+
### valid_bumps
727+
728+
::: field-list
729+
730+
required
731+
: No
732+
733+
default
734+
: all version parts
735+
736+
type
737+
: list of strings
738+
739+
The `valid_bumps` file configuration allows you to control when this file is changed. When a `bump <version component>` command is issued, this file is changed only if the version component is in this list and not in [`invalid_bumps`](#invalid_bumps). The [parse](#parse) configuration defines version components.
740+
741+
The default value, or an empty list, includes all version components.
742+
743+
### invalid_bumps
744+
745+
::: field-list
746+
747+
required
748+
: No
749+
750+
default
751+
: `[]`
752+
753+
type
754+
: list of strings
755+
756+
The `invalid_bumps` file configuration allows you to control when this file is changed by exclusion. 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.
757+
758+
The default value does not exclude anything.
759+
725760
### Examples
726761

727762
=== "TOML"
@@ -753,10 +788,11 @@ if `True`, don't fail if the configured file is missing.
753788
!!! note
754789

755790
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):…]`.
756-
757-
758-
For example, to change `coolapp/__init__.py` with the defaults, and alter `CHANGELOG.md` in twice:
759-
791+
792+
793+
794+
​ For example, to change `coolapp/__init__.py` with the defaults, and alter `CHANGELOG.md` in twice:
795+
760796
```ini
761797
[bumpversion:file:coolapp/__init__.py]
762798

tests/fixtures/basic_cfg_expected.txt

+9-3
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,43 @@
88
'glob_exclude': None,
99
'ignore_missing_file': False,
1010
'ignore_missing_version': False,
11+
'invalid_bumps': (),
1112
'key_path': None,
1213
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
1314
'regex': False,
1415
'replace': '{new_version}',
1516
'search': '{current_version}',
1617
'serialize': ('{major}.{minor}.{patch}-{release}',
17-
'{major}.{minor}.{patch}')},
18+
'{major}.{minor}.{patch}'),
19+
'valid_bumps': ('major', 'minor', 'patch', 'release')},
1820
{'filename': 'bumpversion/__init__.py',
1921
'glob': None,
2022
'glob_exclude': None,
2123
'ignore_missing_file': False,
2224
'ignore_missing_version': False,
25+
'invalid_bumps': (),
2326
'key_path': None,
2427
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
2528
'regex': False,
2629
'replace': '{new_version}',
2730
'search': '{current_version}',
2831
'serialize': ('{major}.{minor}.{patch}-{release}',
29-
'{major}.{minor}.{patch}')},
32+
'{major}.{minor}.{patch}'),
33+
'valid_bumps': ('major', 'minor', 'patch', 'release')},
3034
{'filename': 'CHANGELOG.md',
3135
'glob': None,
3236
'glob_exclude': None,
3337
'ignore_missing_file': False,
3438
'ignore_missing_version': False,
39+
'invalid_bumps': (),
3540
'key_path': None,
3641
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
3742
'regex': False,
3843
'replace': '**unreleased**\n**v{new_version}**',
3944
'search': '**unreleased**',
4045
'serialize': ('{major}.{minor}.{patch}-{release}',
41-
'{major}.{minor}.{patch}')}],
46+
'{major}.{minor}.{patch}'),
47+
'valid_bumps': ('major', 'minor', 'patch', 'release')}],
4248
'ignore_missing_files': False,
4349
'ignore_missing_version': False,
4450
'included_paths': [],

tests/fixtures/basic_cfg_expected.yaml

+21
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ files:
1010
glob_exclude: null
1111
ignore_missing_file: false
1212
ignore_missing_version: false
13+
invalid_bumps:
14+
1315
key_path: null
1416
parse: "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?"
1517
regex: false
@@ -18,11 +20,18 @@ files:
1820
serialize:
1921
- "{major}.{minor}.{patch}-{release}"
2022
- "{major}.{minor}.{patch}"
23+
valid_bumps:
24+
- "major"
25+
- "minor"
26+
- "patch"
27+
- "release"
2128
- filename: "bumpversion/__init__.py"
2229
glob: null
2330
glob_exclude: null
2431
ignore_missing_file: false
2532
ignore_missing_version: false
33+
invalid_bumps:
34+
2635
key_path: null
2736
parse: "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?"
2837
regex: false
@@ -31,11 +40,18 @@ files:
3140
serialize:
3241
- "{major}.{minor}.{patch}-{release}"
3342
- "{major}.{minor}.{patch}"
43+
valid_bumps:
44+
- "major"
45+
- "minor"
46+
- "patch"
47+
- "release"
3448
- filename: "CHANGELOG.md"
3549
glob: null
3650
glob_exclude: null
3751
ignore_missing_file: false
3852
ignore_missing_version: false
53+
invalid_bumps:
54+
3955
key_path: null
4056
parse: "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?"
4157
regex: false
@@ -44,6 +60,11 @@ files:
4460
serialize:
4561
- "{major}.{minor}.{patch}-{release}"
4662
- "{major}.{minor}.{patch}"
63+
valid_bumps:
64+
- "major"
65+
- "minor"
66+
- "patch"
67+
- "release"
4768
ignore_missing_files: false
4869
ignore_missing_version: false
4970
included_paths:

0 commit comments

Comments
 (0)