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

1"""Configuration management."""

2 from __future__ import annotations

3

4 from typing import TYPE_CHECKING, Union

5

6 from bumpversion.config.files import read_config_file

7 from bumpversion.config.models import Config

8 from bumpversion.exceptions import ConfigurationError

9 from bumpversion.ui import get_indented_logger

10

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

12 from pathlib import Path

13

14 logger = get_indented_logger(__name__)

15

16 DEFAULTS = {

17 "current_version": None,

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

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

20 "search": "{current_version}",

21 "replace": "{new_version}",

22 "regex": False,

23 "ignore_missing_version": False,

24 "tag": False,

25 "sign_tags": False,

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

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

28 "allow_dirty": False,

29 "commit": False,

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

31 "commit_args": None,

32 "scm_info": None,

33 "parts": {},

34 "files": [],

35}

36

37

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

39 """

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

41

42 Args:

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

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

45

46 Returns:

47 The configuration

48 """

49 from bumpversion.config.utils import get_all_file_configs, get_all_part_configs

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

51

52 logger.info("Reading configuration")

53 logger.indent()

54

55 config_dict = DEFAULTS.copy()

56 parsed_config = read_config_file(config_file) if config_file else {}

57

58 # We want to strip out unrecognized key-values to avoid inadvertent issues

59 config_dict.update({key: val for key, val in parsed_config.items() if key in DEFAULTS.keys()})

60

61 allowed_overrides = set(DEFAULTS.keys())

62 config_dict.update({key: val for key, val in overrides.items() if key in allowed_overrides})

63

64 # Set any missing version parts

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

66

67 # Set any missing file configuration

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

69

70 # Resolve the SCMInfo class for Pydantic's BaseSettings

71 Config.model_rebuild()

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

73

74 # Get the information about the SCM

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

76 config.scm_info = scm_info

77

78 # Update and verify the current_version

79 config.current_version = check_current_version(config)

80

81 logger.dedent()

82

83 return config

84

85

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

87 """

88 Returns the current version.

89

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

91 it attempts to retrieve it via a tag.

92

93 Args:

94 config: The current configuration dictionary.

95

96 Returns:

97 The version number

98

99 Raises:

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

101 """

102 current_version = config.current_version

103 scm_info = config.scm_info

104

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

106 return scm_info.current_version

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

108 logger.warning(

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

110 current_version,

111 scm_info.current_version,

112 )

113 return current_version

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

115 return current_version

116

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