diff --git a/commitizen/bump.py b/commitizen/bump.py index ed410bbcc1..1c0f7ac2d8 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -217,42 +217,6 @@ def _version_to_regex(version: str) -> str: return version.replace(".", r"\.").replace("+", r"\+") -def normalize_tag( - version: Union[VersionProtocol, str], - tag_format: Optional[str] = None, - version_type_cls: Optional[Type[VersionProtocol]] = None, -) -> str: - """The tag and the software version might be different. - - That's why this function exists. - - Example: - | tag | version (PEP 0440) | - | --- | ------- | - | v0.9.0 | 0.9.0 | - | ver1.0.0 | 1.0.0 | - | ver1.0.0.a0 | 1.0.0a0 | - """ - if version_type_cls is None: - version_type_cls = Version - if isinstance(version, str): - version = version_type_cls(version) - - if not tag_format: - return str(version) - - major, minor, patch = version.release - prerelease = "" - # version.pre is needed for mypy check - if version.is_prerelease and version.pre: - prerelease = f"{version.pre[0]}{version.pre[1]}" - - t = Template(tag_format) - return t.safe_substitute( - version=version, major=major, minor=minor, patch=patch, prerelease=prerelease - ) - - def create_commit_message( current_version: Union[Version, str], new_version: Union[Version, str], diff --git a/commitizen/changelog.py b/commitizen/changelog.py index 74cee3260a..e42824dc4b 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -37,9 +37,9 @@ from packaging.version import InvalidVersion, Version from commitizen import defaults -from commitizen.bump import normalize_tag from commitizen.exceptions import InvalidConfigurationError, NoCommitsFoundError from commitizen.git import GitCommit, GitTag +from commitizen.tags import tag_from_version if sys.version_info >= (3, 8): from commitizen.version_types import VersionProtocol @@ -341,13 +341,13 @@ def get_oldest_and_newest_rev( except ValueError: newest = version - newest_tag = normalize_tag( + newest_tag = tag_from_version( newest, tag_format=tag_format, version_type_cls=version_type_cls ) oldest_tag = None if oldest: - oldest_tag = normalize_tag( + oldest_tag = tag_from_version( oldest, tag_format=tag_format, version_type_cls=version_type_cls ) diff --git a/commitizen/cli.py b/commitizen/cli.py index ed89b5675a..355a37b1ce 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -1,8 +1,8 @@ import argparse import logging import sys -from pathlib import Path from functools import partial +from pathlib import Path from types import TracebackType from typing import List @@ -274,6 +274,13 @@ "If not set, it will include prereleases in the changelog" ), }, + { + "name": "--tag-regex", + "help": ( + "regex match for tags represented " + "within the changelog. default: '.*'" + ), + }, ], }, { diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 53e194bc6f..f33734ca76 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -22,6 +22,7 @@ NoVersionSpecifiedError, ) from commitizen.providers import get_provider +from commitizen.tags import tag_from_version logger = getLogger("commitizen") @@ -161,7 +162,7 @@ def __call__(self): # noqa: C901 f"--major-version-zero is meaningless for current version {current_version}" ) - current_tag_version: str = bump.normalize_tag( + current_tag_version: str = tag_from_version( current_version, tag_format=tag_format, version_type_cls=self.version_type, @@ -223,7 +224,7 @@ def __call__(self): # noqa: C901 version_type_cls=self.version_type, ) - new_tag_version = bump.normalize_tag( + new_tag_version = tag_from_version( new_version, tag_format=tag_format, version_type_cls=self.version_type, diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 69d223ed49..24685face7 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -1,12 +1,14 @@ import os.path +import re from difflib import SequenceMatcher from operator import itemgetter from typing import Callable, Dict, List, Optional from packaging.version import parse -from commitizen import bump, changelog, defaults, factory, git, out, version_types +from commitizen import changelog, defaults, factory, git, out, version_types from commitizen.config import BaseConfig +from commitizen.defaults import DEFAULT_SETTINGS from commitizen.exceptions import ( DryRunExit, NoCommitsFoundError, @@ -16,6 +18,7 @@ NotAllowed, ) from commitizen.git import GitTag, smart_open +from commitizen.tags import make_tag_pattern, tag_from_version class Changelog: @@ -55,8 +58,8 @@ def __init__(self, config: BaseConfig, args): or defaults.change_type_order ) self.rev_range = args.get("rev_range") - self.tag_format = args.get("tag_format") or self.config.settings.get( - "tag_format" + self.tag_format: str = args.get("tag_format") or self.config.settings.get( + "tag_format", DEFAULT_SETTINGS["tag_format"] ) self.merge_prerelease = args.get( "merge_prerelease" @@ -65,6 +68,11 @@ def __init__(self, config: BaseConfig, args): version_type = self.config.settings.get("version_type") self.version_type = version_type and version_types.VERSION_TYPES[version_type] + tag_regex = args.get("tag_regex") or self.config.settings.get("tag_regex") + if not tag_regex: + tag_regex = make_tag_pattern(self.tag_format) + self.tag_pattern = re.compile(str(tag_regex), re.VERBOSE | re.IGNORECASE) + def _find_incremental_rev(self, latest_version: str, tags: List[GitTag]) -> str: """Try to find the 'start_rev'. @@ -138,7 +146,7 @@ def __call__(self): # Don't continue if no `file_name` specified. assert self.file_name - tags = git.get_tags() + tags = git.get_tags(pattern=self.tag_pattern) if not tags: tags = [] @@ -148,7 +156,7 @@ def __call__(self): changelog_meta = changelog.get_metadata(self.file_name) latest_version = changelog_meta.get("latest_version") if latest_version: - latest_tag_version: str = bump.normalize_tag( + latest_tag_version: str = tag_from_version( latest_version, tag_format=self.tag_format, version_type_cls=self.version_type, diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 08fdadef77..c79bc331be 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -10,7 +10,7 @@ from commitizen.__version__ import __version__ from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig from commitizen.cz import registry -from commitizen.defaults import config_files +from commitizen.defaults import DEFAULT_SETTINGS, config_files from commitizen.exceptions import InitFailedError, NoAnswersError from commitizen.git import get_latest_tag_name, get_tag_names, smart_open from commitizen.version_types import VERSION_TYPES @@ -203,14 +203,15 @@ def _ask_tag_format(self, latest_tag) -> str: f'Is "{tag_format}" the correct tag format?', style=self.cz.style ).unsafe_ask() + default_format = DEFAULT_SETTINGS["tag_format"] if not is_correct_format: tag_format = questionary.text( - 'Please enter the correct version format: (default: "$version")', + f'Please enter the correct version format: (default: "{default_format}")', style=self.cz.style, ).unsafe_ask() if not tag_format: - tag_format = "$version" + tag_format = default_format return tag_format def _ask_version_provider(self) -> str: diff --git a/commitizen/defaults.py b/commitizen/defaults.py index a7c285edba..91035ae995 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -35,7 +35,7 @@ class Settings(TypedDict, total=False): version: Optional[str] version_files: List[str] version_provider: Optional[str] - tag_format: Optional[str] + tag_format: str bump_message: Optional[str] allow_abort: bool changelog_file: str @@ -68,7 +68,7 @@ class Settings(TypedDict, total=False): "version": None, "version_files": [], "version_provider": "commitizen", - "tag_format": None, # example v$version + "tag_format": "$version", # example v$version "bump_message": None, # bumped v$current_version to $new_version "allow_abort": False, "changelog_file": "CHANGELOG.md", diff --git a/commitizen/git.py b/commitizen/git.py index 2c2cb5b368..3eb8b33a87 100644 --- a/commitizen/git.py +++ b/commitizen/git.py @@ -1,4 +1,5 @@ import os +import re from enum import Enum from os import linesep from pathlib import Path @@ -140,7 +141,7 @@ def get_filenames_in_commit(git_reference: str = ""): raise GitCommandError(c.err) -def get_tags(dateformat: str = "%Y-%m-%d") -> List[GitTag]: +def get_tags(dateformat: str = "%Y-%m-%d", *, pattern: re.Pattern) -> List[GitTag]: inner_delimiter = "---inner_delimiter---" formatter = ( f'"%(refname:lstrip=2){inner_delimiter}' @@ -163,7 +164,9 @@ def get_tags(dateformat: str = "%Y-%m-%d") -> List[GitTag]: for line in c.out.split("\n")[:-1] ] - return git_tags + filtered_git_tags = [t for t in git_tags if pattern.fullmatch(t.name)] + + return filtered_git_tags def tag_exist(tag: str) -> bool: diff --git a/commitizen/tags.py b/commitizen/tags.py new file mode 100644 index 0000000000..32ac6f890b --- /dev/null +++ b/commitizen/tags.py @@ -0,0 +1,65 @@ +import re +import sys +from string import Template +from typing import Any, Optional, Type, Union + +from packaging.version import VERSION_PATTERN, Version + +if sys.version_info >= (3, 8): + from commitizen.version_types import VersionProtocol +else: + # workaround mypy issue for 3.7 python + VersionProtocol = Any + + +def tag_from_version( + version: Union[VersionProtocol, str], + tag_format: str, + version_type_cls: Optional[Type[VersionProtocol]] = None, +) -> str: + """The tag and the software version might be different. + + That's why this function exists. + + Example: + | tag | version (PEP 0440) | + | --- | ------- | + | v0.9.0 | 0.9.0 | + | ver1.0.0 | 1.0.0 | + | ver1.0.0.a0 | 1.0.0a0 | + """ + if version_type_cls is None: + version_type_cls = Version + if isinstance(version, str): + version = version_type_cls(version) + + major, minor, patch = version.release + prerelease = "" + # version.pre is needed for mypy check + if version.is_prerelease and version.pre: + prerelease = f"{version.pre[0]}{version.pre[1]}" + + t = Template(tag_format) + return t.safe_substitute( + version=version, major=major, minor=minor, patch=patch, prerelease=prerelease + ) + + +def make_tag_pattern(tag_format: str) -> str: + """Make regex pattern to match all tags created by tag_format.""" + escaped_format = re.escape(tag_format) + escaped_format = re.sub( + r"\\\$(version|major|minor|patch|prerelease)", r"$\1", escaped_format + ) + # pre-release part of VERSION_PATTERN + pre_release_pattern = r"([-_\.]?(a|b|c|rc|alpha|beta|pre|preview)([-_\.]?[0-9]+)?)?" + filter_regex = Template(escaped_format).safe_substitute( + # VERSION_PATTERN allows the v prefix, but we'd rather have users configure it + # explicitly. + version=VERSION_PATTERN.lstrip("\n v?"), + major="[0-9]+", + minor="[0-9]+", + patch="[0-9]+", + prerelease=pre_release_pattern, + ) + return filter_regex diff --git a/docs/bump.md b/docs/bump.md index 1c96fb43aa..a573a3dd91 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -327,7 +327,7 @@ In your `pyproject.toml` or `.cz.toml` tag_format = "v$major.$minor.$patch$prerelease" ``` -The variables must be preceded by a `$` sign. +The variables must be preceded by a `$` sign. Default is `$version`. Supported variables: diff --git a/docs/changelog.md b/docs/changelog.md index d6799e198f..24730dabc8 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -186,6 +186,28 @@ cz changelog --merge-prerelease changelog_merge_prerelease = true ``` +### `tag-regex` + +This value can be set in the `toml` file with the key `tag_regex` under `tools.commitizen`. + +`tag_regex` is the regex pattern that selects tags to include in the changelog. +By default, the changelog will capture all git tags matching the `tag_format`, including pre-releases. + +Example use-cases: + +- Exclude pre-releases from the changelog +- Include existing tags that do not follow `tag_format` in the changelog + +```bash +cz changelog --tag-regex="[0-9]*\\.[0-9]*\\.[0-9]" +``` + +```toml +[tools.commitizen] +# ... +tag_regex = "[0-9]*\\.[0-9]*\\.[0-9]" +``` + ## Hooks Supported hook methods: diff --git a/docs/config.md b/docs/config.md index f93aca60e7..07f36ebc18 100644 --- a/docs/config.md +++ b/docs/config.md @@ -38,10 +38,18 @@ Version provider used to read and write version [Read more](#version-providers) Type: `str` -Default: `None` +Default: `$version` Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [Read more][tag_format] +### `tag_regex` + +Type: `str` + +Default: Based on `tag_format` + +Tags must match this to be included in the changelog (e.g. `"([0-9.])*"` to exclude pre-releases). [Read more][tag_regex] + ### `update_changelog_on_bump` Type: `bool` @@ -339,6 +347,7 @@ setup( [version_files]: bump.md#version_files [tag_format]: bump.md#tag_format +[tag_regex]: changelog.md#tag_regex [bump_message]: bump.md#bump_message [major-version-zero]: bump.md#-major-version-zero [prerelease-offset]: bump.md#-prerelease_offset diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000000..ab1033bd37 --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/tests/commands/conftest.py b/tests/commands/conftest.py index 91931849b2..c62af685fa 100644 --- a/tests/commands/conftest.py +++ b/tests/commands/conftest.py @@ -44,10 +44,5 @@ def config_customize(): @pytest.fixture() -def changelog_path() -> str: +def changelog_path(tmp_commitizen_project) -> str: return os.path.join(os.getcwd(), "CHANGELOG.md") - - -@pytest.fixture() -def config_path() -> str: - return os.path.join(os.getcwd(), "pyproject.toml") diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index f11a485db4..6468bdfe8e 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -533,6 +533,24 @@ def test_bump_with_changelog_config(mocker: MockFixture, changelog_path, config_ assert "0.2.0" in out +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_with_changelog_excludes_custom_tags(mocker: MockFixture, changelog_path): + create_file_and_commit("feat(user): new file") + git.tag("custom-tag") + create_file_and_commit("feat(user): Another new file") + testargs = ["cz", "bump", "--yes", "--changelog"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + tag_exists = git.tag_exist("0.2.0") + assert tag_exists is True + + with open(changelog_path, "r") as f: + out = f.read() + assert out.startswith("#") + assert "## 0.2.0" in out + assert "custom-tag" not in out + + @pytest.mark.usefixtures("tmp_commitizen_project") def test_prevent_prerelease_when_no_increment_detected(mocker: MockFixture, capsys): create_file_and_commit("feat: new file") diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index 30033d9c7d..6c215138db 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -1,6 +1,8 @@ import itertools import sys from datetime import datetime +from typing import List +from unittest.mock import patch import pytest from pytest_mock import MockFixture @@ -1271,3 +1273,53 @@ def test_changelog_prerelease_rev_with_use_version_type_semver( out, _ = capsys.readouterr() file_regression.check(out, extension=".second-prerelease.md") + + +@pytest.mark.parametrize( + "config_file, expected_versions", + [ + pytest.param("", ["Unreleased"], id="v-prefix-not-configured"), + pytest.param( + 'tag_format = "v$version"', + ["v1.1.0", "v1.1.0-beta", "v1.0.0"], + id="v-prefix-configured-as-tag-format", + ), + pytest.param( + 'tag_format = "v$version"\n' + 'tag_regex = ".*"', + ["v1.1.0", "custom-tag", "v1.1.0-beta", "v1.0.0"], + id="tag-regex-matches-all-tags", + ), + pytest.param( + 'tag_format = "v$version"\n' + r'tag_regex = "v[0-9\\.]*"', + ["v1.1.0", "v1.0.0"], + id="tag-regex-excludes-pre-releases", + ), + ], +) +def test_changelog_tag_regex( + config_path, changelog_path, config_file: str, expected_versions: List[str] +): + with open(config_path, "a") as f: + f.write(config_file) + + # Create 4 tags with one valid feature each + create_file_and_commit("feat: initial") + git.tag("v1.0.0") + create_file_and_commit("feat: add 1") + git.tag("v1.1.0-beta") + create_file_and_commit("feat: add 2") + git.tag("custom-tag") + create_file_and_commit("feat: add 3") + git.tag("v1.1.0") + + # call CLI + with patch.object(sys, "argv", ["cz", "changelog"]): + cli.main() + + # open CLI output + with open(changelog_path, "r") as f: + out = f.read() + + headings = [line for line in out.splitlines() if line.startswith("## ")] + changelog_versions = [heading[3:].split()[0] for heading in headings] + assert changelog_versions == expected_versions diff --git a/tests/conftest.py b/tests/conftest.py index e2177472db..d070140133 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,8 +8,8 @@ from commitizen import cmd, defaults from commitizen.config import BaseConfig -from commitizen.cz.base import BaseCommitizen from commitizen.cz import registry +from commitizen.cz.base import BaseCommitizen from tests.utils import create_file_and_commit SIGNER = "GitHub Action" @@ -122,7 +122,7 @@ def config(): @pytest.fixture() -def config_path() -> str: +def config_path(tmp_commitizen_project) -> str: return os.path.join(os.getcwd(), "pyproject.toml") diff --git a/tests/test_bump_normalize_tag.py b/tests/test_bump_normalize_tag.py deleted file mode 100644 index 3bc9828a2f..0000000000 --- a/tests/test_bump_normalize_tag.py +++ /dev/null @@ -1,23 +0,0 @@ -import pytest -from packaging.version import Version - -from commitizen import bump - -conversion = [ - (("1.2.3", "v$version"), "v1.2.3"), - (("1.2.3a2", "v$version"), "v1.2.3a2"), - (("1.2.3b2", "v$version"), "v1.2.3b2"), - (("1.2.3", "ver$major.$minor.$patch"), "ver1.2.3"), - (("1.2.3a0", "ver$major.$minor.$patch.$prerelease"), "ver1.2.3.a0"), - (("1.2.3rc2", "$major.$minor.$patch.$prerelease-majestic"), "1.2.3.rc2-majestic"), - (("1.2.3+1.0.0", "v$version"), "v1.2.3+1.0.0"), - (("1.2.3+1.0.0", "v$version-local"), "v1.2.3+1.0.0-local"), - (("1.2.3+1.0.0", "ver$major.$minor.$patch"), "ver1.2.3"), -] - - -@pytest.mark.parametrize("test_input,expected", conversion) -def test_create_tag(test_input, expected): - version, format = test_input - new_tag = bump.normalize_tag(Version(version), format) - assert new_tag == expected diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 78a2697c36..d77a8b624a 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1,3 +1,5 @@ +from pathlib import Path + import pytest from commitizen import changelog, defaults, git @@ -495,7 +497,7 @@ def tags() -> list: @pytest.fixture def changelog_content() -> str: - changelog_path = "tests/CHANGELOG_FOR_TEST.md" + changelog_path = Path(__file__).parent / "CHANGELOG_FOR_TEST.md" with open(changelog_path, "r") as f: return f.read() diff --git a/tests/test_conf.py b/tests/test_conf.py index 4226096371..29597fad6a 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -1,5 +1,4 @@ import json -import os from pathlib import Path import pytest @@ -41,11 +40,11 @@ } -_settings = { +_read_settings = { "name": "cz_jira", "version": "1.0.0", "version_provider": "commitizen", - "tag_format": None, + "tag_format": "$version", "bump_message": None, "allow_abort": False, "version_files": ["commitizen/__version__.py", "pyproject.toml"], @@ -67,7 +66,7 @@ "name": "cz_jira", "version": "2.0.0", "version_provider": "commitizen", - "tag_format": None, + "tag_format": "$version", "bump_message": None, "allow_abort": False, "version_files": ["commitizen/__version__.py", "pyproject.toml"], @@ -85,16 +84,6 @@ "version_type": None, } -_read_settings = { - "name": "cz_jira", - "version": "1.0.0", - "version_files": ["commitizen/__version__.py", "pyproject.toml"], - "style": [["pointer", "reverse"], ["question", "underline"]], - "changelog_file": "CHANGELOG.md", - "pre_bump_hooks": ["scripts/generate_documentation.sh"], - "post_bump_hooks": ["scripts/slack_notification.sh"], -} - @pytest.fixture def config_files_manager(request, tmpdir): @@ -111,7 +100,7 @@ def config_files_manager(request, tmpdir): def test_find_git_project_root(tmpdir): - assert git.find_git_project_root() == Path(os.getcwd()) + assert git.find_git_project_root() == Path(__file__).parent.parent with tmpdir.as_cwd() as _: assert git.find_git_project_root() is None @@ -133,7 +122,7 @@ class TestReadCfg: ) def test_load_conf(_, config_files_manager): cfg = config.read_cfg() - assert cfg.settings == _settings + assert cfg.settings == _read_settings def test_conf_returns_default_when_no_files(_, tmpdir): with tmpdir.as_cwd(): @@ -148,7 +137,7 @@ def test_load_empty_pyproject_toml_and_cz_toml_with_config(_, tmpdir): p.write(PYPROJECT) cfg = config.read_cfg() - assert cfg.settings == _settings + assert cfg.settings == _read_settings class TestTomlConfig: diff --git a/tests/test_git.py b/tests/test_git.py index 81089f6759..cadf89e988 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -1,5 +1,6 @@ import inspect import os +import re import shutil from typing import List, Optional @@ -7,6 +8,7 @@ from pytest_mock import MockFixture from commitizen import cmd, exceptions, git +from commitizen.tags import make_tag_pattern from tests.utils import FakeCommand, create_file_and_commit @@ -28,7 +30,7 @@ def test_get_tags(mocker: MockFixture): ) mocker.patch("commitizen.cmd.run", return_value=FakeCommand(out=tag_str)) - git_tags = git.get_tags() + git_tags = git.get_tags(pattern=re.compile(r"v[0-9\.]+")) latest_git_tag = git_tags[0] assert latest_git_tag.rev == "333" assert latest_git_tag.name == "v1.0.0" @@ -37,7 +39,60 @@ def test_get_tags(mocker: MockFixture): mocker.patch( "commitizen.cmd.run", return_value=FakeCommand(out="", err="No tag available") ) - assert git.get_tags() == [] + assert git.get_tags(pattern=re.compile(r"v[0-9\.]+")) == [] + + +@pytest.mark.parametrize( + "pattern, expected_tags", + [ + pytest.param( + make_tag_pattern(tag_format="$version"), + [], # No versions with normal 1.2.3 pattern + id="default-tag-format", + ), + pytest.param( + make_tag_pattern(tag_format="$major-$minor-$patch$prerelease"), + ["1-0-0", "1-0-0alpha2"], + id="tag-format-with-hyphens", + ), + pytest.param( + r"[0-9]+\-[0-9]+\-[0-9]+", + ["1-0-0"], + id="tag-regex-with-hyphens-that-excludes-alpha", + ), + pytest.param( + make_tag_pattern(tag_format="v$version"), + ["v0.5.0", "v0.0.1-pre"], + id="tag-format-with-v-prefix", + ), + pytest.param( + make_tag_pattern(tag_format="custom-prefix-$version"), + ["custom-prefix-0.0.1"], + id="tag-format-with-custom-prefix", + ), + pytest.param( + ".*", + ["1-0-0", "1-0-0alpha2", "v0.5.0", "v0.0.1-pre", "custom-prefix-0.0.1"], + id="custom-tag-regex-to-include-all-tags", + ), + ], +) +def test_get_tags_filtering( + mocker: MockFixture, pattern: str, expected_tags: List[str] +): + tag_str = ( + "1-0-0---inner_delimiter---333---inner_delimiter---2020-01-20---inner_delimiter---\n" + "1-0-0alpha2---inner_delimiter---333---inner_delimiter---2020-01-20---inner_delimiter---\n" + "v0.5.0---inner_delimiter---222---inner_delimiter---2020-01-17---inner_delimiter---\n" + "v0.0.1-pre---inner_delimiter---111---inner_delimiter---2020-01-17---inner_delimiter---\n" + "custom-prefix-0.0.1---inner_delimiter---111---inner_delimiter---2020-01-17---inner_delimiter---\n" + "custom-non-release-tag" + ) + mocker.patch("commitizen.cmd.run", return_value=FakeCommand(out=tag_str)) + + git_tags = git.get_tags(pattern=re.compile(pattern, flags=re.VERBOSE)) + actual_name_list = [t.name for t in git_tags] + assert actual_name_list == expected_tags def test_get_tag_names(mocker: MockFixture): diff --git a/tests/test_tags.py b/tests/test_tags.py new file mode 100644 index 0000000000..efebe43f32 --- /dev/null +++ b/tests/test_tags.py @@ -0,0 +1,76 @@ +import re +from typing import Dict + +import pytest +from packaging.version import Version + +from commitizen.tags import make_tag_pattern, tag_from_version + +TAG_FORMATS: Dict[str, Dict[str, list]] = { + "v$version": { + "tags_per_version": [ + ("1.2.3", "v1.2.3"), + ("1.2.3a2", "v1.2.3a2"), + ("1.2.3b2", "v1.2.3b2"), + ("1.2.3+1.0.0", "v1.2.3+1.0.0"), + ], + "invalid_tags": ["1.2.3", "unknown-tag", "v1-2-3"], + }, + "ver$major-$minor-$patch$prerelease": { + "tags_per_version": [ + ("1.2.3", "ver1-2-3"), + ("1.2.3a0", "ver1-2-3a0"), + ("1.2.3+1.0.0", "ver1-2-3"), + ], + "invalid_tags": ["1.2.3", "unknown-tag", "v1-2-3", "v1.0.0", "ver1.0.0+123"], + }, + "ver$major.$minor.$patch$prerelease-majestic": { + "tags_per_version": [ + ("1.2.3rc2", "ver1.2.3rc2-majestic"), + ], + "invalid_tags": ["1.2.3", "unknown-tag", "v1-2-3", "v1.0.0", "ver1.0.0"], + }, + "v$version-local": { + "tags_per_version": [("1.2.3+1.0.0", "v1.2.3+1.0.0-local")], + "invalid_tags": ["1.2.3", "unknown-tag", "v1-2-3", "v1.0.0", "ver1.0.0"], + }, +} + + +@pytest.mark.parametrize( + "tag_format, version, expected_tag_name", + [ + (tag_format, version, expected_tag_name) + for tag_format, format_dict in TAG_FORMATS.items() + for version, expected_tag_name in format_dict["tags_per_version"] + ], +) +def test_tag_from_version(tag_format, version, expected_tag_name): + new_tag = tag_from_version(Version(version), tag_format) + assert new_tag == expected_tag_name + + +@pytest.mark.parametrize( + "tag_format,tag_name", + [ + (tag_format, tag_name) + for tag_format, format_dict in TAG_FORMATS.items() + for _, tag_name in format_dict["tags_per_version"] + ], +) +def test_make_tag_pattern_matches(tag_format: str, tag_name: str): + pattern = re.compile(make_tag_pattern(tag_format=tag_format), flags=re.VERBOSE) + assert pattern.fullmatch(tag_name) + + +@pytest.mark.parametrize( + "tag_format,invalid_tag_name", + [ + (tag_format, invalid_tag_name) + for tag_format, format_dict in TAG_FORMATS.items() + for invalid_tag_name in format_dict["invalid_tags"] + ], +) +def test_make_tag_pattern_does_not_match(tag_format: str, invalid_tag_name: str): + pattern = re.compile(make_tag_pattern(tag_format=tag_format), flags=re.VERBOSE) + assert pattern.fullmatch(invalid_tag_name) is None
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: