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 d5f127d

Browse files
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

‎commitizen/changelog.py‎

Lines changed: 25 additions & 8 deletions
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

‎commitizen/changelog_formats/base.py‎

Lines changed: 21 additions & 1 deletion
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):

‎commitizen/commands/changelog.py‎

Lines changed: 4 additions & 5 deletions
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,

‎commitizen/providers/scm_provider.py‎

Lines changed: 6 additions & 0 deletions
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]:

‎docs/tutorials/monorepo_guidance.md‎

Lines changed: 40 additions & 0 deletions
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)

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