From 2cf67d78a70704b8133648e041506fd76f84d803 Mon Sep 17 00:00:00 2001 From: Robert Schweizer Date: Tue, 27 Jun 2023 11:32:36 +0200 Subject: [PATCH 1/3] docs: Document where `tag_format` config value is used I was very confused about these different usages. Documenting them might be a good basis for future simplification. --- docs/bump.md | 12 +++++++++++- tests/test_version_providers.py | 31 +++++++++++++++---------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/docs/bump.md b/docs/bump.md index 5446ef3c42..ddc9117a69 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -317,7 +317,17 @@ cz -nr 21 bump ### `tag_format` -It is used to read the format from the git tags, and also to generate the tags. +`tag_format` and `version_scheme` are combined to make Git tag names from versions. + +These are used in: + +* `cz bump`: Find previous release tag (exact match) and generate new tag. +* Find previous release tags in `cz changelog`. + * If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match. + * `--rev-range` is converted to Git tag names with `tag_format` before searching Git history. +* If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags: + * If no `tag_format` is set: `VersionProtocol.parser` (allows `v` prefix) + * If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases) Commitizen supports 2 types of formats, a simple and a more complex. diff --git a/tests/test_version_providers.py b/tests/test_version_providers.py index edf6e2f1cb..97c2601433 100644 --- a/tests/test_version_providers.py +++ b/tests/test_version_providers.py @@ -173,11 +173,22 @@ def test_file_providers( @pytest.mark.parametrize( - "tag_format,tag,version", + "tag_format,tag,expected_version", ( + # If tag_format is None, version_scheme.parser is used. + # Its DEFAULT_VERSION_PARSER allows a v prefix, but matches PEP440 otherwise. (None, "0.1.0", "0.1.0"), (None, "v0.1.0", "0.1.0"), + (None, "no-match-because-version-scheme-is-strict", "0.0.0"), + # If tag_format is not None, TAG_FORMAT_REGEXS are used, which are much more + # lenient. + ("$version", "match-TAG_FORMAT_REGEXS", "match-TAG_FORMAT_REGEXS"), + ("$version", "0.1.0", "0.1.0"), + ("$version", "v0.1.0", "0.1.0"), + ("$version", "v-0.1.0", "0.1.0"), ("v$version", "v0.1.0", "0.1.0"), + ("v$version", "no-match-because-no-v-prefix", "0.0.0"), + ("v$version", "v-match-TAG_FORMAT_REGEXS", "-match-TAG_FORMAT_REGEXS"), ("version-$version", "version-0.1.0", "0.1.0"), ("version-$version", "version-0.1", "0.1"), ("version-$version", "version-0.1.0rc1", "0.1.0rc1"), @@ -191,7 +202,7 @@ def test_file_providers( ) @pytest.mark.usefixtures("tmp_git_project") def test_scm_provider( - config: BaseConfig, tag_format: str | None, tag: str, version: str + config: BaseConfig, tag_format: str | None, tag: str, expected_version: str ): create_file_and_commit("test: fake commit") create_tag(tag) @@ -203,25 +214,13 @@ def test_scm_provider( provider = get_provider(config) assert isinstance(provider, ScmProvider) - assert provider.get_version() == version + actual_version = provider.get_version() + assert actual_version == expected_version # Should not fail on set_version() provider.set_version("43.1") -@pytest.mark.usefixtures("tmp_git_project") -def test_scm_provider_default_without_matching_tag(config: BaseConfig): - create_file_and_commit("test: fake commit") - create_tag("should-not-match") - create_file_and_commit("test: fake commit") - - config.settings["version_provider"] = "scm" - - provider = get_provider(config) - assert isinstance(provider, ScmProvider) - assert provider.get_version() == "0.0.0" - - @pytest.mark.usefixtures("tmp_git_project") def test_scm_provider_default_without_commits_and_tags(config: BaseConfig): config.settings["version_provider"] = "scm" From 13662fe497301296d4e0579657b31ecd6e868fe8 Mon Sep 17 00:00:00 2001 From: Robert Schweizer Date: Tue, 27 Jun 2023 11:39:43 +0200 Subject: [PATCH 2/3] fix: Treat $version the same as unset tag_format in ScmProvider This should be non-breaking unless users rely on the looser version validation of TAG_FORMAT_REGEXS compared to their `version_scheme`. --- commitizen/providers.py | 6 +++--- docs/bump.md | 2 +- tests/test_version_providers.py | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/commitizen/providers.py b/commitizen/providers.py index dbf8b9f9a2..25b5289438 100644 --- a/commitizen/providers.py +++ b/commitizen/providers.py @@ -195,9 +195,9 @@ class ScmProvider(VersionProvider): def _tag_format_matcher(self) -> Callable[[str], str | None]: version_scheme = get_version_scheme(self.config) - pattern = ( - self.config.settings.get("tag_format") or version_scheme.parser.pattern - ) + pattern = self.config.settings.get("tag_format") + if pattern in (None, "$version"): + pattern = version_scheme.parser.pattern for var, tag_pattern in self.TAG_FORMAT_REGEXS.items(): pattern = pattern.replace(var, tag_pattern) diff --git a/docs/bump.md b/docs/bump.md index ddc9117a69..9d7b9cb474 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -326,7 +326,7 @@ These are used in: * If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match. * `--rev-range` is converted to Git tag names with `tag_format` before searching Git history. * If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags: - * If no `tag_format` is set: `VersionProtocol.parser` (allows `v` prefix) + * If `tag_format` is unset or set to `$version`: `VersionProtocol.parser` (allows `v` prefix) * If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases) Commitizen supports 2 types of formats, a simple and a more complex. diff --git a/tests/test_version_providers.py b/tests/test_version_providers.py index 97c2601433..4fdf119cb0 100644 --- a/tests/test_version_providers.py +++ b/tests/test_version_providers.py @@ -175,17 +175,17 @@ def test_file_providers( @pytest.mark.parametrize( "tag_format,tag,expected_version", ( - # If tag_format is None, version_scheme.parser is used. + # If tag_format is None or $version, version_scheme.parser is used. # Its DEFAULT_VERSION_PARSER allows a v prefix, but matches PEP440 otherwise. (None, "0.1.0", "0.1.0"), (None, "v0.1.0", "0.1.0"), (None, "no-match-because-version-scheme-is-strict", "0.0.0"), - # If tag_format is not None, TAG_FORMAT_REGEXS are used, which are much more - # lenient. - ("$version", "match-TAG_FORMAT_REGEXS", "match-TAG_FORMAT_REGEXS"), + ("$version", "no-match-because-version-scheme-is-strict", "0.0.0"), ("$version", "0.1.0", "0.1.0"), ("$version", "v0.1.0", "0.1.0"), - ("$version", "v-0.1.0", "0.1.0"), + ("$version", "v-0.1.0", "0.0.0"), + # If tag_format is not None or $version, TAG_FORMAT_REGEXS are used, which are + # much more lenient but require a v prefix. ("v$version", "v0.1.0", "0.1.0"), ("v$version", "no-match-because-no-v-prefix", "0.0.0"), ("v$version", "v-match-TAG_FORMAT_REGEXS", "-match-TAG_FORMAT_REGEXS"), From 9248122d95b799a40dd319d8de9ce13f6a5d6296 Mon Sep 17 00:00:00 2001 From: Robert Schweizer Date: Tue, 27 Jun 2023 11:43:36 +0200 Subject: [PATCH 3/3] refactor: Make tag_format properly default to $version We've been using this default already in `normalize_tag`, but setting this value in the settings dict is cleaner. --- commitizen/bump.py | 5 +---- commitizen/commands/changelog.py | 4 ++-- commitizen/commands/init.py | 7 ++++--- commitizen/defaults.py | 4 ++-- commitizen/providers.py | 4 ++-- docs/bump.md | 4 ++-- docs/config.md | 2 +- tests/test_conf.py | 4 ++-- tests/test_version_providers.py | 7 ++----- 9 files changed, 18 insertions(+), 23 deletions(-) diff --git a/commitizen/bump.py b/commitizen/bump.py index 7134ef6a6d..075f170ceb 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -101,7 +101,7 @@ def _version_to_regex(version: str) -> str: def normalize_tag( version: Version | str, - tag_format: str | None = None, + tag_format: str, scheme: VersionScheme | None = None, ) -> str: """The tag and the software version might be different. @@ -118,9 +118,6 @@ def normalize_tag( scheme = scheme or DEFAULT_SCHEME version = scheme(version) if isinstance(version, str) else version - if not tag_format: - return str(version) - major, minor, patch = version.release prerelease = version.prerelease or "" diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index e1cbcddb8f..cf667a299b 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -57,8 +57,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["tag_format"] ) self.merge_prerelease = args.get( "merge_prerelease" diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index b9803d0713..33cd9a26f4 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_schemes import KNOWN_SCHEMES, Version, get_version_scheme @@ -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 c3faaec8a0..62e634dbb7 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -39,7 +39,7 @@ class Settings(TypedDict, total=False): version_provider: str | None version_scheme: str | None version_type: str | None - tag_format: str | None + tag_format: str bump_message: str | None allow_abort: bool allowed_prefixes: list[str] @@ -73,7 +73,7 @@ class Settings(TypedDict, total=False): "version_files": [], "version_provider": "commitizen", "version_scheme": None, - "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, "allowed_prefixes": [ diff --git a/commitizen/providers.py b/commitizen/providers.py index 25b5289438..b764e77020 100644 --- a/commitizen/providers.py +++ b/commitizen/providers.py @@ -195,8 +195,8 @@ class ScmProvider(VersionProvider): def _tag_format_matcher(self) -> Callable[[str], str | None]: version_scheme = get_version_scheme(self.config) - pattern = self.config.settings.get("tag_format") - if pattern in (None, "$version"): + pattern = self.config.settings["tag_format"] + if pattern == "$version": pattern = version_scheme.parser.pattern for var, tag_pattern in self.TAG_FORMAT_REGEXS.items(): pattern = pattern.replace(var, tag_pattern) diff --git a/docs/bump.md b/docs/bump.md index 9d7b9cb474..287d4e1e35 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -326,7 +326,7 @@ These are used in: * If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match. * `--rev-range` is converted to Git tag names with `tag_format` before searching Git history. * If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags: - * If `tag_format` is unset or set to `$version`: `VersionProtocol.parser` (allows `v` prefix) + * If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix) * If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases) Commitizen supports 2 types of formats, a simple and a more complex. @@ -346,7 +346,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/config.md b/docs/config.md index 6f8dea47ea..fdd0ae2460 100644 --- a/docs/config.md +++ b/docs/config.md @@ -46,7 +46,7 @@ Select a version scheme from the following options [`pep440`, `semver`]. Useful 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] diff --git a/tests/test_conf.py b/tests/test_conf.py index ec9ba429fa..5c2cb6b6ff 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -46,7 +46,7 @@ "version": "1.0.0", "version_provider": "commitizen", "version_scheme": None, - "tag_format": None, + "tag_format": "$version", "bump_message": None, "allow_abort": False, "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], @@ -69,7 +69,7 @@ "version": "2.0.0", "version_provider": "commitizen", "version_scheme": None, - "tag_format": None, + "tag_format": "$version", "bump_message": None, "allow_abort": False, "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], diff --git a/tests/test_version_providers.py b/tests/test_version_providers.py index 4fdf119cb0..12948da851 100644 --- a/tests/test_version_providers.py +++ b/tests/test_version_providers.py @@ -175,11 +175,8 @@ def test_file_providers( @pytest.mark.parametrize( "tag_format,tag,expected_version", ( - # If tag_format is None or $version, version_scheme.parser is used. + # If tag_format is $version (the default), version_scheme.parser is used. # Its DEFAULT_VERSION_PARSER allows a v prefix, but matches PEP440 otherwise. - (None, "0.1.0", "0.1.0"), - (None, "v0.1.0", "0.1.0"), - (None, "no-match-because-version-scheme-is-strict", "0.0.0"), ("$version", "no-match-because-version-scheme-is-strict", "0.0.0"), ("$version", "0.1.0", "0.1.0"), ("$version", "v0.1.0", "0.1.0"), @@ -202,7 +199,7 @@ def test_file_providers( ) @pytest.mark.usefixtures("tmp_git_project") def test_scm_provider( - config: BaseConfig, tag_format: str | None, tag: str, expected_version: str + config: BaseConfig, tag_format: str, tag: str, expected_version: str ): create_file_and_commit("test: fake commit") create_tag(tag)