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
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-30 13:37 +0000
1"""Bump-type resolution for the release command.
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"""
10from pathlib import Path
12import typer
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)
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.
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.
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.
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.
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)
54 current_version_str = _resolve_bump_baseline(get_current_version(language))
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())
61 return True, get_interactive_bump_type(current_version_str)
64def _resolve_explicit_bump_type(bump_type: str, language: Language) -> str:
65 """Resolve version from an explicitly provided bump type.
67 Args:
68 bump_type: The bump type keyword (e.g., "MAJOR", "MINOR", "PATCH").
69 language: The programming language for version reading.
71 Returns:
72 The new version string.
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
86def _perform_version_bump(new_version: str, dry_run: bool, language: Language, config: Path | None = None) -> str:
87 """Perform version bump with validation.
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.
95 Returns:
96 The new version string.
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}")
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 )
118 if dry_run:
119 console.info("[DRY-RUN] Version would be bumped before release")
121 return new_version