Coverage for src/rhiza_tools/commands/release/versioning.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-30 13:37 +0000

1"""Bump-type resolution for the release command. 

2 

3A release always bumps the version before tagging. Working out *which* version 

4to bump to — from an explicit ``--bump`` type, an interactive prompt, or a patch 

5default in non-interactive mode — is a self-contained concern with no git 

6plumbing, so it lives here rather than in ``release.py``. ``release.py`` 

7re-exports these helpers, so the public import surface is unchanged. 

8""" 

9 

10from pathlib import Path 

11 

12import typer 

13 

14from rhiza_tools import console 

15from rhiza_tools.commands._shared import parse_semver_or_exit 

16from rhiza_tools.commands.bump import ( 

17 BumpOptions, 

18 Language, 

19 _resolve_bump_baseline, 

20 bump_command, 

21 get_bumped_version_from_type, 

22 get_current_version, 

23 get_interactive_bump_type, 

24) 

25 

26 

27def _resolve_required_bump(non_interactive: bool, bump_type: str | None, *, language: Language) -> tuple[bool, str]: 

28 """Resolve the version bump to apply before releasing. 

29 

30 A release always bumps, so this never returns "no bump". The bump target is 

31 resolved, in order of precedence, from: an explicit ``bump_type``; a patch 

32 default when running non-interactively without one; or an interactive prompt 

33 for the bump type. Cancelling the interactive prompt raises ``typer.Exit`` 

34 (via :func:`get_interactive_bump_type`), aborting the release. 

35 

36 The leading boolean in the return tuple is intentionally always ``True`` to 

37 preserve a stable ``(required, new_version)`` shape for callers. In the 

38 release flow, ``required`` is unconditional because a release always requires 

39 a version bump. 

40 

41 Args: 

42 non_interactive: If True, skip prompts and default to a patch bump. 

43 bump_type: Explicit bump type (e.g., "MAJOR", "MINOR", "PATCH"), if given. 

44 language: The programming language for version reading. 

45 

46 Returns: 

47 Tuple of ``(True, new_version_string)`` where the boolean is a fixed 

48 compatibility flag and ``new_version_string`` is the explicit version to 

49 bump to (not a bump-type keyword). 

50 """ 

51 if bump_type: 

52 return True, _resolve_explicit_bump_type(bump_type, language) 

53 

54 current_version_str = _resolve_bump_baseline(get_current_version(language)) 

55 

56 if non_interactive: 

57 console.warning("No --bump type given in non-interactive mode, defaulting to patch") 

58 current_semver = parse_semver_or_exit(current_version_str) 

59 return True, str(current_semver.bump_patch()) 

60 

61 return True, get_interactive_bump_type(current_version_str) 

62 

63 

64def _resolve_explicit_bump_type(bump_type: str, language: Language) -> str: 

65 """Resolve version from an explicitly provided bump type. 

66 

67 Args: 

68 bump_type: The bump type keyword (e.g., "MAJOR", "MINOR", "PATCH"). 

69 language: The programming language for version reading. 

70 

71 Returns: 

72 The new version string. 

73 

74 Raises: 

75 typer.Exit: If the current version is invalid or the bump type is unsupported. 

76 """ 

77 current_version_str = _resolve_bump_baseline(get_current_version(language)) 

78 current_semver = parse_semver_or_exit(current_version_str) 

79 new_version = get_bumped_version_from_type(current_semver, bump_type.lower()) 

80 if not new_version: 

81 console.error(f"Invalid bump type: {bump_type}. Valid bump types are: major, minor, patch.") 

82 raise typer.Exit(code=1) 

83 return new_version 

84 

85 

86def _perform_version_bump(new_version: str, dry_run: bool, language: Language, config: Path | None = None) -> str: 

87 """Perform version bump with validation. 

88 

89 Args: 

90 new_version: The explicit new version string to bump to. 

91 dry_run: If True, only simulate the bump. 

92 language: The programming language for the bump. 

93 config: Optional path to the .cfg.toml bumpversion config file. 

94 

95 Returns: 

96 The new version string. 

97 

98 Raises: 

99 typer.Exit: If the bump operation fails. 

100 """ 

101 if dry_run: 

102 console.info(f"[DRY-RUN] Would bump version to: {new_version}") 

103 else: 

104 console.info(f"Bumping version to: {new_version}") 

105 

106 bump_command( 

107 BumpOptions( 

108 version=new_version, 

109 dry_run=dry_run, 

110 commit=True, 

111 push=False, # Do not push yet; we will do it after tagging 

112 allow_dirty=False, 

113 language=language, 

114 config=config, 

115 ) 

116 ) 

117 

118 if dry_run: 

119 console.info("[DRY-RUN] Version would be bumped before release") 

120 

121 return new_version