Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 53c8237

Browse files
committed
refactor(changelog): use functions from changelog.py
1 parent 65d8268 commit 53c8237

File tree

13 files changed

+1569
-192
lines changed

13 files changed

+1569
-192
lines changed

‎CHANGELOG.md‎

Lines changed: 434 additions & 69 deletions
Large diffs are not rendered by default.

‎commitizen/changelog.py‎

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,31 @@
1313
1. get commits between versions
1414
2. filter commits with the current cz rules
1515
3. parse commit information
16-
4. generate tree
16+
4. yield tree nodes
17+
5. format tree nodes
18+
6. produce full tree
19+
7. generate changelog
1720
18-
Options:
21+
Extra:
1922
- Generate full or partial changelog
23+
- Include in tree from file all the extra comments added manually
2024
"""
2125
import re
22-
from typing import Dict, Generator, Iterable, List
26+
from collections import OrderedDict, defaultdict
27+
from typing import Dict, Generator, Iterable, List, Optional, Type
28+
29+
import pkg_resources
30+
from jinja2 import Template
31+
from typing_extensions import Protocol
32+
33+
from commitizen import defaults
34+
from commitizen.git import GitCommit, GitObject, GitProtocol, GitTag
2335

2436
MD_VERSION_RE = r"^##\s(?P<version>[a-zA-Z0-9.+]+)\s?\(?(?P<date>[0-9-]+)?\)?"
2537
MD_CHANGE_TYPE_RE = r"^###\s(?P<change_type>[a-zA-Z0-9.+\s]+)"
26-
MD_MESSAGE_RE = r"^-\s(\*{2}(?P<scope>[a-zA-Z0-9]+)\*{2}:\s)?(?P<message>.+)"
38+
MD_MESSAGE_RE = (
39+
r"^-\s(\*{2}(?P<scope>[a-zA-Z0-9]+)\*{2}:\s)?(?P<message>.+)(?P<breaking>!)?"
40+
)
2741
md_version_c = re.compile(MD_VERSION_RE)
2842
md_change_type_c = re.compile(MD_CHANGE_TYPE_RE)
2943
md_message_c = re.compile(MD_MESSAGE_RE)
@@ -50,11 +64,11 @@ def find_version_blocks(filepath: str) -> Generator:
5064
```
5165
## 1.2.1 (2019年07月20日)
5266
53-
## Bug fixes
67+
### Fix
5468
5569
- username validation not working
5670
57-
## Features
71+
### Feat
5872
5973
- new login system
6074
@@ -98,6 +112,8 @@ def parse_md_message(md_message: str) -> Dict:
98112

99113

100114
def transform_change_type(change_type: str) -> str:
115+
# TODO: Use again to parse, for this we have to wait until the maps get
116+
# defined again.
101117
_change_type_lower = change_type.lower()
102118
for match_value, output in CATEGORIES:
103119
if re.search(match_value, _change_type_lower):
@@ -107,22 +123,27 @@ def transform_change_type(change_type: str) -> str:
107123

108124

109125
def generate_block_tree(block: List[str]) -> Dict:
110-
tree: Dict = {"commits": []}
126+
# tree: Dict = {"commits": []}
127+
changes: Dict = defaultdict(list)
128+
tree: Dict = {"changes": changes}
129+
111130
change_type = None
112131
for line in block:
113132
if line.startswith("## "):
133+
# version identified
114134
change_type = None
115135
tree = {**tree, **parse_md_version(line)}
116136
elif line.startswith("### "):
137+
# change_type identified
117138
result = parse_md_change_type(line)
118139
if not result:
119140
continue
120-
change_type = transform_change_type(result.get("change_type", ""))
141+
change_type = result.get("change_type", "").lower()
121142

122143
elif line.startswith("- "):
144+
# message identified
123145
commit = parse_md_message(line)
124-
commit["change_type"] = change_type
125-
tree["commits"].append(commit)
146+
changes[change_type].append(commit)
126147
else:
127148
print("it's something else: ", line)
128149
return tree
@@ -131,3 +152,82 @@ def generate_block_tree(block: List[str]) -> Dict:
131152
def generate_full_tree(blocks: Iterable) -> Iterable[Dict]:
132153
for block in blocks:
133154
yield generate_block_tree(block)
155+
156+
157+
def get_commit_tag(commit: GitProtocol, tags: List[GitProtocol]) -> Optional[GitTag]:
158+
""""""
159+
try:
160+
tag_index = tags.index(commit)
161+
except ValueError:
162+
return None
163+
else:
164+
tag = tags[tag_index]
165+
# if hasattr(tag, "name"):
166+
return tag
167+
168+
169+
def generate_tree_from_commits(
170+
commits: List[GitCommit],
171+
tags: List[GitTag],
172+
commit_parser: str,
173+
changelog_pattern: str = defaults.bump_pattern,
174+
) -> Iterable[Dict]:
175+
pat = re.compile(changelog_pattern)
176+
map_pat = re.compile(commit_parser)
177+
# Check if the latest commit is not tagged
178+
latest_commit = commits[0]
179+
current_tag: Optional[GitTag] = get_commit_tag(latest_commit, tags)
180+
181+
current_tag_name: str = "Unreleased"
182+
current_tag_date: str = ""
183+
if current_tag is not None and current_tag.name:
184+
current_tag_name = current_tag.name
185+
current_tag_date = current_tag.date
186+
187+
changes: Dict = defaultdict(list)
188+
used_tags: List = [current_tag]
189+
for commit in commits:
190+
commit_tag = get_commit_tag(commit, tags)
191+
192+
if commit_tag is not None and commit_tag not in used_tags:
193+
used_tags.append(commit_tag)
194+
yield {
195+
"version": current_tag_name,
196+
"date": current_tag_date,
197+
"changes": changes,
198+
}
199+
# TODO: Check if tag matches the version pattern, otherwie skip it.
200+
# This in order to prevent tags that are not versions.
201+
current_tag_name = commit_tag.name
202+
current_tag_date = commit_tag.date
203+
changes = defaultdict(list)
204+
205+
matches = pat.match(commit.message)
206+
if not matches:
207+
continue
208+
209+
message = map_pat.match(commit.message)
210+
message_body = map_pat.match(commit.body)
211+
if message:
212+
parsed_message: Dict = message.groupdict()
213+
change_type = parsed_message.pop("change_type")
214+
changes[change_type].append(parsed_message)
215+
if message_body:
216+
parsed_message_body: Dict = message_body.groupdict()
217+
change_type = parsed_message_body.pop("change_type")
218+
changes[change_type].append(parsed_message_body)
219+
220+
yield {
221+
"version": current_tag_name,
222+
"date": current_tag_date,
223+
"changes": changes,
224+
}
225+
226+
227+
def render_changelog(tree: Iterable) -> str:
228+
template_file = pkg_resources.resource_string(
229+
__name__, "templates/keep_a_changelog_template.j2"
230+
).decode("utf-8")
231+
jinja_template = Template(template_file, trim_blocks=True)
232+
changelog: str = jinja_template.render(tree=tree)
233+
return changelog

‎commitizen/commands/changelog.py‎

Lines changed: 52 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import pkg_resources
55
from jinja2 import Template
66

7-
from commitizen import factory, git, out
7+
from commitizen import factory, git, out, changelog
88
from commitizen.config import BaseConfig
9-
from commitizen.error_codes import NO_COMMITS_FOUND, NO_PATTERN_MAP
9+
from commitizen.error_codes import NO_COMMITS_FOUND, NO_PATTERN_MAP, TAG_FAILED
1010

1111

1212
class Changelog:
@@ -21,59 +21,68 @@ def __init__(self, config: BaseConfig, args):
2121
self.start_rev = args["start_rev"]
2222

2323
def __call__(self):
24-
changelog_map = self.cz.changelog_map
24+
# changelog_map = self.cz.changelog_map
25+
commit_parser = self.cz.commit_parser
2526
changelog_pattern = self.cz.changelog_pattern
26-
if not changelog_map or not changelog_pattern:
27+
28+
if not changelog_pattern or not commit_parser:
2729
out.error(
2830
f"'{self.config.settings['name']}' rule does not support changelog"
2931
)
3032
raise SystemExit(NO_PATTERN_MAP)
31-
32-
pat = re.compile(changelog_pattern)
33+
# pat = re.compile(changelog_pattern)
3334

3435
commits = git.get_commits(start=self.start_rev)
3536
if not commits:
3637
out.error("No commits found")
3738
raise SystemExit(NO_COMMITS_FOUND)
3839

39-
tag_map = {tag.rev: tag.name for tag in git.get_tags()}
40-
41-
entries = OrderedDict()
42-
# The latest commit is not tagged
43-
latest_commit = commits[0]
44-
if latest_commit.rev not in tag_map:
45-
current_key = "Unreleased"
46-
entries[current_key] = OrderedDict(
47-
{value: [] for value in changelog_map.values()}
48-
)
49-
else:
50-
current_key = tag_map[latest_commit.rev]
51-
52-
for commit in commits:
53-
if commit.rev in tag_map:
54-
current_key = tag_map[commit.rev]
55-
entries[current_key] = OrderedDict(
56-
{value: [] for value in changelog_map.values()}
57-
)
58-
59-
matches = pat.match(commit.message)
60-
if not matches:
61-
continue
62-
63-
processed_commit = self.cz.process_commit(commit.message)
64-
for group_name, commit_type in changelog_map.items():
65-
if matches.group(group_name):
66-
entries[current_key][commit_type].append(processed_commit)
67-
break
68-
69-
template_file = pkg_resources.resource_string(
70-
__name__, "../templates/keep_a_changelog_template.j2"
71-
).decode("utf-8")
72-
jinja_template = Template(template_file)
73-
changelog_str = jinja_template.render(entries=entries)
40+
tags = git.get_tags()
41+
if not tags:
42+
tags = []
43+
44+
tree = changelog.generate_tree_from_commits(
45+
commits, tags, commit_parser, changelog_pattern
46+
)
47+
changelog_out = changelog.render_changelog(tree)
48+
# tag_map = {tag.rev: tag.name for tag in git.get_tags()}
49+
50+
# entries = OrderedDict()
51+
# # The latest commit is not tagged
52+
# latest_commit = commits[0]
53+
# if latest_commit.rev not in tag_map:
54+
# current_key = "Unreleased"
55+
# entries[current_key] = OrderedDict(
56+
# {value: [] for value in changelog_map.values()}
57+
# )
58+
# else:
59+
# current_key = tag_map[latest_commit.rev]
60+
61+
# for commit in commits:
62+
# if commit.rev in tag_map:
63+
# current_key = tag_map[commit.rev]
64+
# entries[current_key] = OrderedDict(
65+
# {value: [] for value in changelog_map.values()}
66+
# )
67+
68+
# matches = pat.match(commit.message)
69+
# if not matches:
70+
# continue
71+
72+
# processed_commit = self.cz.process_commit(commit.message)
73+
# for group_name, commit_type in changelog_map.items():
74+
# if matches.group(group_name):
75+
# entries[current_key][commit_type].append(processed_commit)
76+
# break
77+
78+
# template_file = pkg_resources.resource_string(
79+
# __name__, "../templates/keep_a_changelog_template.j2"
80+
# ).decode("utf-8")
81+
# jinja_template = Template(template_file)
82+
# changelog_str = jinja_template.render(entries=entries)
7483
if self.dry_run:
75-
out.write(changelog_str)
84+
out.write(changelog_out)
7685
raise SystemExit(0)
7786

7887
with open(self.file_name, "w") as changelog_file:
79-
changelog_file.write(changelog_str)
88+
changelog_file.write(changelog_out)

‎commitizen/cz/base.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class BaseCommitizen(metaclass=ABCMeta):
2323
("text", ""),
2424
("disabled", "fg:#858585 italic"),
2525
]
26+
commit_parser: Optional[str] = None
2627

2728
def __init__(self, config: BaseConfig):
2829
self.config = config

‎commitizen/cz/conventional_commits/conventional_commits.py‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
import re
3-
from collections import OrderedDict
43
from typing import Any, Dict, List
54

65
from commitizen import defaults
@@ -31,10 +30,8 @@ def parse_subject(text):
3130
class ConventionalCommitsCz(BaseCommitizen):
3231
bump_pattern = defaults.bump_pattern
3332
bump_map = defaults.bump_map
34-
changelog_pattern = (
35-
r"(?P<break>.*\n\nBREAKING CHANGE)|(?P<feat>^feat)|(?P<fix>^fix)"
36-
)
37-
changelog_map = OrderedDict({"break": "breaking", "feat": "feat", "fix": "fix"})
33+
commit_parser = defaults.commit_parser
34+
changelog_pattern = defaults.bump_pattern
3835

3936
def questions(self) -> List[Dict[str, Any]]:
4037
questions: List[Dict[str, Any]] = [
@@ -194,5 +191,5 @@ def process_commit(self, commit: str) -> str:
194191
pat = re.compile(self.schema_pattern())
195192
m = re.match(pat, commit)
196193
if m is None:
197-
return ''
194+
return ""
198195
return m.group(3).strip()

‎commitizen/defaults.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@
3232
)
3333
)
3434
bump_message = "bump: version $current_version → $new_version"
35+
36+
commit_parser = r"^(?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?" # noqa

‎commitizen/git.py‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@
22
from pathlib import Path
33
from tempfile import NamedTemporaryFile
44
from typing import List, Optional
5-
5+
fromtyping_extensionsimportProtocol
66
from commitizen import cmd
77

88

9+
class GitProtocol(Protocol):
10+
rev: str
11+
name: str
12+
13+
914
class GitObject:
10-
def __eq__(self, other):
11-
if not isinstance(other, GitObject):
15+
rev: str
16+
17+
def __eq__(self, other) -> bool:
18+
if not hasattr(other, "rev"):
1219
return False
1320
return self.rev == other.rev
1421

@@ -34,7 +41,7 @@ def __init__(self, name, rev, date):
3441
self.date = date.strip()
3542

3643
def __repr__(self):
37-
return f"{self.name} ({self.rev})"
44+
return f"GitTag('{self.name}', '{self.rev}', '{self.date}')"
3845

3946

4047
def tag(tag: str):
Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
# CHANGELOG
2-
3-
{% for entry_key, entry_value in entries.items() -%}
4-
## {{entry_key}}
5-
{% for type, commits in entry_value.items() -%}
6-
{%- if commits -%}
7-
### {{type}}
8-
{% for commit in commits-%}
9-
- {{commit}}
10-
{% endfor %}
1+
{% for entry in tree %}
2+
3+
## {{ entry.version }} {% if entry.date %}({{ entry.date }}){% endif %}
4+
5+
{% for change_key, changes in entry.changes.items() %}
6+
7+
### {{ change_key|title }}
8+
9+
{% for change in changes %}
10+
{% if change.scope %}
11+
- **{{ change.scope }}**: {{ change.message }}
12+
{% else %}
13+
- {{ change.message }}
1114
{% endif %}
12-
{%- endfor %}
13-
{%- endfor %}
15+
{% endfor %}
16+
{% endfor %}
17+
{% endfor %}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /