diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 7bb9007cdc..d26ad22681 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -66,13 +66,13 @@ def get_commit_tag(commit: GitCommit, tags: List[GitTag]) -> Optional[GitTag]: def generate_tree_from_commits( - commits: List[GitCommit], - tags: List[GitTag], - commit_parser: str, - changelog_pattern: str = defaults.bump_pattern, - unreleased_version: Optional[str] = None, - change_type_map: Optional[Dict[str, str]] = None, - changelog_message_builder_hook: Optional[Callable] = None, + commits: List[GitCommit], + tags: List[GitTag], + commit_parser: str, + changelog_pattern: str = defaults.bump_pattern, + unreleased_version: Optional[str] = None, + change_type_map: Optional[Dict[str, str]] = None, + changelog_message_builder_hook: Optional[Callable] = None, ) -> Iterable[Dict]: pat = re.compile(changelog_pattern) map_pat = re.compile(commit_parser, re.MULTILINE) @@ -169,13 +169,25 @@ def render_changelog(tree: Iterable) -> str: return changelog -def parse_version_from_markdown(value: str) -> Optional[str]: +def parse_version_from_markdown(value: str, pr_filter: str) -> Optional[str]: + """ + :param pr_filter: pass to only find a certain type of version, or None for any + version type. Example: when there is 1.0.0 and 2.0.0b1 and you pass "" 1.0.0 will be returned + as it is the last release. + """ if not value.startswith("#"): return None + if not value.__contains__(pr_filter): + return None m = re.search(defaults.version_parser, value) if not m: return None - return m.groupdict().get("version") + version = m.groupdict().get("version") + # when pr_filter is provided but empty + is_stable = re.match(defaults.stable_parser, version) + if not is_stable and pr_filter == "": + return None # because the version is a pre-release but a "full" release is expected + return version def parse_title_type_of_line(value: str) -> Optional[str]: @@ -186,7 +198,7 @@ def parse_title_type_of_line(value: str) -> Optional[str]: return m.groupdict().get("title") -def get_metadata(filepath: str) -> Dict: +def get_metadata(filepath: str, pr_filter: str) -> Dict: unreleased_start: Optional[int] = None unreleased_end: Optional[int] = None unreleased_title: Optional[str] = None @@ -213,13 +225,13 @@ def get_metadata(filepath: str) -> Dict: unreleased_title = unreleased continue elif ( - isinstance(unreleased_title, str) - and parse_title_type_of_line(line) == unreleased_title + isinstance(unreleased_title, str) + and parse_title_type_of_line(line) == unreleased_title ): unreleased_end = index # Try to find the latest release done - version = parse_version_from_markdown(line) + version = parse_version_from_markdown(line, pr_filter) if version: latest_version = version latest_version_position = index @@ -259,10 +271,10 @@ def incremental_build(new_content: str, lines: List, metadata: Dict) -> List: elif index == unreleased_end: skip = False if ( - latest_version_position is None - or isinstance(latest_version_position, int) - and isinstance(unreleased_end, int) - and latest_version_position > unreleased_end + latest_version_position is None + or isinstance(latest_version_position, int) + and isinstance(unreleased_end, int) + and latest_version_position > unreleased_end ): continue @@ -270,10 +282,9 @@ def incremental_build(new_content: str, lines: List, metadata: Dict) -> List: continue if ( - isinstance(latest_version_position, int) - and index == latest_version_position + isinstance(latest_version_position, int) + and index == latest_version_position ): - output_lines.append(new_content) output_lines.append("\n") diff --git a/commitizen/cli.py b/commitizen/cli.py index d582a27a18..2530df103b 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -109,6 +109,19 @@ "action": "store_true", "help": "accept automatically questions done", }, + { + "name": "--separate-pre-changelog", + "action": "store_true", + "default": False, + "help": "use a separate changelog for each type of prerelease" + }, + { + "name": "--separate-pre-releases", + "action": "store_true", + "default": False, + "help": "changes in the changelog will always relate to the latest " + "release of the same pre-release" + }, { "name": "--tag-format", "help": ( @@ -260,7 +273,7 @@ "exclusive_group": "group1", }, ], - }, + } ], }, } diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 535632dfc0..2344e8d916 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -22,7 +22,7 @@ def similar(a, b): class Changelog: """Generate a changelog based on the commit history.""" - def __init__(self, config: BaseConfig, args): + def __init__(self, config: BaseConfig, args: dict): if not git.is_git_project(): raise NotAGitProjectError() @@ -40,6 +40,12 @@ def __init__(self, config: BaseConfig, args): ) self.dry_run = args["dry_run"] self.unreleased_version = args["unreleased_version"] + self.separate_pre_releases = args["separate_pre_releases"] + self.separate_pre_changelog = args["separate_pre_changelog"] + self.pre_release = args["prerelease"] + if self.separate_pre_changelog: + self.separate_pre_releases = True + self.file_name = f"{self.file_name}-${self.pre_release}" self.change_type_map = ( self.config.settings.get("change_type_map") or self.cz.change_type_map ) @@ -94,7 +100,10 @@ def __call__(self): tags = [] if self.incremental: - changelog_meta = changelog.get_metadata(self.file_name) + pr_filter = None + if self.separate_pre_releases: + pr_filter = self.pre_release + changelog_meta = changelog.get_metadata(self.file_name, pr_filter) latest_version = changelog_meta.get("latest_version") if latest_version: start_rev = self._find_incremental_rev(latest_version, tags) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index e460bbd6f7..d269c06b96 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -45,3 +45,4 @@ commit_parser = r"^(?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?:\s(?P.*)?" # noqa version_parser = r"(?P([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?(\w+)?)" +stable_parser = r"^([0-9]|[1-9][0-9]*)\.([0-9]|[1-9][0-9]*)\.([0-9]|[1-9][0-9]*)$"