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
5 changes: 5 additions & 0 deletions commitizen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@
}
],
},
{
"name": ["init"],
"help": "init commitizen configuration",
"func": commands.Init,
},
],
},
}
Expand Down
14 changes: 13 additions & 1 deletion commitizen/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,17 @@
from .list_cz import ListCz
from .schema import Schema
from .version import Version
from .init import Init

__all__ = ("Bump", "Check", "Commit", "Example", "Info", "ListCz", "Schema", "Version")

__all__ = (
"Bump",
"Check",
"Commit",
"Example",
"Info",
"ListCz",
"Schema",
"Version",
"Init",
)
109 changes: 109 additions & 0 deletions commitizen/commands/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from packaging.version import Version

import questionary

from commitizen import factory, out
from commitizen.cz import registry
from commitizen.config import BaseConfig, TomlConfig, IniConfig
from commitizen.git import get_latest_tag, get_all_tags
from commitizen.defaults import config_files


class Init:
def __init__(self, config: BaseConfig, *args):
self.config: BaseConfig = config
self.cz = factory.commiter_factory(self.config)

def __call__(self):
values_to_add = {}

# No config file exist
if not self.config.path:
config_path = self._ask_config_path()

if "toml" in config_path:
self.config = TomlConfig(data="", path=config_path)
else:
self.config = IniConfig(data="", path=config_path)

self.config.init_empty_config_file()

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)
out.write("The configuration are all set.")
else:
# TODO: handle the case that config file exist but no value
out.line(f"Config file {self.config.path} already exists")

def _ask_config_path(self) -> str:
name = questionary.select(
"Please choose a supported config file: (default: pyproject.tml)",
choices=config_files,
default="pyproject.toml",
style=self.cz.style,
).ask()
return name

def _ask_name(self) -> str:
name = questionary.select(
"Please choose a cz: (default: cz_conventional_commits)",
choices=list(registry.keys()),
default="cz_conventional_commits",
style=self.cz.style,
).ask()
return name

def _ask_tag(self) -> str:
latest_tag = get_latest_tag()
if not latest_tag:
out.error("No Existing Tag. Set tag to v0.0.1")
return "0.0.1"

is_correct_tag = questionary.confirm(
f"Is {latest_tag} the latest tag?", style=self.cz.style, default=False
).ask()
if not is_correct_tag:
tags = get_all_tags()
if not tags:
out.error("No Existing Tag. Set tag to v0.0.1")
return "0.0.1"

latest_tag = questionary.select(
"Please choose the latest tag: ",
choices=get_all_tags(),
style=self.cz.style,
).ask()

if not latest_tag:
out.error("Tag is required!")
raise SystemExit()
return latest_tag

def _ask_tag_format(self, latest_tag) -> str:
is_correct_format = False
if latest_tag.startswith("v"):
tag_format = r"v$version"
is_correct_format = questionary.confirm(
f'Is "{tag_format}" the correct tag format?', style=self.cz.style
).ask()

if not is_correct_format:
tag_format = questionary.text(
'Please enter the correct version format: (default: "$version")',
style=self.cz.style,
).ask()

if not tag_format:
tag_format = "$version"
return tag_format

def _update_config_file(self, values):
if not values:
out.write("The configuration were all set. Nothing to add.")
raise SystemExit()

for key, value in values.items():
self.config.set_key(key, value)
4 changes: 4 additions & 0 deletions commitizen/config/ini_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def __init__(self, *, data: str, path: str):
self._parse_setting(data)
self.add_path(path)

def init_empty_config_file(self):
with open(self.path, "w") as toml_file:
toml_file.write("[commitizen]")

def set_key(self, key, value):
"""Set or update a key in the conf.

Expand Down
4 changes: 4 additions & 0 deletions commitizen/config/toml_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ def __init__(self, *, data: str, path: str):
self._parse_setting(data)
self.add_path(path)

def init_empty_config_file(self):
with open(self.path, "w") as toml_file:
toml_file.write("[tool.commitizen]")

def set_key(self, key, value):
"""Set or update a key in the conf.

Expand Down
15 changes: 15 additions & 0 deletions commitizen/git.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
from tempfile import NamedTemporaryFile
from typing import Optional, List

from commitizen import cmd

Expand Down Expand Up @@ -40,3 +41,17 @@ def is_staging_clean() -> bool:
c = cmd.run("git diff --no-ext-diff --name-only")
c_cached = cmd.run("git diff --no-ext-diff --cached --name-only")
return not (bool(c.out) or bool(c_cached.out))


def get_latest_tag() -> Optional[str]:
c = cmd.run("git describe --abbrev=0 --tags")
if c.err:
return None
return c.out.strip()


def get_all_tags() -> Optional[List[str]]:
c = cmd.run("git tag --list")
if c.err:
return []
return [tag.strip() for tag in c.out.split("\n") if tag.strip()]
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ commands:
schema show commit schema
bump bump semantic version based on the git log
check validates that a commit message matches the commitizen schema
init init commitizen configuration
```

## Contributing
Expand Down
12 changes: 10 additions & 2 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,12 @@ def test_schema(config):

def test_list_cz(config):
with mock.patch("commitizen.out.write") as mocked_write:

commands.ListCz(config)()
mocked_write.assert_called_once()


def test_version(config):
with mock.patch("commitizen.out.write") as mocked_write:

commands.Version(config)()
mocked_write.assert_called_once()

Expand Down Expand Up @@ -214,3 +212,13 @@ def test_check_conventional_commit(config, mocker):
def test_check_command_when_commit_file_not_found(config):
with pytest.raises(FileNotFoundError):
commands.Check(config=config, arguments={"commit_msg_file": ""})()


def test_init_when_config_already_exists(config, capsys):
# Set config path
path = "tests/pyproject.toml"
config.add_path(path)

commands.Init(config)()
captured = capsys.readouterr()
assert captured.out == f"Config file {path} already exists\n"