From 1da13bda9bd86c03f6911e5490635778b9ef75d3 Mon Sep 17 00:00:00 2001 From: Lars Solberg Date: Fri, 29 Dec 2023 00:41:20 +0100 Subject: [PATCH] feat(bump): functionality to add build-metadata to version string --- commitizen/cli.py | 5 +++ commitizen/commands/bump.py | 13 +++++++ commitizen/commands/changelog.py | 12 +++++- commitizen/cz/utils.py | 6 +++ commitizen/version_schemes.py | 15 +++++++- docs/bump.md | 23 +++++++++++- tests/test_bump_create_commit_message.py | 47 ++++++++++++++++++++++++ 7 files changed, 117 insertions(+), 4 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 511f745652..12e3aa6451 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -308,6 +308,11 @@ def __call__( "help": "bump to the given version (e.g: 1.5.3)", "metavar": "MANUAL_VERSION", }, + { + "name": ["--build-metadata"], + "help": "Add additional build-metadata to the version-number", + "default": None, + }, ], }, { diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index af365ecd28..df3e725860 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -152,6 +152,7 @@ def __call__(self): # noqa: C901 is_files_only: bool | None = self.arguments["files_only"] is_local_version: bool | None = self.arguments["local_version"] manual_version = self.arguments["manual_version"] + build_metadata = self.arguments["build_metadata"] if manual_version: if increment: @@ -168,6 +169,11 @@ def __call__(self): # noqa: C901 "--local-version cannot be combined with MANUAL_VERSION" ) + if build_metadata: + raise NotAllowed( + "--build-metadata cannot be combined with MANUAL_VERSION" + ) + if major_version_zero: raise NotAllowed( "--major-version-zero cannot be combined with MANUAL_VERSION" @@ -184,6 +190,12 @@ def __call__(self): # noqa: C901 f"--major-version-zero is meaningless for current version {current_version}" ) + if build_metadata: + if is_local_version: + raise NotAllowed( + "--local-version cannot be combined with --build-metadata" + ) + current_tag_version: str = bump.normalize_tag( current_version, tag_format=tag_format, @@ -237,6 +249,7 @@ def __call__(self): # noqa: C901 prerelease_offset=prerelease_offset, devrelease=devrelease, is_local_version=is_local_version, + build_metadata=build_metadata, ) new_tag_version = bump.normalize_tag( diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 8b3c309636..f44f59bb7c 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -20,6 +20,7 @@ from commitizen.changelog_formats import get_changelog_format from commitizen.git import GitTag, smart_open from commitizen.version_schemes import get_version_scheme +from commitizen.cz.utils import strip_local_version class Changelog: @@ -98,7 +99,12 @@ def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str: """ SIMILARITY_THRESHOLD = 0.89 tag_ratio = map( - lambda tag: (SequenceMatcher(None, latest_version, tag.name).ratio(), tag), + lambda tag: ( + SequenceMatcher( + None, latest_version, strip_local_version(tag.name) + ).ratio(), + tag, + ), tags, ) try: @@ -169,7 +175,9 @@ def __call__(self): tag_format=self.tag_format, scheme=self.scheme, ) - start_rev = self._find_incremental_rev(latest_tag_version, tags) + start_rev = self._find_incremental_rev( + strip_local_version(latest_tag_version), tags + ) if self.rev_range: start_rev, end_rev = changelog.get_oldest_and_newest_rev( diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py index ea13be22c5..71dac105a7 100644 --- a/commitizen/cz/utils.py +++ b/commitizen/cz/utils.py @@ -1,3 +1,5 @@ +import re + from commitizen.cz import exceptions @@ -9,3 +11,7 @@ def required_validator(answer, msg=None): def multiple_line_breaker(answer, sep="|"): return "\n".join(line.strip() for line in answer.split(sep) if line) + + +def strip_local_version(version: str) -> str: + return re.sub(r"\+.+", "", version) diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index d7967ae928..07af30bcd2 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -166,6 +166,17 @@ def generate_devrelease(self, devrelease: int | None) -> str: return f"dev{devrelease}" + def generate_build_metadata(self, build_metadata: str | None) -> str: + """Generate build-metadata + + Build-metadata (local version) is not used in version calculations + but added after + statically. + """ + if build_metadata is None: + return "" + + return f"+{build_metadata}" + def increment_base(self, increment: str | None = None) -> str: prev_release = list(self.release) increments = [MAJOR, MINOR, PATCH] @@ -195,6 +206,7 @@ def bump( prerelease_offset: int = 0, devrelease: int | None = None, is_local_version: bool = False, + build_metadata: str | None = None, ) -> Self: """Based on the given increment a proper semver will be generated. @@ -215,8 +227,9 @@ def bump( base = self.increment_base(increment) dev_version = self.generate_devrelease(devrelease) pre_version = self.generate_prerelease(prerelease, offset=prerelease_offset) + build_metadata = self.generate_build_metadata(build_metadata) # TODO: post version - return self.scheme(f"{base}{pre_version}{dev_version}") # type: ignore + return self.scheme(f"{base}{pre_version}{dev_version}{build_metadata}") # type: ignore class Pep440(BaseVersion): diff --git a/docs/bump.md b/docs/bump.md index d0ceb90ea0..0d9b4960d9 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -55,7 +55,7 @@ $ cz bump --help usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog] [--no-verify] [--yes] [--tag-format TAG_FORMAT] [--bump-message BUMP_MESSAGE] [--prerelease {alpha,beta,rc}] [--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}] [--check-consistency] [--annotated-tag] [--gpg-sign] [--changelog-to-stdout] [--git-output-to-stderr] [--retry] [--major-version-zero] - [--prerelease-offset PRERELEASE_OFFSET] [--version-scheme {semver,pep440}] [--version-type {semver,pep440}] + [--prerelease-offset PRERELEASE_OFFSET] [--version-scheme {semver,pep440}] [--version-type {semver,pep440}] [--build-metadata BUILD_METADATA] [MANUAL_VERSION] positional arguments: @@ -95,6 +95,8 @@ options: choose version scheme --version-type {semver,pep440} Deprecated, use --version-scheme + --build-metadata {BUILD_METADATA} + additional metadata in the version string ``` ### `--files-only` @@ -264,6 +266,25 @@ cz bump --changelog --extra key=value -e short="quoted value" See [the template customization section](customization.md#customizing-the-changelog-template). +### `--build-metadata` + +Provides a way to specify additional metadata in the version string. This parameter is not compatible with `--local-version` as it uses the same part of the version string. + +```bash +cz bump --build-metadata yourmetadata +``` + +Will create a version like `1.1.2+yourmetadata`. +This can be useful for multiple things +* Git hash in version +* Labeling the version with additional metadata. + +Note that Commitizen ignores everything after `+` when it bumps the version. It is therefore safe to write different build-metadata between versions. + +You should normally not use this functionality, but if you decide to do, keep in mind that +* Version `1.2.3+a`, and `1.2.3+b` are the same version! Tools should not use the string after `+` for version calculation. This is probably not a guarantee (example in helm) even tho it is in the spec. +* It might be problematic having the metadata in place when doing upgrades depending on what tool you use. + ## Avoid raising errors Some situations from commitizen raise an exit code different than 0. diff --git a/tests/test_bump_create_commit_message.py b/tests/test_bump_create_commit_message.py index c096f23c39..517c7a0459 100644 --- a/tests/test_bump_create_commit_message.py +++ b/tests/test_bump_create_commit_message.py @@ -99,3 +99,50 @@ def test_bump_pre_commit_changelog_fails_always(mocker: MockFixture, freezer, re cmd.run("pre-commit install") with pytest.raises(exceptions.BumpCommitFailedError): cli.main() + + +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_with_build_metadata(mocker: MockFixture, freezer): + def _add_entry(test_str: str, args: list): + Path(test_str).write_text("") + cmd.run("git add -A") + cmd.run(f'git commit -m "fix: test-{test_str}"') + cz_args = ["cz", "bump", "--changelog", "--yes"] + args + mocker.patch.object(sys, "argv", cz_args) + cli.main() + + freezer.move_to("2024-01-01") + + _add_entry("a", ["--build-metadata", "a.b.c"]) + _add_entry("b", []) + _add_entry("c", ["--build-metadata", "alongmetadatastring"]) + _add_entry("d", []) + + # Pre-commit fixed last line adding extra indent and "\" char + assert Path("CHANGELOG.md").read_text() == dedent( + """\ + ## 0.1.4 (2024-01-01) + + ### Fix + + - test-d + + ## 0.1.3+alongmetadatastring (2024-01-01) + + ### Fix + + - test-c + + ## 0.1.2 (2024-01-01) + + ### Fix + + - test-b + + ## 0.1.1+a.b.c (2024-01-01) + + ### Fix + + - test-a + """ + )