Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions commitizen/commands/check.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import re
import sys
from typing import Dict, Optional
from typing import Any, Dict, Optional

from commitizen import factory, git, out
from commitizen.config import BaseConfig
Expand All @@ -15,7 +15,7 @@
class Check:
"""Check if the current commit msg matches the commitizen format."""

def __init__(self, config: BaseConfig, arguments: Dict[str, str], cwd=os.getcwd()):
def __init__(self, config: BaseConfig, arguments: Dict[str, Any], cwd=os.getcwd()):
"""Initial check command.

Args:
Expand Down
119 changes: 78 additions & 41 deletions commitizen/commands/init.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import os
import shutil
from typing import Any, Dict, List, Optional

import questionary
import yaml
Expand All @@ -9,7 +11,7 @@
from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig
from commitizen.cz import registry
from commitizen.defaults import config_files
from commitizen.exceptions import NoAnswersError
from commitizen.exceptions import InitFailedError, NoAnswersError
from commitizen.git import get_latest_tag_name, get_tag_names, smart_open


Expand All @@ -19,34 +21,43 @@ def __init__(self, config: BaseConfig, *args):
self.cz = factory.commiter_factory(self.config)

def __call__(self):
values_to_add = {}
if self.config.path:
out.line(f"Config file {self.config.path} already exists")
return

# No config for commitizen exist
if not self.config.path:
config_path = self._ask_config_path()
if "toml" in config_path:
self.config = TomlConfig(data="", path=config_path)
elif "json" in config_path:
self.config = JsonConfig(data="{}", path=config_path)
elif "yaml" in config_path:
self.config = YAMLConfig(data="", path=config_path)

self.config.init_empty_config_content()

values_to_add["name"] = self._ask_name()
tag = self._ask_tag()
values_to_add["version"] = Version(tag).public
values_to_add["tag_format"] = self._ask_tag_format(tag)
self._update_config_file(values_to_add)

if questionary.confirm("Do you want to install pre-commit hook?").ask():
self._install_pre_commit_hook()

out.write("You can bump the version and create changelog running:\n")
out.info("cz bump --changelog")
out.success("The configuration are all set.")
else:
out.line(f"Config file {self.config.path} already exists")
config_path = self._ask_config_path()
if "toml" in config_path:
self.config = TomlConfig(data="", path=config_path)
elif "json" in config_path:
self.config = JsonConfig(data="{}", path=config_path)
elif "yaml" in config_path:
self.config = YAMLConfig(data="", path=config_path)
self.config.init_empty_config_content()

values_to_add = {}
values_to_add["name"] = self._ask_name()
tag = self._ask_tag()
values_to_add["version"] = Version(tag).public
values_to_add["tag_format"] = self._ask_tag_format(tag)
self._update_config_file(values_to_add)

hook_types = questionary.checkbox(
"What types of pre-commit hook you want to install? (Leave blank if you don't want to install)",
choices=[
questionary.Choice("commit-msg", checked=True),
questionary.Choice("pre-push", checked=True),
],
).ask()
if hook_types:
try:
self._install_pre_commit_hook(hook_types)
except InitFailedError as e:
raise InitFailedError(f"Failed to install pre-commit hook.\n{e}")

out.write("You can bump the version and create changelog running:\n")
out.info("cz bump --changelog")
out.success("The configuration are all set.")

def _ask_config_path(self) -> str:
name: str = questionary.select(
Expand Down Expand Up @@ -109,17 +120,45 @@ def _ask_tag_format(self, latest_tag) -> str:
tag_format = "$version"
return tag_format

def _install_pre_commit_hook(self):
def _search_pre_commit(self) -> bool:
"""Check whether pre-commit is installed"""
return shutil.which("pre-commit") is not None

def _exec_install_pre_commit_hook(self, hook_types: List[str]):
cmd_str = self._gen_pre_commit_cmd(hook_types)
c = cmd.run(cmd_str)
if c.return_code != 0:
err_msg = (
f"Error running {cmd_str}."
"Outputs are attached below:\n"
f"stdout: {c.out}\n"
f"stderr: {c.err}"
)
raise InitFailedError(err_msg)

def _gen_pre_commit_cmd(self, hook_types: List[str]) -> str:
"""Generate pre-commit command according to given hook types"""
if not hook_types:
raise ValueError("At least 1 hook type should be provided.")
cmd_str = "pre-commit install " + " ".join(
f"--hook-type {ty}" for ty in hook_types
)
return cmd_str

def _install_pre_commit_hook(self, hook_types: Optional[List[str]] = None):
pre_commit_config_filename = ".pre-commit-config.yaml"
cz_hook_config = {
"repo": "https://github.com/commitizen-tools/commitizen",
"rev": f"v{__version__}",
"hooks": [{"id": "commitizen"}],
"hooks": [
{"id": "commitizen"},
{"id": "commitizen-branch", "stages": ["push"]},
],
}

config_data = {}
if not os.path.isfile(pre_commit_config_filename):
# .pre-commit-config does not exist
# .pre-commit-config.yaml does not exist
config_data["repos"] = [cz_hook_config]
else:
with open(pre_commit_config_filename) as config_file:
Expand All @@ -135,23 +174,21 @@ def _install_pre_commit_hook(self):
else:
config_data["repos"].append(cz_hook_config)
else:
# .pre-commit-config exists but there's no "repos" key
# .pre-commit-config.yaml exists but there's no "repos" key
config_data["repos"] = [cz_hook_config]

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

c = cmd.run("pre-commit install --hook-type commit-msg")
if c.return_code == 127:
out.error(
"pre-commit is not installed in current environement.\n"
"Run 'pre-commit install --hook-type commit-msg' again after it's installed"
if not self._search_pre_commit():
raise InitFailedError(
"pre-commit is not installed in current environement."
)
elif c.return_code != 0:
out.error(c.err)
else:
out.write("commitizen pre-commit hook is now installed in your '.git'\n")
if hook_types is None:
hook_types = ["commit-msg", "pre-push"]
self._exec_install_pre_commit_hook(hook_types)
out.write("commitizen pre-commit hook is now installed in your '.git'\n")

def _update_config_file(self, values):
def _update_config_file(self, values: Dict[str, Any]):
for key, value in values.items():
self.config.set_key(key, value)
5 changes: 5 additions & 0 deletions commitizen/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ExitCode(enum.IntEnum):
UNRECOGNIZED_CHARACTERSET_ENCODING = 22
GIT_COMMAND_ERROR = 23
INVALID_MANUAL_VERSION = 24
INIT_FAILED = 25


class CommitizenException(Exception):
Expand Down Expand Up @@ -163,3 +164,7 @@ class GitCommandError(CommitizenException):

class InvalidManualVersion(CommitizenException):
exit_code = ExitCode.INVALID_MANUAL_VERSION


class InitFailedError(CommitizenException):
exit_code = ExitCode.INIT_FAILED
Loading