Skip to content

Commit c9ea08e

Browse files
committed
fix(init): skip hook question when no installer is present
If neither pre-commit nor prek is on PATH, skip the hook-type question instead of failing init. Users who want hooks can install a tool and retry.
1 parent 7591d04 commit c9ea08e

3 files changed

Lines changed: 44 additions & 15 deletions

File tree

commitizen/commands/init.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,7 @@ def __call__(self) -> None:
109109
tag_format = self._ask_tag_format(tag) # confirm & text
110110
update_changelog_on_bump = self._ask_update_changelog_on_bump() # confirm
111111
major_version_zero = self._ask_major_version_zero(version) # confirm
112-
hook_types: list[str] | None = questionary.checkbox(
113-
"What types of pre-commit hook you want to install? (Leave blank if you don't want to install)",
114-
choices=[
115-
questionary.Choice("commit-msg", checked=False),
116-
questionary.Choice("pre-push", checked=False),
117-
],
118-
).unsafe_ask()
112+
hook_types = self._ask_hook_types()
119113
except KeyboardInterrupt:
120114
raise InitFailedError("Stopped by user")
121115

@@ -160,6 +154,25 @@ def __call__(self) -> None:
160154
out.info("\tcz bump\n")
161155
out.success("Configuration complete 🚀")
162156

157+
def _ask_hook_types(self) -> list[str] | None:
158+
"""Ask which pre-commit hook types to install.
159+
160+
Skip the question when neither ``pre-commit`` nor ``prek`` is
161+
installed, so users who do not use those tools are not prompted.
162+
"""
163+
if not project_info.available_hook_installers():
164+
out.info("No pre-commit hook detected, skipping question")
165+
return None
166+
167+
hook_types: list[str] | None = questionary.checkbox(
168+
"What types of pre-commit hook you want to install? (Leave blank if you don't want to install)",
169+
choices=[
170+
questionary.Choice("commit-msg", checked=False),
171+
questionary.Choice("pre-push", checked=False),
172+
],
173+
).unsafe_ask()
174+
return hook_types
175+
163176
def _ask_hook_installer(self) -> str:
164177
"""Choose ``pre-commit`` or ``prek`` when installing Git hooks.
165178

docs/commands/init.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ During the initialization process, you'll be prompted to configure the following
4343
- `pep440`: Python Package Versioning
4444
6. **Changelog Generation**: Configure whether to automatically generate changelog during version bumps
4545
7. **Alpha Versioning**: Option to keep major version at 0 for alpha/beta software
46-
8. **Pre-commit Hooks**: Set up Git hooks for automated commit message validation. If you choose to install hooks, Commitizen uses `pre-commit` or `prek` (whichever is on PATH). If both are installed, you are asked which one to use.
46+
8. **Pre-commit Hooks**: Set up Git hooks for automated commit message validation. If neither `pre-commit` nor `prek` is on PATH, the hook question is skipped. If you choose to install hooks, Commitizen uses whichever of those tools is available. If both are installed, you are asked which one to use.
4747

4848
See [Configuration Options][configuration_options] for more details.
4949

tests/commands/test_init_command.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from commitizen import cmd, commands
1111
from commitizen.__version__ import __version__
12-
from commitizen.exceptions import InitFailedError, NoAnswersError
12+
from commitizen.exceptions import NoAnswersError
1313

1414
if TYPE_CHECKING:
1515
from pytest_mock import MockFixture
@@ -228,18 +228,34 @@ def test_cz_hook_exists_in_pre_commit_config(
228228

229229

230230
class TestNoPreCommitInstalled:
231-
@pytest.mark.usefixtures("default_choice")
232-
def test_pre_commit_not_installed(
233-
self, mocker: MockFixture, config: BaseConfig, tmp_path, monkeypatch
231+
def test_skips_hook_question_when_neither_installer_is_installed(
232+
self, mocker: MockFixture, config: BaseConfig, tmp_path, monkeypatch, capsys
234233
):
235-
# Assume neither `pre-commit` nor `prek` is installed
234+
mocker.patch(
235+
"questionary.select",
236+
side_effect=[
237+
FakeQuestion("pyproject.toml"),
238+
FakeQuestion("cz_conventional_commits"),
239+
FakeQuestion("commitizen"),
240+
FakeQuestion("semver"),
241+
],
242+
)
243+
mocker.patch("questionary.confirm", return_value=FakeQuestion(True))
244+
mocker.patch("questionary.text", return_value=FakeQuestion("$version"))
245+
checkbox = mocker.patch("questionary.checkbox")
236246
mocker.patch(
237247
"commitizen.project_info.available_hook_installers",
238248
return_value=[],
239249
)
240250
monkeypatch.chdir(tmp_path)
241-
with pytest.raises(InitFailedError):
242-
commands.Init(config)()
251+
252+
commands.Init(config)()
253+
254+
checkbox.assert_not_called()
255+
captured = capsys.readouterr()
256+
assert "No pre-commit hook detected, skipping question" in captured.out
257+
assert Path("pyproject.toml").read_text(encoding="utf-8") == expected_config
258+
assert not Path(pre_commit_config_filename).exists()
243259

244260

245261
def _init_hook_answers(mocker: MockFixture) -> None:

0 commit comments

Comments
 (0)