diff --git a/.cz.toml b/.cz.toml index fb7a260..6cc2c8b 100644 --- a/.cz.toml +++ b/.cz.toml @@ -8,5 +8,5 @@ version_files = [ "README.md:^docker" ] bump_message = "BUMP: version $current_version → $new_version" -commit_url = "https://github.com/kpn/cz-kpn/commit/$COMMIT_REV" update_changelog_on_bump = true +kpn_commit_url = "https://github.com/kpn/cz-kpn/commit/$COMMIT_REV" diff --git a/README.md b/README.md index c0781ce..1dbbeeb 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,20 @@ NEW: Add login screen (#MY-123) [MORE INFO](./src/cz_kpn/cz_kpn_info.txt) +## Custom configuration + +This rules support custom configuration. You can set the following options in your `pyproject.toml` file: + +```toml +[tool.commitizen] +# ... +kpn_strict_check = true +kpn_commit_url = "https://github.com/kpn/cz-kpn/commit/$COMMIT_REV" +``` + +- `kpn_strict_check`: Enable strict mode during `cz check` and `cz commit`. +- `kpn_commit_url`: URL to the commit page on your version control system. Which will be used to generate the changelog. + ## Installation Install globally in your system @@ -68,7 +82,7 @@ Just run: cz bump ``` -### Commiting +### Committing ```sh cz commit @@ -149,15 +163,10 @@ More info in [commitizen website](https://commitizen-tools.github.io/commitizen/ cz check --rev-range ugnu348hg84hg84g..j8fj84g84h84hg83h2392 ``` -### Adding commit link to the changelog - -Add to the configuration the `commit_url` parameter, using `$COMMIT_REV` as -variable, you can take a look at this project's [.cz.toml](.cz.toml) as an example +You can also check against the last version: -```toml -[tool.commitizen] -... -commit_url = "https://YOUR_DOMAIN/projects/YOUR_GROUP/repos/YOUR_PROJECT/commits/$COMMIT_REV" +```bash +cz check --rev-range "$(cz version -p).." ``` ## Configuration @@ -186,15 +195,19 @@ Contents: ```bash $ cz --help -Commitizen is a cli tool to generate conventional commits. -For more information about the topic go to https://conventionalcommits.org/ +usage: cz [-h] [--config CONFIG] [--debug] [-n NAME] [-nr NO_RAISE] + {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ... + +Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management. +For more information, please visit https://commitizen-tools.github.io/commitizen options: -h, --help show this help message and exit + --config CONFIG the path of configuration file --debug use debug mode -n NAME, --name NAME use the given commitizen (default: cz_conventional_commits) -nr NO_RAISE, --no-raise NO_RAISE - comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at + comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/ commands: diff --git a/src/cz_kpn/consts.py b/src/cz_kpn/consts.py index c362a26..5419ba9 100644 --- a/src/cz_kpn/consts.py +++ b/src/cz_kpn/consts.py @@ -22,3 +22,9 @@ BUMP_PATTERN = r"^(NEW|CHANGE|FIX|OPT|BREAK)" COMMIT_PARSER = r"^(?PNEW|CHANGE|FIX|OPT|BREAK)(?:\((?P[^()\r\n]*)\)|\()?:?\s(?P.+)" # noqa +COMMIT_PARSER_STRICT = r"(?PBREAK|CHANGE|NEW|FIX|OPT)(\((?=[a-z]))?(?P(?<=\()(test|ci|docs|build)(?=\)))?((?<=[a-z])\))?: .{3,79} (?P\(#[A-Z]+-[0-9]+\)|\(#[0-9]+\))" # noqa + + +## CUSTOM SETTINGS KEYS +STRICT_CHECK = "kpn_strict_check" +COMMIT_URL = "kpn_commit_url" diff --git a/src/cz_kpn/rules.py b/src/cz_kpn/rules.py index ae9d536..c4aaaf1 100644 --- a/src/cz_kpn/rules.py +++ b/src/cz_kpn/rules.py @@ -5,6 +5,7 @@ from commitizen import git from commitizen.cz.base import BaseCommitizen +from commitizen.cz.utils import required_validator from commitizen.question import CzQuestion from cz_kpn.consts import ( @@ -12,15 +13,30 @@ BREAK_DESCR, BUMP_PATTERN, COMMIT_PARSER, + COMMIT_PARSER_STRICT, + COMMIT_URL, FIX, FIX_DESCR, NEW, NEW_DESCR, OPT, OPT_DESCR, + STRICT_CHECK, ) +def _parse_subject(text: str) -> str: + value = text.strip(".").strip() + msg = "" + if not value: + msg = "Subject is required." + elif len(value) < 3: + msg = "Subject must be at least 3 characters long." + elif len(value) > 79: + msg = "Subject must be at most 79 characters long." + return required_validator(value, msg=msg) + + class KPNCz(BaseCommitizen): bump_pattern = BUMP_PATTERN bump_map = { @@ -47,6 +63,17 @@ class KPNCz(BaseCommitizen): "FIX": "Fixes", } + def parse_issue(self, text: str) -> str: + """Parse issue ID. + + If valid it's converted to uppercase. + if strict enabled, it's required. + """ + kpn_strict = self.config.settings.get(STRICT_CHECK, False) + if kpn_strict: + return required_validator(text.upper(), msg="Issue ID is required") + return text.upper() + def questions(self) -> Iterable[CzQuestion]: questions: list[CzQuestion] = [ { @@ -60,8 +87,18 @@ def questions(self) -> Iterable[CzQuestion]: {"value": BREAK, "name": f"{BREAK} - {BREAK_DESCR}"}, ], }, - {"type": "input", "name": "title", "message": "Short description:\n"}, - {"type": "input", "name": "issue", "message": "Issue ID:\n"}, + { + "type": "input", + "name": "title", + "message": "Short description:\n", + "filter": _parse_subject, + }, + { + "type": "input", + "name": "issue", + "message": "Issue ID:\n", + "filter": self.parse_issue, + }, {"type": "input", "name": "description", "message": "Long description:\n"}, ] return questions @@ -99,10 +136,10 @@ def info(self) -> str: with open(filepath, "r") as f: return f.read() - def changelog_message_builder_hook( # type: ignore + def changelog_message_builder_hook( self, message: dict[str, Any], commit: git.GitCommit ) -> dict[str, Any]: - commit_url: str = self.config.settings.get("commit_url") # type: ignore + commit_url: str | None = self.config.settings.get(COMMIT_URL) # type: ignore if commit_url: t = Template(commit_url) url = t.safe_substitute(COMMIT_REV=commit.rev) @@ -112,4 +149,7 @@ def changelog_message_builder_hook( # type: ignore return message def schema_pattern(self) -> str: + kpn_strict = self.config.settings.get(STRICT_CHECK, False) + if kpn_strict: + return COMMIT_PARSER_STRICT return COMMIT_PARSER diff --git a/tests/test_commit_parser.py b/tests/test_commit_parser.py index f341968..f7284b3 100644 --- a/tests/test_commit_parser.py +++ b/tests/test_commit_parser.py @@ -3,9 +3,9 @@ import pytest try: - from cz_kpn.consts import COMMIT_PARSER + from cz_kpn.consts import COMMIT_PARSER, COMMIT_PARSER_STRICT except AttributeError: - from cz_kpn.consts import COMMIT_PARSER + from cz_kpn.consts import COMMIT_PARSER, COMMIT_PARSER_STRICT MESSAGE_AND_VALIDITY = [ ("BREAK: New commit with break to bump auto-tag (#3625)", True), @@ -40,3 +40,40 @@ def test_parser_regex(message, result): print(message.groupdict()) assert is_valid == result + + +STRICT_MESSAGE_AND_VALIDITY = [ + ("BREAK: New commit with break to bump auto-tag (#3625)", True), + ("BREAK: Change foobar into quux (#DE-2053)", True), + ("NEW: BaseView for all my generic functionality (#J-1)", True), + ("NEW: FancyView using BaseView that does something fancy (#J-2)", True), + ("BREAK: Change foobar into quux (#DE-2053)", True), + ("BREAK: Foo (#1)", True), + ("BREAK: Foo (#99999999)", True), + ("OPT: Small fixes for the FancyView", False), + ("FIX: Bug in the BaseView", False), + ("OPT(test): Written unittests for the FancyView", False), + ("FIX: Processed pull-request feedback", False), + ("FIX: Missing mock data for FancyView and BaseView", False), + ("FIX: Something completely unrelated", False), + ("NEW: Add new InboxMessage and Image version (XXX-3083)", False), + ("NEW XXX-2966 Add support for teams per campaign", False), + ("NEW Add support for teams per campaign", False), + ("feat: add stuff", False), + ("Add README", False), + ("fix stuff", False), + ("foo(bar): pepe", False), + ("FIX: ", False), +] + + +@pytest.mark.parametrize("message, result", STRICT_MESSAGE_AND_VALIDITY) +def test_parser_regex_strict(message, result): + map_pat = re.compile(COMMIT_PARSER_STRICT, re.MULTILINE) + print(COMMIT_PARSER_STRICT) + message = map_pat.match(message) + is_valid = bool(message) + if is_valid: + print(message.groupdict()) + + assert is_valid == result