Coverage for /Users/OORDCOR/Documents/code/bump-my-version/bumpversion/config/__init__.py: 83%

1"""Configuration management."""

2

3 from __future__ import annotations

4

5 from typing import TYPE_CHECKING, Union

6

7 from bumpversion.config.files import read_config_file

8 from bumpversion.config.models import Config

9 from bumpversion.exceptions import ConfigurationError

10 from bumpversion.ui import get_indented_logger

11

12 if TYPE_CHECKING: # pragma: no-coverage 12 ↛ 13line 12 didn't jump to line 13, because the condition on line 12 was never true

13 from pathlib import Path

14

15 logger = get_indented_logger(__name__)

16

17 DEFAULTS = {

18 "current_version": None,

19 "parse": r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)",

20 "serialize": ["{major}.{minor}.{patch}"],

21 "search": "{current_version}",

22 "replace": "{new_version}",

23 "regex": False,

24 "ignore_missing_version": False,

25 "ignore_missing_files": False,

26 "tag": False,

27 "sign_tags": False,

28 "tag_name": "v{new_version}",

29 "tag_message": "Bump version: {current_version} → {new_version}",

30 "allow_dirty": False,

31 "commit": False,

32 "message": "Bump version: {current_version} → {new_version}",

33 "commit_args": None,

34 "scm_info": None,

35 "parts": {},

36 "files": [],

37}

38

39

40 def get_configuration(config_file: Union[str, Path, None] = None, **overrides) -> Config:

41 """

42 Return the configuration based on any configuration files and overrides.

43

44 Args:

45 config_file: An explicit configuration file to use, otherwise search for one

46 **overrides: Specific configuration key-values to override in the configuration

47

48 Returns:

49 The configuration

50 """

51 from bumpversion.config.utils import get_all_file_configs, get_all_part_configs

52 from bumpversion.scm import SCMInfo, SourceCodeManager, get_scm_info # noqa: F401

53

54 logger.info("Reading configuration")

55 logger.indent()

56

57 config_dict = DEFAULTS.copy()

58 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})

65

66 # Set any missing version parts

67 config_dict["parts"] = get_all_part_configs(config_dict)

68

69 # Set any missing file configuration

70 config_dict["files"] = get_all_file_configs(config_dict)

71

72 # Resolve the SCMInfo class for Pydantic's BaseSettings

73 Config.model_rebuild()

74 config = Config(**config_dict) # type: ignore[arg-type]

75

76 # Get the information about the SCM

77 scm_info = get_scm_info(config.tag_name, config.parse)

78 config.scm_info = scm_info

79

80 # Update and verify the current_version

81 config.current_version = check_current_version(config)

82

83 logger.dedent()

84

85 return config

86

87

88 def check_current_version(config: Config) -> str:

89 """

90 Returns the current version.

91

92 If the current version is not specified in the config file, command line or env variable,

93 it attempts to retrieve it via a tag.

94

95 Args:

96 config: The current configuration dictionary.

97

98 Returns:

99 The version number

100

101 Raises:

102 ConfigurationError: If it can't find the current version

103 """

104 current_version = config.current_version

105 scm_info = config.scm_info

106

107 if current_version is None and scm_info.current_version: 107 ↛ 108line 107 didn't jump to line 108, because the condition on line 107 was never true

108 return scm_info.current_version

109 elif current_version and scm_info.current_version and current_version != scm_info.current_version: 109 ↛ 110line 109 didn't jump to line 110, because the condition on line 109 was never true

110 logger.warning(

111 "Specified version (%s) does not match last tagged version (%s)",

112 current_version,

113 scm_info.current_version,

114 )

115 return current_version

116 elif current_version: 116 ↛ 119line 116 didn't jump to line 119, because the condition on line 116 was never false

117 return current_version

118

119 raise ConfigurationError("Unable to determine the current version.")