Skip to content

Commit 5faf92c

Browse files
authored
Merge pull request #84 from dany2691/cz-check
feat(Commands/check): enforce the project to always use conventional …
2 parents 8ff68ed + 221f528 commit 5faf92c

File tree

5 files changed

+127
-2
lines changed

5 files changed

+127
-2
lines changed

README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,14 @@ Usage
160160
--version get the version of the installed commitizen
161161

162162
commands:
163-
{ls,commit,c,example,info,schema,bump}
163+
{ls,commit,c,example,info,schema,bump,check}
164164
ls show available commitizens
165165
commit (c) create new commit
166166
example show commit example
167167
info show information about the cz
168168
schema show commit schema
169169
bump bump semantic version based on the git log
170+
check enforce the project to always use conventional commits
170171

171172
Contributing
172173
============

commitizen/cli.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,21 @@
111111
"help": "get the version of the installed commitizen",
112112
"func": commands.Version,
113113
},
114+
{
115+
"name": ["check"],
116+
"help": "enforce the project to always use conventional commits",
117+
"func": commands.Check,
118+
"arguments": [
119+
{
120+
"name": "--commit-msg-file",
121+
"help": (
122+
"ask for the name of the temporal file that contains "
123+
"the commit message. "
124+
"Using it in a git hook script: MSG_FILE=$1"
125+
),
126+
}
127+
],
128+
},
114129
],
115130
},
116131
}

commitizen/commands/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from .bump import Bump
22
from .commit import Commit
3+
from .check import Check
34
from .example import Example
45
from .info import Info
56
from .list_cz import ListCz
67
from .schema import Schema
78
from .version import Version
89

9-
__all__ = ("Bump", "Commit", "Example", "Info", "ListCz", "Schema", "Version")
10+
__all__ = ("Bump", "Check", "Commit", "Example", "Info", "ListCz", "Schema",
11+
"Version")

commitizen/commands/check.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os
2+
import re
3+
4+
from commitizen import out
5+
6+
PATTERN = (r'(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert)'
7+
r'(\([\w\-]+\))?:\s.*')
8+
NO_COMMIT_MSG = 3
9+
INVALID_COMMIT_MSG = 5
10+
11+
12+
class Check:
13+
"""Check if the current commit msg is a conventional commit."""
14+
15+
def __init__(self, config: dict, arguments: dict, cwd=os.getcwd()):
16+
"""Init method.
17+
18+
Parameters
19+
----------
20+
config : dict
21+
the config object required for the command to perform its action
22+
arguments : dict
23+
the arguments object that contains all
24+
the flags provided by the user
25+
26+
"""
27+
self.config: dict = config
28+
self.arguments: dict = arguments
29+
30+
def __call__(self):
31+
"""Validate if a commit message follows the conventional pattern.
32+
33+
Raises
34+
------
35+
SystemExit
36+
if the commit provided not follows the conventional pattern
37+
38+
"""
39+
commit_msg_content = self._get_commit_msg()
40+
if self._is_conventional(PATTERN, commit_msg_content) is not None:
41+
out.success("Conventional commit validation: successful!")
42+
else:
43+
out.error("conventional commit validation: failed!")
44+
out.error("please enter a commit message in the conventional format.")
45+
raise SystemExit(INVALID_COMMIT_MSG)
46+
47+
def _get_commit_msg(self):
48+
temp_filename: str = self.arguments.get("commit_msg_file")
49+
return open(temp_filename, 'r').read()
50+
51+
def _is_conventional(self, pattern, commit_msg):
52+
return re.match(PATTERN, commit_msg)

tests/test_commands.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import contextlib
12
import os
3+
import shutil
4+
import tempfile
25
from unittest import mock
36

47
import pytest
@@ -15,6 +18,15 @@ def staging_is_clean(mocker):
1518
is_staging_clean_mock.return_value = False
1619

1720

21+
@contextlib.contextmanager
22+
def get_temp_dir():
23+
temp_dir = tempfile.mkdtemp()
24+
try:
25+
yield temp_dir
26+
finally:
27+
shutil.rmtree(temp_dir)
28+
29+
1830
@pytest.mark.usefixtures("staging_is_clean")
1931
def test_commit(mocker):
2032
prompt_mock = mocker.patch("questionary.prompt")
@@ -158,3 +170,46 @@ def test_version():
158170

159171
commands.Version(config)()
160172
mocked_write.assert_called_once()
173+
174+
175+
def test_check_no_conventional_commit(mocker):
176+
with pytest.raises(SystemExit):
177+
error_mock = mocker.patch("commitizen.out.error")
178+
179+
with get_temp_dir() as dir:
180+
181+
tempfile = os.path.join(dir, "temp_commit_file")
182+
with open(tempfile, 'w') as f:
183+
f.write("no conventional commit")
184+
185+
check_cmd = commands.Check(
186+
config=config,
187+
arguments={"commit_msg_file": tempfile}
188+
)
189+
check_cmd()
190+
error_mock.assert_called_once()
191+
192+
193+
def test_check_conventional_commit(mocker):
194+
success_mock = mocker.patch("commitizen.out.success")
195+
with get_temp_dir() as dir:
196+
197+
tempfile = os.path.join(dir, "temp_commit_file")
198+
with open(tempfile, 'w') as f:
199+
f.write("feat(lang): added polish language")
200+
201+
check_cmd = commands.Check(
202+
config=config,
203+
arguments={"commit_msg_file": tempfile}
204+
)
205+
206+
check_cmd()
207+
success_mock.assert_called_once()
208+
209+
210+
def test_check_command_when_commit_file_not_found():
211+
with pytest.raises(FileNotFoundError):
212+
commands.Check(
213+
config=config,
214+
arguments={"commit_msg_file": ""}
215+
)()

0 commit comments

Comments
 (0)