Skip to content

Commit 1910b96

Browse files
committed
feat(changelog): adds a git tag parser
The git tag parser is used to filter out undesired git tags from changelog.
1 parent 7916511 commit 1910b96

File tree

7 files changed

+65
-4
lines changed

7 files changed

+65
-4
lines changed

commitizen/changelog.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import re
3030
from collections import OrderedDict, defaultdict
3131
from datetime import date
32-
from typing import Callable, Dict, Iterable, List, Optional, Tuple
32+
from typing import Callable, Dict, Iterable, List, Optional, Pattern, Tuple
3333

3434
from jinja2 import Environment, PackageLoader
3535

@@ -74,6 +74,7 @@ def generate_tree_from_commits(
7474
unreleased_version: Optional[str] = None,
7575
change_type_map: Optional[Dict[str, str]] = None,
7676
changelog_message_builder_hook: Optional[Callable] = None,
77+
tag_pattern: Pattern = re.compile(".*"),
7778
) -> Iterable[Dict]:
7879
pat = re.compile(changelog_pattern)
7980
map_pat = re.compile(commit_parser, re.MULTILINE)
@@ -97,14 +98,15 @@ def generate_tree_from_commits(
9798
commit_tag = get_commit_tag(commit, tags)
9899

99100
if commit_tag is not None and commit_tag not in used_tags:
101+
matches = tag_pattern.fullmatch(commit_tag.name)
102+
if not matches:
103+
continue
100104
used_tags.append(commit_tag)
101105
yield {
102106
"version": current_tag_name,
103107
"date": current_tag_date,
104108
"changes": changes,
105109
}
106-
# TODO: Check if tag matches the version pattern, otherwise skip it.
107-
# This in order to prevent tags that are not versions.
108110
current_tag_name = commit_tag.name
109111
current_tag_date = commit_tag.date
110112
changes = defaultdict(list)

commitizen/commands/changelog.py

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ def __call__(self):
154154
unreleased_version,
155155
change_type_map=change_type_map,
156156
changelog_message_builder_hook=changelog_message_builder_hook,
157+
tag_pattern=self.cz.tag_pattern,
157158
)
158159
if self.change_type_order:
159160
tree = changelog.order_changelog_tree(tree, self.change_type_order)

commitizen/cz/base.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import re
12
from abc import ABCMeta, abstractmethod
2-
from typing import Callable, Dict, List, Optional, Tuple
3+
from typing import Callable, Dict, List, Optional, Pattern, Tuple
34

45
from prompt_toolkit.styles import Style, merge_styles
56

@@ -31,6 +32,8 @@ class BaseCommitizen(metaclass=ABCMeta):
3132
changelog_pattern: Optional[str] = r".*"
3233
change_type_map: Optional[Dict[str, str]] = None
3334
change_type_order: Optional[List[str]] = None
35+
tag_parser = ".*"
36+
tag_pattern: Pattern
3437

3538
# Executed per message parsed by the commitizen
3639
changelog_message_builder_hook: Optional[
@@ -45,6 +48,8 @@ def __init__(self, config: BaseConfig):
4548
if not self.config.settings.get("style"):
4649
self.config.settings.update({"style": BaseCommitizen.default_style_config})
4750

51+
self.tag_pattern = re.compile(self.tag_parser)
52+
4853
@abstractmethod
4954
def questions(self) -> Questions:
5055
"""Questions regarding the commit message."""

commitizen/cz/customize/customize.py

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ def __init__(self, config: BaseConfig):
5050
if change_type_map:
5151
self.change_type_map = change_type_map
5252

53+
tag_parser = self.custom_settings.get("tag_pattern")
54+
if tag_parser:
55+
self.tag_parser = str(tag_parser)
56+
5357
def questions(self) -> Questions:
5458
return self.custom_settings.get("questions", [{}])
5559

docs/changelog.md

+7
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ cz changelog --start-rev="v0.2.0"
161161
changelog_start_rev = "v0.2.0"
162162
```
163163
164+
### `tag_parser`
165+
166+
This value can be set om the `toml` file with the key `tag_parser` under `tools.commitizen`
167+
168+
Be default the changlog will capture all git tag (e.g. regex `.*`). To specify a tag pattern the user may create a regex pattern to for whichever tags they would like captured.
169+
Any tag that does not match is effectively treated as if it does not exist.
170+
164171
## Hooks
165172
166173
Supported hook methods:

docs/config.md

+2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ commitizen:
128128
| `version` | `str` | `None` | Current version. Example: "0.1.2" |
129129
| `version_files` | `list` | `[ ]` | Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [See more][version_files] |
130130
| `tag_format` | `str` | `None` | Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [See more][tag_format] |
131+
| `tag_parser ` | `str` | `.*` | Generate changelog using only tags matching the regex pattern (e.g. `"v([0-9.])*"`). [See more][tag_parser] |
131132
| `update_changelog_on_bump` | `bool` | `false` | Create changelog when running `cz bump` |
132133
| `annotated_tag` | `bool` | `false` | Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] |
133134
| `bump_message` | `str` | `None` | Create custom commit message, useful to skip ci. [See more][bump_message] |
@@ -142,6 +143,7 @@ commitizen:
142143
[version_files]: bump.md#version_files
143144
[tag_format]: bump.md#tag_format
144145
[bump_message]: bump.md#bump_message
146+
[tag_parser]: changelog.md#tag_parser
145147
[allow_abort]: check.md#allow-abort
146148
[additional-features]: https://github.com/tmbo/questionary#additional-features
147149
[customization]: customization.md

tests/test_changelog.py

+40
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import re
2+
from typing import Dict, Iterable, List, Pattern
3+
14
import pytest
25

36
from commitizen import changelog, defaults, git
@@ -790,6 +793,26 @@ def test_get_commit_tag_is_None(gitcommits, tags):
790793
)
791794

792795

796+
def _filter_tree(tag_pattern: Pattern, tree: Iterable[Dict]) -> List[Dict[str, str]]:
797+
"""filters the tree. commits with invalid tags are kept within the current node"""
798+
799+
current = None
800+
out = []
801+
for node in tree:
802+
if not current or tag_pattern.fullmatch(node["version"]) or not out:
803+
current = node.copy()
804+
out.append(current)
805+
else:
806+
changes = current["changes"]
807+
for key, value in node["changes"].items():
808+
if key in changes:
809+
changes[key].extend(value)
810+
else:
811+
changes[key] = value
812+
813+
return out
814+
815+
793816
def test_generate_tree_from_commits(gitcommits, tags):
794817
parser = defaults.commit_parser
795818
changelog_pattern = defaults.bump_pattern
@@ -800,6 +823,23 @@ def test_generate_tree_from_commits(gitcommits, tags):
800823
assert tuple(tree) == COMMITS_TREE
801824

802825

826+
def test_generate_tree_from_commits_release_filter(gitcommits, tags):
827+
parser = defaults.commit_parser
828+
changelog_pattern = defaults.bump_pattern
829+
830+
# match release tags only
831+
tag_parser = r"v([0-9]+)\.([0-9]+)\.([0-9]+)"
832+
tag_pattern = re.compile(tag_parser)
833+
834+
tree = changelog.generate_tree_from_commits(
835+
gitcommits, tags, parser, changelog_pattern, tag_pattern=tag_pattern
836+
)
837+
838+
expected_tree = _filter_tree(tag_pattern, COMMITS_TREE)
839+
840+
assert list(tree) == expected_tree
841+
842+
803843
@pytest.mark.parametrize(
804844
"change_type_order, expected_reordering",
805845
(

0 commit comments

Comments
 (0)