26
26
"""
27
27
from __future__ import annotations
28
28
29
- import os
30
29
import re
31
30
from collections import OrderedDict , defaultdict
31
+ from dataclasses import dataclass
32
32
from datetime import date
33
- from typing import TYPE_CHECKING , Callable , Iterable , cast
33
+ from typing import TYPE_CHECKING , Callable , Iterable
34
34
35
35
from jinja2 import (
36
36
BaseLoader ,
37
37
ChoiceLoader ,
38
38
Environment ,
39
39
FileSystemLoader ,
40
- PackageLoader ,
41
40
Template ,
42
41
)
43
42
44
43
from commitizen import out
45
44
from commitizen .bump import normalize_tag
46
- from commitizen .defaults import encoding
47
45
from commitizen .exceptions import InvalidConfigurationError , NoCommitsFoundError
48
46
from commitizen .git import GitCommit , GitTag
49
47
from commitizen .version_schemes import (
50
48
DEFAULT_SCHEME ,
51
49
BaseVersion ,
52
50
InvalidVersion ,
53
- Pep440 ,
54
51
)
55
52
56
53
if TYPE_CHECKING :
57
54
from commitizen .version_schemes import VersionScheme
58
55
59
- DEFAULT_TEMPLATE = "CHANGELOG.md.j2"
56
+
57
+ @dataclass
58
+ class Metadata :
59
+ """
60
+ Metadata extracted from the changelog produced by a plugin
61
+ """
62
+
63
+ unreleased_start : int | None = None
64
+ unreleased_end : int | None = None
65
+ latest_version : str | None = None
66
+ latest_version_position : int | None = None
60
67
61
68
62
69
def get_commit_tag (commit : GitCommit , tags : list [GitTag ]) -> GitTag | None :
@@ -196,100 +203,31 @@ def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterab
196
203
return sorted_tree
197
204
198
205
199
- def get_changelog_template (
200
- loader : BaseLoader | None = None , template : str | None = None
201
- ) -> Template :
206
+ def get_changelog_template (loader : BaseLoader , template : str ) -> Template :
202
207
loader = ChoiceLoader (
203
208
[
204
209
FileSystemLoader ("." ),
205
- loader or PackageLoader ( "commitizen" , "templates" ) ,
210
+ loader ,
206
211
]
207
212
)
208
213
env = Environment (loader = loader , trim_blocks = True )
209
- return env .get_template (template or DEFAULT_TEMPLATE )
214
+ return env .get_template (template )
210
215
211
216
212
217
def render_changelog (
213
218
tree : Iterable ,
214
- loader : BaseLoader | None = None ,
215
- template : str | None = None ,
219
+ loader : BaseLoader ,
220
+ template : str ,
216
221
** kwargs ,
217
222
) -> str :
218
- jinja_template = get_changelog_template (loader , template or DEFAULT_TEMPLATE )
223
+ jinja_template = get_changelog_template (loader , template )
219
224
changelog : str = jinja_template .render (tree = tree , ** kwargs )
220
225
return changelog
221
226
222
227
223
- def parse_version_from_markdown (
224
- value : str , scheme : VersionScheme = Pep440
225
- ) -> str | None :
226
- if not value .startswith ("#" ):
227
- return None
228
- m = scheme .parser .search (value )
229
- if not m :
230
- return None
231
- return cast (str , m .group ("version" ))
232
-
233
-
234
- def parse_title_type_of_line (value : str ) -> str | None :
235
- md_title_parser = r"^(?P<title>#+)"
236
- m = re .search (md_title_parser , value )
237
- if not m :
238
- return None
239
- return m .groupdict ().get ("title" )
240
-
241
-
242
- def get_metadata (
243
- filepath : str , scheme : VersionScheme = Pep440 , encoding : str = encoding
244
- ) -> dict :
245
- unreleased_start : int | None = None
246
- unreleased_end : int | None = None
247
- unreleased_title : str | None = None
248
- latest_version : str | None = None
249
- latest_version_position : int | None = None
250
- if not os .path .isfile (filepath ):
251
- return {
252
- "unreleased_start" : None ,
253
- "unreleased_end" : None ,
254
- "latest_version" : None ,
255
- "latest_version_position" : None ,
256
- }
257
-
258
- with open (filepath , encoding = encoding ) as changelog_file :
259
- for index , line in enumerate (changelog_file ):
260
- line = line .strip ().lower ()
261
-
262
- unreleased : str | None = None
263
- if "unreleased" in line :
264
- unreleased = parse_title_type_of_line (line )
265
- # Try to find beginning and end lines of the unreleased block
266
- if unreleased :
267
- unreleased_start = index
268
- unreleased_title = unreleased
269
- continue
270
- elif (
271
- isinstance (unreleased_title , str )
272
- and parse_title_type_of_line (line ) == unreleased_title
273
- ):
274
- unreleased_end = index
275
-
276
- # Try to find the latest release done
277
- version = parse_version_from_markdown (line , scheme )
278
- if version :
279
- latest_version = version
280
- latest_version_position = index
281
- break # there's no need for more info
282
- if unreleased_start is not None and unreleased_end is None :
283
- unreleased_end = index
284
- return {
285
- "unreleased_start" : unreleased_start ,
286
- "unreleased_end" : unreleased_end ,
287
- "latest_version" : latest_version ,
288
- "latest_version_position" : latest_version_position ,
289
- }
290
-
291
-
292
- def incremental_build (new_content : str , lines : list [str ], metadata : dict ) -> list [str ]:
228
+ def incremental_build (
229
+ new_content : str , lines : list [str ], metadata : Metadata
230
+ ) -> list [str ]:
293
231
"""Takes the original lines and updates with new_content.
294
232
295
233
The metadata governs how to remove the old unreleased section and where to place the
@@ -303,9 +241,9 @@ def incremental_build(new_content: str, lines: list[str], metadata: dict) -> lis
303
241
Returns:
304
242
Updated lines
305
243
"""
306
- unreleased_start = metadata .get ( " unreleased_start" )
307
- unreleased_end = metadata .get ( " unreleased_end" )
308
- latest_version_position = metadata .get ( " latest_version_position" )
244
+ unreleased_start = metadata .unreleased_start
245
+ unreleased_end = metadata .unreleased_end
246
+ latest_version_position = metadata .latest_version_position
309
247
skip = False
310
248
output_lines : list [str ] = []
311
249
for index , line in enumerate (lines ):
0 commit comments