Skip to content

Commit d5f127d

Browse files
committed
fix(changelog): handle custom tag_format in changelog generation
When the tag_format does not follow the allowed schemas patterns then changlog generation fails.
1 parent 947a715 commit d5f127d

File tree

5 files changed

+96
-14
lines changed

5 files changed

+96
-14
lines changed

Diff for: commitizen/changelog.py

+25-8
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,34 @@ def tag_included_in_changelog(
9393
return True
9494

9595

96-
def get_version_tags(scheme: type[BaseVersion], tags: list[GitTag]) -> list[GitTag]:
96+
def get_version_tags(
97+
scheme: type[BaseVersion], tags: list[GitTag], tag_format: str
98+
) -> list[GitTag]:
9799
valid_tags: list[GitTag] = []
100+
TAG_FORMAT_REGEXS = {
101+
"$version": str(scheme.parser.pattern),
102+
"$major": r"(?P<major>\d+)",
103+
"$minor": r"(?P<minor>\d+)",
104+
"$patch": r"(?P<patch>\d+)",
105+
"$prerelease": r"(?P<prerelease>\w+\d+)?",
106+
"$devrelease": r"(?P<devrelease>\.dev\d+)?",
107+
"${version}": str(scheme.parser.pattern),
108+
"${major}": r"(?P<major>\d+)",
109+
"${minor}": r"(?P<minor>\d+)",
110+
"${patch}": r"(?P<patch>\d+)",
111+
"${prerelease}": r"(?P<prerelease>\w+\d+)?",
112+
"${devrelease}": r"(?P<devrelease>\.dev\d+)?",
113+
}
114+
tag_format_regex = tag_format
115+
for pattern, regex in TAG_FORMAT_REGEXS.items():
116+
tag_format_regex = tag_format_regex.replace(pattern, regex)
98117
for tag in tags:
99-
try:
100-
scheme(tag.name)
101-
except InvalidVersion:
102-
out.warn(f"InvalidVersion {tag}")
103-
else:
118+
if re.match(tag_format_regex, tag.name):
104119
valid_tags.append(tag)
105-
120+
else:
121+
out.warn(
122+
f"InvalidVersion {tag.name} doesn't match configured tag format {tag_format}"
123+
)
106124
return valid_tags
107125

108126

@@ -351,7 +369,6 @@ def get_oldest_and_newest_rev(
351369
oldest, newest = version.split("..")
352370
except ValueError:
353371
newest = version
354-
355372
newest_tag = normalize_tag(newest, tag_format=tag_format, scheme=scheme)
356373

357374
oldest_tag = None

Diff for: commitizen/changelog_formats/base.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,30 @@ def __init__(self, config: BaseConfig):
2424
# Constructor needs to be redefined because `Protocol` prevent instantiation by default
2525
# See: https://bugs.python.org/issue44807
2626
self.config = config
27+
self.tag_format = self.config.settings.get("tag_format")
2728

2829
@property
2930
def version_parser(self) -> Pattern:
30-
return get_version_scheme(self.config).parser
31+
version_regex = get_version_scheme(self.config).parser.pattern
32+
if self.tag_format != "$version":
33+
TAG_FORMAT_REGEXS = {
34+
"$version": version_regex,
35+
"$major": "(?P<major>\d+)",
36+
"$minor": "(?P<minor>\d+)",
37+
"$patch": "(?P<patch>\d+)",
38+
"$prerelease": "(?P<prerelease>\w+\d+)?",
39+
"$devrelease": "(?P<devrelease>\.dev\d+)?",
40+
"${version}": version_regex,
41+
"${major}": "(?P<major>\d+)",
42+
"${minor}": "(?P<minor>\d+)",
43+
"${patch}": "(?P<patch>\d+)",
44+
"${prerelease}": "(?P<prerelease>\w+\d+)?",
45+
"${devrelease}": "(?P<devrelease>\.dev\d+)?",
46+
}
47+
version_regex = self.tag_format
48+
for pattern, regex in TAG_FORMAT_REGEXS.items():
49+
version_regex = version_regex.replace(pattern, regex)
50+
return rf"{version_regex}"
3151

3252
def get_metadata(self, filepath: str) -> Metadata:
3353
if not os.path.isfile(filepath):

Diff for: commitizen/commands/changelog.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,10 @@ def __call__(self):
169169
# Don't continue if no `file_name` specified.
170170
assert self.file_name
171171

172-
tags = changelog.get_version_tags(self.scheme, git.get_tags()) or []
173-
172+
tags = (
173+
changelog.get_version_tags(self.scheme, git.get_tags(), self.tag_format)
174+
or []
175+
)
174176
end_rev = ""
175177
if self.incremental:
176178
changelog_meta = self.changelog_format.get_metadata(self.file_name)
@@ -183,21 +185,18 @@ def __call__(self):
183185
start_rev = self._find_incremental_rev(
184186
strip_local_version(latest_tag_version), tags
185187
)
186-
187188
if self.rev_range:
188189
start_rev, end_rev = changelog.get_oldest_and_newest_rev(
189190
tags,
190191
version=self.rev_range,
191192
tag_format=self.tag_format,
192193
scheme=self.scheme,
193194
)
194-
195195
commits = git.get_commits(start=start_rev, end=end_rev, args="--topo-order")
196196
if not commits and (
197197
self.current_version is None or not self.current_version.is_prerelease
198198
):
199199
raise NoCommitsFoundError("No commits found")
200-
201200
tree = changelog.generate_tree_from_commits(
202201
commits,
203202
tags,

Diff for: commitizen/providers/scm_provider.py

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ class ScmProvider(VersionProvider):
3131
"$patch": r"(?P<patch>\d+)",
3232
"$prerelease": r"(?P<prerelease>\w+\d+)?",
3333
"$devrelease": r"(?P<devrelease>\.dev\d+)?",
34+
"${version}": r"(?P<version>.+)",
35+
"${major}": r"(?P<major>\d+)",
36+
"${minor}": r"(?P<minor>\d+)",
37+
"${patch}": r"(?P<patch>\d+)",
38+
"${prerelease}": r"(?P<prerelease>\w+\d+)?",
39+
"${devrelease}": r"(?P<devrelease>\.dev\d+)?",
3440
}
3541

3642
def _tag_format_matcher(self) -> Callable[[str], VersionProtocol | None]:

Diff for: docs/tutorials/monorepo_guidance.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Configuring commitizen in a monorepo
2+
3+
This tutorial assumes the monorepo layout is designed with multiple components that can be released independently of each other,
4+
some suggested layouts:
5+
6+
```
7+
library-a
8+
.cz.toml
9+
library-b
10+
.cz.toml
11+
```
12+
13+
```
14+
src
15+
library-b
16+
.cz.toml
17+
library-z
18+
.cz.toml
19+
```
20+
21+
Each component will have its own changelog, commits will need to use scopes so only relevant commits are included in the
22+
appropriate change log for a given component. Example config and commit for `library-b`
23+
24+
```toml
25+
[tool.commitizen]
26+
name = "cz_customize"
27+
version = "0.0.0"
28+
tag_format = "${version}-library-b" # the component name can be a prefix or suffix with or without a separator
29+
update_changelog_on_bump = true
30+
31+
[tool.commitizen.customize]
32+
changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" #the pattern on types can be a wild card or any types you wish to include
33+
```
34+
35+
example commit message for the above
36+
37+
`fix:(library-b) Some awesome message`
38+
39+
If the above is followed and the `cz bump --changelog` is run in the directory containing the component the changelog
40+
should be generated in the same directory with only commits scoped to the component.

0 commit comments

Comments
 (0)