Skip to content

feat(changelog): add merge-prerelease flag #703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ jobs:
shell: bash
- name: Upload coverage to Codecov
if: runner.os == 'Linux'
uses: codecov/codecov-action@v1.0.3
uses: codecov/codecov-action@v3
with:
token: ${{secrets.CODECOV_TOKEN}}
file: ./coverage.xml
33 changes: 30 additions & 3 deletions commitizen/changelog.py
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@
from typing import Callable, Dict, Iterable, List, Optional, Tuple

from jinja2 import Environment, PackageLoader
from packaging.version import InvalidVersion, Version

from commitizen import defaults
from commitizen.bump import normalize_tag
@@ -43,6 +44,31 @@ def get_commit_tag(commit: GitCommit, tags: List[GitTag]) -> Optional[GitTag]:
return next((tag for tag in tags if tag.rev == commit.rev), None)


def get_version(tag: GitTag) -> Optional[Version]:
version = None
try:
version = Version(tag.name)
except InvalidVersion:
pass
return version


def tag_included_in_changelog(
tag: GitTag, used_tags: List, merge_prerelease: bool
) -> bool:
if tag in used_tags:
return False

version = get_version(tag)
if version is None:
return False

if merge_prerelease and version.is_prerelease:
return False

return True


def generate_tree_from_commits(
commits: List[GitCommit],
tags: List[GitTag],
@@ -51,6 +77,7 @@ def generate_tree_from_commits(
unreleased_version: Optional[str] = None,
change_type_map: Optional[Dict[str, str]] = None,
changelog_message_builder_hook: Optional[Callable] = None,
merge_prerelease: bool = False,
) -> Iterable[Dict]:
pat = re.compile(changelog_pattern)
map_pat = re.compile(commit_parser, re.MULTILINE)
@@ -73,15 +100,15 @@ def generate_tree_from_commits(
for commit in commits:
commit_tag = get_commit_tag(commit, tags)

if commit_tag is not None and commit_tag not in used_tags:
if commit_tag is not None and tag_included_in_changelog(
commit_tag, used_tags, merge_prerelease
):
used_tags.append(commit_tag)
yield {
"version": current_tag_name,
"date": current_tag_date,
"changes": changes,
}
# TODO: Check if tag matches the version pattern, otherwise skip it.
# This in order to prevent tags that are not versions.
current_tag_name = commit_tag.name
current_tag_date = commit_tag.date
changes = defaultdict(list)
11 changes: 10 additions & 1 deletion commitizen/cli.py
Original file line number Diff line number Diff line change
@@ -248,10 +248,19 @@
"name": "--start-rev",
"default": None,
"help": (
"start rev of the changelog."
"start rev of the changelog. "
"If not set, it will generate changelog from the start"
),
},
{
"name": "--merge-prerelease",
"action": "store_true",
"default": False,
"help": (
"collect all changes from prereleases into next non-prerelease. "
"If not set, it will include prereleases in the changelog"
),
},
],
},
{
5 changes: 5 additions & 0 deletions commitizen/commands/changelog.py
Original file line number Diff line number Diff line change
@@ -49,6 +49,9 @@ def __init__(self, config: BaseConfig, args):
self.tag_format = args.get("tag_format") or self.config.settings.get(
"tag_format"
)
self.merge_prerelease = args.get(
"merge_prerelease"
) or self.config.settings.get("changelog_merge_prerelease")

def _find_incremental_rev(self, latest_version: str, tags: List[GitTag]) -> str:
"""Try to find the 'start_rev'.
@@ -110,6 +113,7 @@ def __call__(self):
changelog_message_builder_hook: Optional[
Callable
] = self.cz.changelog_message_builder_hook
merge_prerelease = self.merge_prerelease

if not changelog_pattern or not commit_parser:
raise NoPatternMapError(
@@ -156,6 +160,7 @@ def __call__(self):
unreleased_version,
change_type_map=change_type_map,
changelog_message_builder_hook=changelog_message_builder_hook,
merge_prerelease=merge_prerelease,
)
if self.change_type_order:
tree = changelog.order_changelog_tree(tree, self.change_type_order)
2 changes: 2 additions & 0 deletions commitizen/defaults.py
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ class Settings(TypedDict, total=False):
changelog_file: str
changelog_incremental: bool
changelog_start_rev: Optional[str]
changelog_merge_prerelease: bool
update_changelog_on_bump: bool
use_shortcuts: bool
style: Optional[List[Tuple[str, str]]]
@@ -67,6 +68,7 @@ class Settings(TypedDict, total=False):
"changelog_file": "CHANGELOG.md",
"changelog_incremental": False,
"changelog_start_rev": None,
"changelog_merge_prerelease": False,
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
20 changes: 19 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
@@ -21,7 +21,9 @@ optional arguments:
set the value for the new version (use the tag value), instead of using unreleased
--incremental generates changelog from last created version, useful if the changelog has been manually modified
--start-rev START_REV
start rev of the changelog.If not set, it will generate changelog from the start
start rev of the changelog. If not set, it will generate changelog from the start
--merge-prerelease
collect all changes from prereleases into next non-prerelease. If not set, it will include prereleases in the changelog
```

### Examples
@@ -161,6 +163,22 @@ cz changelog --start-rev="v0.2.0"
changelog_start_rev = "v0.2.0"
```

### merge-prerelease

This flag can be set in the `toml` file with the key `changelog_merge_prerelease` under `tools.commitizen`

Collects changes from prereleases into the next non-prerelease. This means that if you have a prerelease version, and then a normal release, the changelog will show the prerelease changes as part of the changes of the normal release. If not set, it will include prereleases in the changelog.

```bash
cz changelog --merge-prerelease
```

```toml
[tools.commitizen]
# ...
changelog_merge_prerelease = true
```

## Hooks

Supported hook methods:
1 change: 1 addition & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
| `changelog_file` | `str` | `CHANGELOG.md` | filename of exported changelog |
| `changelog_incremental` | `bool` | `false` | Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true` |
| `changelog_start_rev` | `str` | `None` | Start from a given git rev to generate the changelog |
| `changelog_merge_prerelease` | `bool` | `false` | Collect all changes of prerelease versions into the next non-prerelease version when creating the changelog. |
| `style` | `list` | see above | Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] |
| `customize` | `dict` | `None` | **This is only supported when config through `toml`.** Custom rules for committing and bumping. [See more][customization] |
| `use_shortcuts` | `bool` | `false` | If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [See more][shortcuts] |
20 changes: 2 additions & 18 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -77,7 +77,6 @@ ipython = "^7.2"
pytest = "^7.2.0"
pytest-cov = "^4.0"
pytest-mock = "^3.10"
codecov = "^2.0"
pytest-regressions = "^2.4.0"
pytest-freezer = "^0.4.6"
# code formatter
2 changes: 1 addition & 1 deletion scripts/test
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ set -e
export PREFIX='poetry run python -m '
export REGEX='^(?![.]|venv).*'

${PREFIX}pytest -n 3 --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen tests/
${PREFIX}pytest -n 3 --dist=loadfile --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen tests/
${PREFIX}black commitizen tests --check
${PREFIX}isort --check-only commitizen tests
${PREFIX}flake8 commitizen/ tests/
107 changes: 107 additions & 0 deletions tests/commands/test_changelog_command.py
Original file line number Diff line number Diff line change
@@ -538,6 +538,40 @@ def test_changelog_config_flag_increment(
file_regression.check(out, extension=".md")


@pytest.mark.parametrize("test_input", ["rc", "alpha", "beta"])
@pytest.mark.usefixtures("tmp_commitizen_project")
def test_changelog_config_flag_merge_prerelease(
mocker: MockFixture, changelog_path, config_path, file_regression, test_input
):
with open(config_path, "a") as f:
f.write("changelog_merge_prerelease = true\n")

create_file_and_commit("irrelevant commit")
mocker.patch("commitizen.git.GitTag.date", "1970-01-01")
git.tag("1.0.0")

create_file_and_commit("feat: add new output")
create_file_and_commit("fix: output glitch")

testargs = ["cz", "bump", "--prerelease", test_input, "--yes"]
mocker.patch.object(sys, "argv", testargs)
cli.main()

create_file_and_commit("fix: mama gotta work")
create_file_and_commit("feat: add more stuff")
create_file_and_commit("Merge into master")

testargs = ["cz", "changelog"]

mocker.patch.object(sys, "argv", testargs)
cli.main()

with open(changelog_path, "r") as f:
out = f.read()

file_regression.check(out, extension=".md")


@pytest.mark.usefixtures("tmp_commitizen_project")
def test_changelog_config_start_rev_option(
mocker: MockFixture, capsys, config_path, file_regression
@@ -626,6 +660,79 @@ def test_changelog_incremental_with_release_candidate_version(
file_regression.check(out, extension=".md")


@pytest.mark.parametrize("test_input", ["rc", "alpha", "beta"])
@pytest.mark.usefixtures("tmp_commitizen_project")
def test_changelog_release_candidate_version_with_merge_prerelease(
mocker: MockFixture, changelog_path, file_regression, test_input
):
"""Fix #357"""
with open(changelog_path, "w") as f:
f.write(KEEP_A_CHANGELOG)
create_file_and_commit("irrelevant commit")
mocker.patch("commitizen.git.GitTag.date", "1970-01-01")
git.tag("1.0.0")

create_file_and_commit("feat: add new output")
create_file_and_commit("fix: output glitch")

testargs = ["cz", "bump", "--prerelease", test_input, "--yes"]
mocker.patch.object(sys, "argv", testargs)
cli.main()

create_file_and_commit("fix: mama gotta work")
create_file_and_commit("feat: add more stuff")
create_file_and_commit("Merge into master")

testargs = ["cz", "changelog", "--merge-prerelease"]

mocker.patch.object(sys, "argv", testargs)
cli.main()

with open(changelog_path, "r") as f:
out = f.read()

file_regression.check(out, extension=".md")


@pytest.mark.parametrize("test_input", ["rc", "alpha", "beta"])
@pytest.mark.usefixtures("tmp_commitizen_project")
def test_changelog_incremental_with_merge_prerelease(
mocker: MockFixture, changelog_path, file_regression, test_input
):
"""Fix #357"""
with open(changelog_path, "w") as f:
f.write(KEEP_A_CHANGELOG)
create_file_and_commit("irrelevant commit")
mocker.patch("commitizen.git.GitTag.date", "1970-01-01")
git.tag("1.0.0")

create_file_and_commit("feat: add new output")

testargs = ["cz", "bump", "--prerelease", test_input, "--yes", "--changelog"]
mocker.patch.object(sys, "argv", testargs)
cli.main()

create_file_and_commit("fix: output glitch")

testargs = ["cz", "bump", "--prerelease", test_input, "--yes"]
mocker.patch.object(sys, "argv", testargs)
cli.main()

create_file_and_commit("fix: mama gotta work")
create_file_and_commit("feat: add more stuff")
create_file_and_commit("Merge into master")

testargs = ["cz", "changelog", "--merge-prerelease", "--incremental"]

mocker.patch.object(sys, "argv", testargs)
cli.main()

with open(changelog_path, "r") as f:
out = f.read()

file_regression.check(out, extension=".md")


@pytest.mark.usefixtures("tmp_commitizen_project")
def test_changelog_with_filename_as_empty_string(
mocker: MockFixture, changelog_path, config_path
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Unreleased

### Feat

- add more stuff
- add new output

### Fix

- mama gotta work
- output glitch

## 1.0.0 (1970-01-01)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Unreleased

### Feat

- add more stuff
- add new output

### Fix

- mama gotta work
- output glitch

## 1.0.0 (1970-01-01)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Unreleased

### Feat

- add more stuff
- add new output

### Fix

- mama gotta work
- output glitch

## 1.0.0 (1970-01-01)
Loading