Skip to content

Commit eae6463

Browse files
committed
fix: only use version tags when generating a changelog
This commit ensures that the tags used to generate the changelog are all correctly parsed by Version() This fixes two bugs: - The header of the changelog could be a non-version tag - The rev-range computed when running could use a non-version tag as a lower bound
1 parent 17b2f02 commit eae6463

File tree

7 files changed

+251
-7
lines changed

7 files changed

+251
-7
lines changed

commitizen/changelog.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ def get_version(tag: GitTag) -> Version | None:
6363
return version
6464

6565

66+
def get_version_tags(tags: List[GitTag]) -> List[GitTag]:
67+
return list(filter(get_version, tags))
68+
69+
6670
def tag_included_in_changelog(
6771
tag: GitTag, used_tags: list, merge_prerelease: bool
6872
) -> bool:

commitizen/commands/changelog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def __call__(self):
139139
# Don't continue if no `file_name` specified.
140140
assert self.file_name
141141

142-
tags = git.get_tags()
142+
tags = changelog.get_version_tags(git.get_tags())
143143
if not tags:
144144
tags = []
145145

commitizen/git.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,22 @@ def tag(tag: str, annotated: bool = False, signed: bool = False) -> cmd.Command:
9292
return c
9393

9494

95-
def commit(message: str, args: str = "") -> cmd.Command:
95+
def commit(
96+
message: str, args: str = "", committer_date: Optional[str] = None
97+
) -> cmd.Command:
9698
f = NamedTemporaryFile("wb", delete=False)
9799
f.write(message.encode("utf-8"))
98100
f.close()
99-
c = cmd.run(f"git commit {args} -F {f.name}")
101+
102+
command = f"git commit {args} -F {f.name}"
103+
104+
if committer_date and os.name == "nt": # pragma: no cover
105+
# Using `cmd /v /c "{command}"` sets environment variables only for that command
106+
command = f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"'
107+
elif committer_date:
108+
command = f"GIT_COMMITTER_DATE={committer_date} {command}"
109+
110+
c = cmd.run(command)
100111
os.unlink(f.name)
101112
return c
102113

poetry.lock

Lines changed: 128 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ types-termcolor = "^0.1.1"
6969
# documentation
7070
mkdocs = "^1.4.2"
7171
mkdocs-material = "^9.1.6"
72+
deprecated = "^1.2.13"
73+
types-deprecated = "^1.2.9.2"
74+
types-python-dateutil = "^2.8.19.13"
7275

7376

7477
[tool.poetry.scripts]

tests/commands/test_changelog_command.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66
from pytest_mock import MockFixture
7+
from dateutil import relativedelta
78

89
from commitizen import cli, git
910
from commitizen.commands.changelog import Changelog
@@ -17,6 +18,7 @@
1718
from tests.utils import (
1819
create_branch,
1920
create_file_and_commit,
21+
create_tag,
2022
get_current_branch,
2123
merge_branch,
2224
switch_branch,
@@ -1267,3 +1269,88 @@ def test_changelog_prerelease_rev_with_use_version_type_semver(
12671269
out, _ = capsys.readouterr()
12681270

12691271
file_regression.check(out, extension=".second-prerelease.md")
1272+
1273+
1274+
@pytest.mark.usefixtures("tmp_commitizen_project")
1275+
def test_changelog_uses_version_tags_for_header(mocker: MockFixture, config):
1276+
"""Tests that changelog headers always use version tags even if there are non-version tags
1277+
1278+
This tests a scenario fixed in this commit:
1279+
The first header was using a non-version tag and outputting "## 0-not-a-version" instead of "## 1.0.0"""
1280+
create_file_and_commit("feat: commit in 1.0.0")
1281+
create_tag("0-not-a-version")
1282+
create_tag("1.0.0")
1283+
create_tag("also-not-a-version")
1284+
1285+
write_patch = mocker.patch("commitizen.commands.changelog.out.write")
1286+
1287+
changelog = Changelog(
1288+
config, {"dry_run": True, "incremental": True, "unreleased_version": None}
1289+
)
1290+
1291+
with pytest.raises(DryRunExit):
1292+
changelog()
1293+
1294+
assert write_patch.call_args[0][0].startswith("## 1.0.0")
1295+
1296+
1297+
@pytest.mark.usefixtures("tmp_commitizen_project")
1298+
def test_changelog_from_current_version_tag_with_nonversion_tag(
1299+
mocker: MockFixture, config
1300+
):
1301+
"""Tests that changelog generation for a single version works even if
1302+
there is a non-version tag in the list of tags
1303+
1304+
This tests a scenario which is fixed in this commit:
1305+
You have a commit in between two versions (1.0.0..2.0.0) which is tagged with a non-version tag (not-a-version).
1306+
In this case commitizen should disregard the non-version tag when determining the rev-range & generating the changelog.
1307+
"""
1308+
create_file_and_commit(
1309+
"feat: initial commit",
1310+
committer_date=(
1311+
datetime.now() - relativedelta.relativedelta(seconds=3)
1312+
).isoformat(),
1313+
)
1314+
create_tag("1.0.0")
1315+
1316+
create_file_and_commit(
1317+
"feat: commit 1",
1318+
committer_date=(
1319+
datetime.now() - relativedelta.relativedelta(seconds=2)
1320+
).isoformat(),
1321+
)
1322+
create_tag("1-not-a-version")
1323+
1324+
create_file_and_commit(
1325+
"feat: commit 2",
1326+
committer_date=(
1327+
datetime.now() - relativedelta.relativedelta(seconds=1)
1328+
).isoformat(),
1329+
)
1330+
1331+
create_file_and_commit("bump: version 1.0.0 → 2.0.0")
1332+
create_tag("2.0.0")
1333+
1334+
mocker.patch("commitizen.git.GitTag.date", "2022-02-13")
1335+
write_patch = mocker.patch("commitizen.commands.changelog.out.write")
1336+
1337+
changelog = Changelog(
1338+
config,
1339+
{
1340+
"dry_run": True,
1341+
"incremental": False,
1342+
"unreleased_version": None,
1343+
"rev_range": "2.0.0",
1344+
},
1345+
)
1346+
1347+
with pytest.raises(DryRunExit):
1348+
changelog()
1349+
1350+
full_changelog = "\
1351+
## 2.0.0 (2022-02-13)\n\n\
1352+
### Feat\n\n\
1353+
- commit 2\n\
1354+
- commit 1\n"
1355+
1356+
write_patch.assert_called_with(full_changelog)

0 commit comments

Comments
 (0)