Skip to content

Commit 947cc68

Browse files
BogayLee-W
authored andcommitted
fix(init): report error when hook installation failed
1 parent 3c7d67d commit 947cc68

File tree

3 files changed

+71
-42
lines changed

3 files changed

+71
-42
lines changed

commitizen/commands/init.py

+57-41
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
import shutil
3+
from typing import Any, Dict
24

35
import questionary
46
import yaml
@@ -9,7 +11,7 @@
911
from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig
1012
from commitizen.cz import registry
1113
from commitizen.defaults import config_files
12-
from commitizen.exceptions import NoAnswersError
14+
from commitizen.exceptions import InitFailedError, NoAnswersError
1315
from commitizen.git import get_latest_tag_name, get_tag_names, smart_open
1416

1517

@@ -19,34 +21,36 @@ def __init__(self, config: BaseConfig, *args):
1921
self.cz = factory.commiter_factory(self.config)
2022

2123
def __call__(self):
22-
values_to_add = {}
24+
if self.config.path:
25+
out.line(f"Config file {self.config.path} already exists")
26+
return
2327

2428
# No config for commitizen exist
25-
if not self.config.path:
26-
config_path = self._ask_config_path()
27-
if "toml" in config_path:
28-
self.config = TomlConfig(data="", path=config_path)
29-
elif "json" in config_path:
30-
self.config = JsonConfig(data="{}", path=config_path)
31-
elif "yaml" in config_path:
32-
self.config = YAMLConfig(data="", path=config_path)
33-
34-
self.config.init_empty_config_content()
35-
36-
values_to_add["name"] = self._ask_name()
37-
tag = self._ask_tag()
38-
values_to_add["version"] = Version(tag).public
39-
values_to_add["tag_format"] = self._ask_tag_format(tag)
40-
self._update_config_file(values_to_add)
41-
42-
if questionary.confirm("Do you want to install pre-commit hook?").ask():
43-
self._install_pre_commit_hook()
44-
45-
out.write("You can bump the version and create changelog running:\n")
46-
out.info("cz bump --changelog")
47-
out.success("The configuration are all set.")
48-
else:
49-
out.line(f"Config file {self.config.path} already exists")
29+
config_path = self._ask_config_path()
30+
if "toml" in config_path:
31+
self.config = TomlConfig(data="", path=config_path)
32+
elif "json" in config_path:
33+
self.config = JsonConfig(data="{}", path=config_path)
34+
elif "yaml" in config_path:
35+
self.config = YAMLConfig(data="", path=config_path)
36+
self.config.init_empty_config_content()
37+
38+
values_to_add = {}
39+
values_to_add["name"] = self._ask_name()
40+
tag = self._ask_tag()
41+
values_to_add["version"] = Version(tag).public
42+
values_to_add["tag_format"] = self._ask_tag_format(tag)
43+
self._update_config_file(values_to_add)
44+
45+
if questionary.confirm("Do you want to install pre-commit hook?").ask():
46+
if not self._install_pre_commit_hook():
47+
raise InitFailedError(
48+
"Installation failed. See error outputs for more information."
49+
)
50+
51+
out.write("You can bump the version and create changelog running:\n")
52+
out.info("cz bump --changelog")
53+
out.success("The configuration are all set.")
5054

5155
def _ask_config_path(self) -> str:
5256
name: str = questionary.select(
@@ -109,7 +113,20 @@ def _ask_tag_format(self, latest_tag) -> str:
109113
tag_format = "$version"
110114
return tag_format
111115

112-
def _install_pre_commit_hook(self):
116+
def _search_pre_commit(self):
117+
return shutil.which("pre-commit") is not None
118+
119+
def _exec_install_pre_commit_hook(self):
120+
cmd_str = "pre-commit install --hook-type commit-msg"
121+
c = cmd.run(cmd_str)
122+
if c.return_code != 0:
123+
out.error(f"Error running {cmd_str}. Outputs are attached below:")
124+
out.error(f"stdout: {c.out}")
125+
out.error(f"stderr: {c.err}")
126+
return False
127+
return True
128+
129+
def _install_pre_commit_hook(self) -> bool:
113130
pre_commit_config_filename = ".pre-commit-config.yaml"
114131
cz_hook_config = {
115132
"repo": "https://github.com/commitizen-tools/commitizen",
@@ -119,7 +136,7 @@ def _install_pre_commit_hook(self):
119136

120137
config_data = {}
121138
if not os.path.isfile(pre_commit_config_filename):
122-
# .pre-commit-config does not exist
139+
# .pre-commit-config.yaml does not exist
123140
config_data["repos"] = [cz_hook_config]
124141
else:
125142
with open(pre_commit_config_filename) as config_file:
@@ -135,23 +152,22 @@ def _install_pre_commit_hook(self):
135152
else:
136153
config_data["repos"].append(cz_hook_config)
137154
else:
138-
# .pre-commit-config exists but there's no "repos" key
155+
# .pre-commit-config.yaml exists but there's no "repos" key
139156
config_data["repos"] = [cz_hook_config]
140157

141158
with smart_open(pre_commit_config_filename, "w") as config_file:
142159
yaml.safe_dump(config_data, stream=config_file)
143160

144-
c = cmd.run("pre-commit install --hook-type commit-msg")
145-
if c.return_code == 127:
146-
out.error(
147-
"pre-commit is not installed in current environement.\n"
148-
"Run 'pre-commit install --hook-type commit-msg' again after it's installed"
149-
)
150-
elif c.return_code != 0:
151-
out.error(c.err)
152-
else:
153-
out.write("commitizen pre-commit hook is now installed in your '.git'\n")
161+
if not self._search_pre_commit():
162+
out.error("pre-commit is not installed in current environement.")
163+
return False
164+
165+
if not self._exec_install_pre_commit_hook():
166+
return False
167+
168+
out.write("commitizen pre-commit hook is now installed in your '.git'\n")
169+
return True
154170

155-
def _update_config_file(self, values):
171+
def _update_config_file(self, values: Dict[str, Any]):
156172
for key, value in values.items():
157173
self.config.set_key(key, value)

commitizen/exceptions.py

+5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class ExitCode(enum.IntEnum):
2929
UNRECOGNIZED_CHARACTERSET_ENCODING = 22
3030
GIT_COMMAND_ERROR = 23
3131
INVALID_MANUAL_VERSION = 24
32+
INIT_FAILED = 25
3233

3334

3435
class CommitizenException(Exception):
@@ -163,3 +164,7 @@ class GitCommandError(CommitizenException):
163164

164165
class InvalidManualVersion(CommitizenException):
165166
exit_code = ExitCode.INVALID_MANUAL_VERSION
167+
168+
169+
class InitFailedError(CommitizenException):
170+
exit_code = ExitCode.INIT_FAILED

tests/commands/test_init_command.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,15 @@ def default_choice(_, request, mocker):
105105
)
106106
mocker.patch("questionary.confirm", return_value=FakeQuestion(True))
107107
mocker.patch("questionary.text", return_value=FakeQuestion("$version"))
108-
mocker.patch("questionary.confirm", return_value=FakeQuestion(True))
108+
# Assume the `pre-commit` is installed
109+
mocker.patch(
110+
"commitizen.commands.init.Init._search_pre_commit",
111+
return_value=True,
112+
)
113+
mocker.patch(
114+
"commitizen.commands.init.Init._exec_install_pre_commit_hook",
115+
return_value=True,
116+
)
109117
return request.param
110118

111119
def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config):

0 commit comments

Comments
 (0)