Skip to content

Commit 39fc233

Browse files
committed
Add HookError for failed hook execution with tests
Raise HookError when a hook script exits with a non-zero status. Modified logger to display warnings instead of debug messages in such scenarios. Added tests to ensure exceptions are raised for failed hooks.
1 parent c84bfa7 commit 39fc233

File tree

3 files changed

+42
-6
lines changed

3 files changed

+42
-6
lines changed

bumpversion/exceptions.py

+6
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,9 @@ class BadInputError(BumpVersionError):
5959
"""User input was bad."""
6060

6161
pass
62+
63+
64+
class HookError(BumpVersionError):
65+
"""A hook failed."""
66+
67+
pass

bumpversion/hooks.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from bumpversion.config.models import Config
99
from bumpversion.context import get_context
10+
from bumpversion.exceptions import HookError
1011
from bumpversion.ui import get_indented_logger
1112
from bumpversion.versioning.models import Version
1213

@@ -101,10 +102,15 @@ def run_hooks(hooks: List[str], env: Dict[str, str], dry_run: bool = False) -> N
101102
logger.debug(f"Running {script!r}")
102103
logger.indent()
103104
result = run_command(script, env)
104-
logger.debug(result.stdout)
105-
logger.debug(result.stderr)
106-
logger.debug(f"Exited with {result.returncode}")
107-
logger.indent()
105+
if result.returncode != 0:
106+
logger.warning(result.stdout)
107+
logger.warning(result.stderr)
108+
raise HookError(f"{script!r} exited with {result.returncode}. ")
109+
else:
110+
logger.debug(f"Exited with {result.returncode}")
111+
logger.debug(result.stdout)
112+
logger.debug(result.stderr)
113+
logger.dedent()
108114
logger.dedent()
109115

110116

tests/test_hooks/test_run_hooks.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"""Tests for the run_hooks function."""
22

33
from bumpversion import hooks
4+
import pytest
5+
6+
from bumpversion.exceptions import HookError
47

58

69
def test_calls_each_hook(mocker):
@@ -18,19 +21,40 @@ def test_calls_each_hook(mocker):
1821
# Assert
1922
expected_calls = [
2023
mocker.call("Running 'script1'"),
24+
mocker.call("Exited with 0"),
2125
mocker.call("output"),
2226
mocker.call("error"),
23-
mocker.call("Exited with 0"),
2427
mocker.call("Running 'script2'"),
28+
mocker.call("Exited with 0"),
2529
mocker.call("output"),
2630
mocker.call("error"),
27-
mocker.call("Exited with 0"),
2831
]
2932
mock_logger.debug.assert_has_calls(expected_calls)
3033
mock_run_command.assert_any_call("script1", env)
3134
mock_run_command.assert_any_call("script2", env)
3235

3336

37+
def test_raises_exception_if_hook_fails(mocker):
38+
"""If a hook responds with an error, an exception should be raised."""
39+
# Assemble
40+
mock_logger = mocker.patch("bumpversion.hooks.logger")
41+
mock_run_command = mocker.patch("bumpversion.hooks.run_command")
42+
hooks_list = ["script1", "script2"]
43+
env = {"var": "value"}
44+
mock_run_command.return_value = mocker.MagicMock(stdout="output", stderr="error", returncode=1)
45+
46+
# Act
47+
with pytest.raises(HookError):
48+
hooks.run_hooks(hooks_list, env)
49+
50+
# Assert
51+
expected_debug_calls = [mocker.call("Running 'script1'")]
52+
expected_info_calls = [mocker.call("output"), mocker.call("error")]
53+
mock_logger.debug.assert_has_calls(expected_debug_calls)
54+
mock_logger.info.assert_has_calls(expected_info_calls)
55+
mock_run_command.assert_any_call("script1", env)
56+
57+
3458
def test_does_not_call_each_hook_when_dry_run(mocker):
3559
"""It should not call each hook passed to it when dry_run is True."""
3660
# Assemble

0 commit comments

Comments
 (0)