From 846698f886b7e231fbe1ad062b25cfa5aad2536c Mon Sep 17 00:00:00 2001 From: Georgios Patsiaouras Date: Tue, 16 May 2023 13:44:41 +0200 Subject: [PATCH] feat(scope): Added support for non optional scope By using the argument --force-scope the presense of scope in the commit message is enforced, meaning that the commit should always have a scope. --- conventional_pre_commit/format.py | 11 ++++++---- conventional_pre_commit/hook.py | 7 +++++-- pyproject.toml | 2 +- tests/conftest.py | 5 +++++ tests/messages/conventional_commit_with_scope | 1 + tests/test_format.py | 21 ++++++++++++++++++- tests/test_hook.py | 12 +++++++++++ 7 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 tests/messages/conventional_commit_with_scope diff --git a/conventional_pre_commit/format.py b/conventional_pre_commit/format.py index b1a6a81..73f4863 100644 --- a/conventional_pre_commit/format.py +++ b/conventional_pre_commit/format.py @@ -21,9 +21,12 @@ def r_types(types): return "|".join(types) -def r_scope(): +def r_scope(optional=True): """Regex str for an optional (scope).""" - return r"(\([\w \/:-]+\))?" + if optional: + return r"(\([\w \/:-]+\))?" + else: + return r"(\([\w \/:-]+\))" def r_delim(): @@ -43,7 +46,7 @@ def conventional_types(types=[]): return types -def is_conventional(input, types=DEFAULT_TYPES): +def is_conventional(input, types=DEFAULT_TYPES, optional_scope=True): """ Returns True if input matches Conventional Commits formatting https://www.conventionalcommits.org @@ -51,7 +54,7 @@ def is_conventional(input, types=DEFAULT_TYPES): Optionally provide a list of additional custom types. """ types = conventional_types(types) - pattern = f"^({r_types(types)}){r_scope()}{r_delim()}{r_subject()}$" + pattern = f"^({r_types(types)}){r_scope(optional_scope)}{r_delim()}{r_subject()}$" regex = re.compile(pattern, re.DOTALL) return bool(regex.match(input)) diff --git a/conventional_pre_commit/hook.py b/conventional_pre_commit/hook.py index 1839f0f..7f464c5 100644 --- a/conventional_pre_commit/hook.py +++ b/conventional_pre_commit/hook.py @@ -20,6 +20,9 @@ def main(argv=[]): ) parser.add_argument("types", type=str, nargs="*", default=format.DEFAULT_TYPES, help="Optional list of types to support") parser.add_argument("input", type=str, help="A file containing a git commit message") + parser.add_argument( + "--force-scope", action="store_false", default=True, dest="optional_scope", help="Force commit to have scope defined." + ) if len(argv) < 1: argv = sys.argv[1:] @@ -44,7 +47,7 @@ def main(argv=[]): ) return RESULT_FAIL - if format.is_conventional(message, args.types): + if format.is_conventional(message, args.types, args.optional_scope): return RESULT_SUCCESS else: print( @@ -66,7 +69,7 @@ def main(argv=[]): fix: remove infinite loop - {Colors.YELLOW}Optionally, include a scope in parentheses after the type for more context:{Colors.RESTORE} + {Colors.YELLOW}Example commit with scope in parentheses after the type for more context:{Colors.RESTORE} fix(account): remove infinite loop""" ) diff --git a/pyproject.toml b/pyproject.toml index 143bf62..c3a0bb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "conventional_pre_commit" -version = "2.2.0" +version = "2.3.0" description = "A pre-commit hook that checks commit messages for Conventional Commits formatting." readme = "README.md" license = { file = "LICENSE" } diff --git a/tests/conftest.py b/tests/conftest.py index 73bd90b..87366c8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,6 +19,11 @@ def conventional_commit_path(): return get_message_path("conventional_commit") +@pytest.fixture +def conventional_commit_with_scope_path(): + return get_message_path("conventional_commit_with_scope") + + @pytest.fixture def custom_commit_path(): return get_message_path("custom_commit") diff --git a/tests/messages/conventional_commit_with_scope b/tests/messages/conventional_commit_with_scope new file mode 100644 index 0000000..15d8933 --- /dev/null +++ b/tests/messages/conventional_commit_with_scope @@ -0,0 +1 @@ +feat(scope): message diff --git a/tests/test_format.py b/tests/test_format.py index 55ffdd0..c9b54ea 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -4,7 +4,6 @@ from conventional_pre_commit import format - CUSTOM_TYPES = ["one", "two"] @@ -23,6 +22,14 @@ def test_r_scope__optional(): assert regex.match("") +def test_r_scope__not_optional(): + result = format.r_scope(optional=False) + regex = re.compile(result) + + # Assert not optional anymore + assert not regex.match("") + + def test_r_scope__parenthesis_required(): result = format.r_scope() regex = re.compile(result) @@ -187,6 +194,18 @@ def test_is_conventional__scope_space(): assert not format.is_conventional(input) +def test_is_conventional__scope_not_optional(): + input = "feat: message" + + assert not format.is_conventional(input, optional_scope=False) + + +def test_is_conventional__scope_not_optional_empty_parenthesis(): + input = "feat(): message" + + assert not format.is_conventional(input, optional_scope=False) + + def test_is_conventional__missing_delimiter(): input = "feat message" diff --git a/tests/test_hook.py b/tests/test_hook.py index 16f3ad9..673c840 100644 --- a/tests/test_hook.py +++ b/tests/test_hook.py @@ -92,3 +92,15 @@ def test_main_fail__conventional_gbk(conventional_gbk_commit_path): result = main([conventional_gbk_commit_path]) assert result == RESULT_FAIL + + +def test_main_fail__conventional_with_scope(cmd, conventional_commit_path): + result = subprocess.call((cmd, "--force-scope", conventional_commit_path)) + + assert result == RESULT_FAIL + + +def test_main_success__conventional_with_scope(cmd, conventional_commit_with_scope_path): + result = subprocess.call((cmd, "--force-scope", conventional_commit_with_scope_path)) + + assert result == RESULT_SUCCESS