13
13
1. get commits between versions
14
14
2. filter commits with the current cz rules
15
15
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
17
20
18
- Options :
21
+ Extra :
19
22
- Generate full or partial changelog
23
+ - Include in tree from file all the extra comments added manually
20
24
"""
21
25
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
23
35
24
36
MD_VERSION_RE = r"^##\s(?P<version>[a-zA-Z0-9.+]+)\s?\(?(?P<date>[0-9-]+)?\)?"
25
37
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
+ )
27
41
md_version_c = re .compile (MD_VERSION_RE )
28
42
md_change_type_c = re .compile (MD_CHANGE_TYPE_RE )
29
43
md_message_c = re .compile (MD_MESSAGE_RE )
@@ -50,11 +64,11 @@ def find_version_blocks(filepath: str) -> Generator:
50
64
```
51
65
## 1.2.1 (2019-07-20)
52
66
53
- ## Bug fixes
67
+ ### Fix
54
68
55
69
- username validation not working
56
70
57
- ## Features
71
+ ### Feat
58
72
59
73
- new login system
60
74
@@ -98,6 +112,8 @@ def parse_md_message(md_message: str) -> Dict:
98
112
99
113
100
114
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.
101
117
_change_type_lower = change_type .lower ()
102
118
for match_value , output in CATEGORIES :
103
119
if re .search (match_value , _change_type_lower ):
@@ -107,22 +123,27 @@ def transform_change_type(change_type: str) -> str:
107
123
108
124
109
125
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
+
111
130
change_type = None
112
131
for line in block :
113
132
if line .startswith ("## " ):
133
+ # version identified
114
134
change_type = None
115
135
tree = {** tree , ** parse_md_version (line )}
116
136
elif line .startswith ("### " ):
137
+ # change_type identified
117
138
result = parse_md_change_type (line )
118
139
if not result :
119
140
continue
120
- change_type = transform_change_type ( result .get ("change_type" , "" ))
141
+ change_type = result .get ("change_type" , "" ). lower ( )
121
142
122
143
elif line .startswith ("- " ):
144
+ # message identified
123
145
commit = parse_md_message (line )
124
- commit ["change_type" ] = change_type
125
- tree ["commits" ].append (commit )
146
+ changes [change_type ].append (commit )
126
147
else :
127
148
print ("it's something else: " , line )
128
149
return tree
@@ -131,3 +152,82 @@ def generate_block_tree(block: List[str]) -> Dict:
131
152
def generate_full_tree (blocks : Iterable ) -> Iterable [Dict ]:
132
153
for block in blocks :
133
154
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
0 commit comments