Skip to content

Commit 35e6fc2

Browse files
PhilipNelson5woile
authored andcommitted
feat(hooks): support interactive hooks scripts
1 parent c0e087a commit 35e6fc2

3 files changed

Lines changed: 46 additions & 11 deletions

File tree

commitizen/cmd.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,43 @@ def run_shell(cmd: str, env: Mapping[str, str] | None = None) -> Command:
103103
https://github.com/commitizen-tools/commitizen/issues/1918
104104
"""
105105
return _popen(cmd, shell=True, env=env)
106+
107+
108+
def run_interactive(
109+
cmd: str | Sequence[str], env: Mapping[str, str] | None = None
110+
) -> int:
111+
"""Run a command safely without shell interpretation and without redirecting stdin, stdout, or stderr
112+
113+
Args:
114+
cmd: The command to run
115+
env: Extra environment variables to define in the subprocess. Defaults to None.
116+
117+
Returns:
118+
subprocess returncode
119+
"""
120+
if env is not None:
121+
env = {**os.environ, **env}
122+
if isinstance(cmd, str):
123+
warnings.warn(
124+
"Passing a string to cmd.run_interactive() is deprecated and will be removed in v5. "
125+
"Use a list of arguments instead, or use cmd.run_interactive_shell() explicitly.",
126+
DeprecationWarning,
127+
stacklevel=2,
128+
)
129+
return subprocess.run(cmd, shell=True, env=env).returncode
130+
return subprocess.run(cmd, shell=False, env=env).returncode
131+
132+
133+
def run_interactive_shell(cmd: str, env: Mapping[str, str] | None = None) -> int:
134+
"""Run a command without redirecting stdin, stdout, or stderr
135+
136+
Args:
137+
cmd: The command to run
138+
env: Extra environment variables to define in the subprocess. Defaults to None.
139+
140+
Returns:
141+
subprocess returncode
142+
"""
143+
if env is not None:
144+
env = {**os.environ, **env}
145+
return subprocess.run(cmd, shell=True, env=env).returncode

commitizen/hooks.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,9 @@ def run(hooks: str | list[str], _env_prefix: str = "CZ_", **env: object) -> None
1717
for hook in hooks:
1818
out.info(f"Running hook '{hook}'")
1919

20-
c = cmd.run_shell(hook, env=_format_env(_env_prefix, env))
20+
return_code = cmd.run_interactive(hook, env=_format_env(_env_prefix, env))
2121

22-
if c.out:
23-
out.write(c.out)
24-
if c.err:
25-
out.error(c.err)
26-
27-
if c.return_code != 0:
22+
if return_code != 0:
2823
raise RunHookError(f"Running hook '{hook}' failed")
2924

3025

tests/test_bump_hooks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ def test_run(mocker: MockFixture):
1212
bump_hooks = ["pre_bump_hook", "pre_bump_hook_1"]
1313

1414
cmd_run_mock = mocker.Mock()
15-
cmd_run_mock.return_value.return_code = 0
16-
mocker.patch.object(cmd, "run_shell", cmd_run_mock)
15+
cmd_run_mock.return_value = 0
16+
mocker.patch.object(cmd, "run_interactive", cmd_run_mock)
1717

1818
hooks.run(bump_hooks)
1919

@@ -29,8 +29,8 @@ def test_run_error(mocker: MockFixture):
2929
bump_hooks = ["pre_bump_hook", "pre_bump_hook_1"]
3030

3131
cmd_run_mock = mocker.Mock()
32-
cmd_run_mock.return_value.return_code = 1
33-
mocker.patch.object(cmd, "run_shell", cmd_run_mock)
32+
cmd_run_mock.return_value = 1
33+
mocker.patch.object(cmd, "run_interactive", cmd_run_mock)
3434

3535
with pytest.raises(RunHookError):
3636
hooks.run(bump_hooks)

0 commit comments

Comments
 (0)