Skip to content

Commit

Permalink
Don't let "uncategorized" changes impact the versioning
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin-b authored Feb 13, 2024
2 parents da7e1f6 + 44f8a1b commit e043537
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 16 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- Uncategorized change (usually comments) will no longer impact the release version.
- Typing is using built-in types for `tuple`, `list` and `dict` consistently.

## [2.0.0.dev5] - 2023-01-03
### Changed
Expand Down
5 changes: 2 additions & 3 deletions keepachangelog/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import sys
from typing import List
import argparse

import keepachangelog
Expand All @@ -22,7 +21,7 @@ def _command_release(args: argparse.Namespace) -> None:
print(new_version)


def _parse_args(command_line: List[str]) -> argparse.Namespace:
def _parse_args(command_line: list[str]) -> argparse.Namespace:
class CustomFormatter(
argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
):
Expand Down Expand Up @@ -97,7 +96,7 @@ class CustomFormatter(
return parser.parse_args(command_line)


def main(command_line: List[str] = None) -> None:
def main(command_line: list[str] = None) -> None:
args = _parse_args(command_line)
args.func(args)

Expand Down
16 changes: 8 additions & 8 deletions keepachangelog/_changelog.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import datetime
import re
from typing import Dict, List, Optional, Iterable, Union
from typing import Optional, Iterable, Union

from keepachangelog._versioning import (
actual_version,
Expand All @@ -14,7 +14,7 @@ def is_release(line: str) -> bool:
return line.startswith("## ")


def add_release(changes: Dict[str, dict], line: str) -> dict:
def add_release(changes: dict[str, dict], line: str) -> dict:
release_line = line[3:].lower().strip(" ")
# A release is separated by a space between version and release date
# Release pattern should match lines like: "[0.0.1] - 2020-12-31" or [Unreleased]
Expand Down Expand Up @@ -49,7 +49,7 @@ def is_category(line: str) -> bool:
return line.startswith("### ")


def add_category(release: dict, line: str) -> List[str]:
def add_category(release: dict, line: str) -> list[str]:
category = line[4:].lower().strip(" ")
return release.setdefault(category, [])

Expand All @@ -62,13 +62,13 @@ def is_link(line: str) -> bool:
return link_pattern.fullmatch(line) is not None


def add_information(category: List[str], line: str):
def add_information(category: list[str], line: str):
category.append(line.lstrip(" *-").rstrip(" -"))


def to_dict(
changelog_path: Union[str, Iterable[str]], *, show_unreleased: bool = False
) -> Dict[str, dict]:
) -> dict[str, dict]:
"""
Convert changelog markdown file following keep a changelog format into python dict.
Expand All @@ -84,7 +84,7 @@ def to_dict(
return _to_dict(changelog_path, show_unreleased)


def _to_dict(change_log: Iterable[str], show_unreleased: bool) -> Dict[str, dict]:
def _to_dict(change_log: Iterable[str], show_unreleased: bool) -> dict[str, dict]:
changes = {}
# As URLs can be defined before actual usage, maintain a separate dict
urls = {}
Expand Down Expand Up @@ -127,7 +127,7 @@ def _to_dict(change_log: Iterable[str], show_unreleased: bool) -> Dict[str, dict
return changes


def from_dict(changes: Dict[str, dict]):
def from_dict(changes: dict[str, dict]):
content = """# Changelog
All notable changes to this project will be documented in this file.
Expand Down Expand Up @@ -173,7 +173,7 @@ def from_dict(changes: Dict[str, dict]):
return content


def to_raw_dict(changelog_path: str) -> Dict[str, dict]:
def to_raw_dict(changelog_path: str) -> dict[str, dict]:
changes = {}
# As URLs can be defined before actual usage, maintain a separate dict
urls = {}
Expand Down
10 changes: 5 additions & 5 deletions keepachangelog/_versioning.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import re
from functools import cmp_to_key
from typing import Tuple, Optional, Iterable, List
from typing import Optional, Iterable

initial_semantic_version = {
"major": 0,
Expand All @@ -23,7 +23,7 @@ def contains_breaking_changes(unreleased: dict) -> bool:


def only_contains_bug_fixes(unreleased: dict) -> bool:
return ["fixed"] == list(unreleased)
return {"fixed"} == set(unreleased) - {"uncategorized"}


def bump_major(semantic_version: dict):
Expand Down Expand Up @@ -71,7 +71,7 @@ def _compare(first_version: str, second_version: str) -> int:


def semantic_order(
first_version: Tuple[str, dict], second_version: Tuple[str, dict]
first_version: tuple[str, dict], second_version: tuple[str, dict]
) -> int:
_, semantic_first_version = first_version
_, semantic_second_version = second_version
Expand Down Expand Up @@ -107,12 +107,12 @@ def semantic_order(
return pre_release_difference


def actual_version(changelog: dict) -> Tuple[Optional[str], dict]:
def actual_version(changelog: dict) -> tuple[Optional[str], dict]:
versions = to_sorted_semantic(changelog.keys())
return versions.pop() if versions else (None, initial_semantic_version.copy())


def to_sorted_semantic(versions: Iterable[str]) -> List[Tuple[str, dict]]:
def to_sorted_semantic(versions: Iterable[str]) -> list[tuple[str, dict]]:
"""
Convert a list of string semantic versions to a sorted list of semantic versions.
Note: unreleased is not considered as a semantic version and will thus be removed from the resulting versions.
Expand Down
56 changes: 56 additions & 0 deletions tests/test_changelog_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,34 @@ def patch_digit_changelog(tmpdir):
return changelog_file_path


@pytest.fixture
def patch_digit_changelog_with_extra_info(tmpdir):
changelog_file_path = os.path.join(tmpdir, "PATCH_DIGIT_CHANGELOG.md")
with open(changelog_file_path, "wt") as file:
file.write(
"""# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
A description is worth a thousand patches.
### Fixed
- Enhancement
## [9.9.10] - 2018-05-31
### Fixed
- Enhancement
## [9.9.9] - 2018-05-31
### Fixed
- Enhancement
"""
)
return changelog_file_path


def test_major_release(major_changelog, mock_date):
assert keepachangelog.release(major_changelog) == "2.0.0"
with open(major_changelog, encoding="utf-8") as file:
Expand Down Expand Up @@ -1042,3 +1070,31 @@ def test_custom_release(unstable_changelog, mock_date):
- Enhancement 2 (1.1.0)
"""
)

def test_patch_release_with_description(patch_digit_changelog_with_extra_info, mock_date):
assert keepachangelog.release(patch_digit_changelog_with_extra_info) == "9.9.11"
with open(patch_digit_changelog_with_extra_info) as file:
assert (
file.read()
== """# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [9.9.11] - 2021-03-19
A description is worth a thousand patches.
### Fixed
- Enhancement
## [9.9.10] - 2018-05-31
### Fixed
- Enhancement
## [9.9.9] - 2018-05-31
### Fixed
- Enhancement
"""
)

0 comments on commit e043537

Please sign in to comment.