From 53a1d176de140d07238e8c291cd84a0d9e6e4cd2 Mon Sep 17 00:00:00 2001 From: Jens Troeger Date: Thu, 6 Oct 2022 14:20:36 +1000 Subject: [PATCH 1/2] feat: add major-version-zero option to support initial package development --- commitizen/cli.py | 6 +++ commitizen/commands/bump.py | 18 +++++++- commitizen/defaults.py | 12 +++++ docs/bump.md | 33 ++++++++++++- tests/commands/test_bump_command.py | 72 ++++++++++++++++++++++++++++- tests/test_conf.py | 2 + tests/utils.py | 6 +++ 7 files changed, 145 insertions(+), 4 deletions(-) diff --git a/commitizen/cli.py b/commitizen/cli.py index 2064ef3bf1..bbad4e8b61 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -183,6 +183,12 @@ "default": False, "help": "retry commit if it fails the 1st time", }, + { + "name": ["--major-version-zero"], + "action": "store_true", + "default": None, + "help": "keep major version at zero, even for breaking changes", + }, { "name": "manual_version", "type": str, diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 06dc908dcc..14e339c669 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -5,7 +5,7 @@ import questionary from packaging.version import InvalidVersion, Version -from commitizen import bump, cmd, factory, git, out +from commitizen import bump, cmd, defaults, factory, git, out from commitizen.commands.changelog import Changelog from commitizen.config import BaseConfig from commitizen.exceptions import ( @@ -45,6 +45,7 @@ def __init__(self, config: BaseConfig, arguments: dict): "bump_message", "gpg_sign", "annotated_tag", + "major_version_zero", ] if arguments[key] is not None }, @@ -101,6 +102,7 @@ def __call__(self): # noqa: C901 tag_format: str = self.bump_settings["tag_format"] bump_commit_message: str = self.bump_settings["bump_message"] version_files: List[str] = self.bump_settings["version_files"] + major_version_zero: bool = self.bump_settings["major_version_zero"] dry_run: bool = self.arguments["dry_run"] is_yes: bool = self.arguments["yes"] @@ -126,6 +128,20 @@ def __call__(self): # noqa: C901 "--local-version cannot be combined with MANUAL_VERSION" ) + if major_version_zero: + raise NotAllowed( + "--major-version-zero cannot be combined with MANUAL_VERSION" + ) + + if major_version_zero: + if not current_version.startswith("0."): + raise NotAllowed( + f"--major-version-zero is meaningless for current version {current_version}" + ) + + # Update the bump map to ensure major version doesn't increment. + self.cz.bump_map = defaults.bump_map_major_version_zero + current_tag_version: str = bump.normalize_tag( current_version, tag_format=tag_format ) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index f6b6dee1c3..bdc853e1eb 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -39,6 +39,7 @@ class Settings(TypedDict, total=False): use_shortcuts: bool style: Optional[List[Tuple[str, str]]] customize: CzSettings + major_version_zero: bool name: str = "cz_conventional_commits" @@ -63,6 +64,7 @@ class Settings(TypedDict, total=False): "changelog_start_rev": None, "update_changelog_on_bump": False, "use_shortcuts": False, + "major_version_zero": False, } MAJOR = "MAJOR" @@ -80,6 +82,16 @@ class Settings(TypedDict, total=False): (r"^perf", PATCH), ) ) +bump_map_major_version_zero = OrderedDict( + ( + (r"^.+!$", MINOR), + (r"^BREAKING[\-\ ]CHANGE", MINOR), + (r"^feat", MINOR), + (r"^fix", PATCH), + (r"^refactor", PATCH), + (r"^perf", PATCH), + ) +) change_type_order = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"] bump_message = "bump: version $current_version → $new_version" diff --git a/docs/bump.md b/docs/bump.md index bf6f00d8e8..e94b96cb63 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -59,7 +59,8 @@ usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog] [--bump-message BUMP_MESSAGE] [--prerelease {alpha,beta,rc}] [--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}] [--check-consistency] [--annotated-tag] [--gpg-sign] - [--changelog-to-stdout] [--retry] [MANUAL_VERSION] + [--changelog-to-stdout] [--retry] [--major-version-zero] + [MANUAL_VERSION] positional arguments: MANUAL_VERSION bump to the given version (e.g: 1.5.3) @@ -93,6 +94,7 @@ options: --changelog-to-stdout Output changelog to the stdout --retry retry commit if it fails the 1st time + --major-version-zero keep major version at zero, even for breaking changes ``` ### `--files-only` @@ -199,6 +201,18 @@ It will retry the commit if it fails the 1st time. Useful to combine with code formatters, like [Prettier](https://prettier.io/). +### `--major-version-zero` + +A project in its initial development should have a major version zero, and even breaking changes +should not bump that major version from zero. This command ensures that behavior. + +If `--major-version-zero` is used for projects that have a version number greater than zero it fails. +If used together with a manual version the command also fails. + +We recommend setting `major_version_zero = true` in your configuration file while a project +is in its initial development. Remove that configuration using a breaking-change commit to bump +your project’s major version to `v1.0.0` once your project has reached maturity. + ## Avoid raising errors Some situations from commitizen rise an exit code different than 0. @@ -369,6 +383,8 @@ When set to `true` commitizen will create annotated tags. ```toml [tool.commitizen] +annotated_tag = true +``` --- @@ -379,7 +395,20 @@ When set to `true` commitizen will create gpg signed tags. ```toml [tool.commitizen] gpg_sign = true -annotated_tag = true +``` + +--- + +### `major_version_zero` + +When set to `true` commitizen will keep the major version at zero. +Useful during the initial development stage of your project. + +Defaults to: `false` + +```toml +[tool.commitizen] +major_version_zero = true ``` ## Custom bump diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 81b4f9d1b4..fbe7c0db6b 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -22,7 +22,7 @@ NotAllowed, NoVersionSpecifiedError, ) -from tests.utils import create_file_and_commit +from tests.utils import create_file_and_commit, create_tag @pytest.mark.parametrize( @@ -151,6 +151,31 @@ def test_bump_major_increment(commit_msg, mocker): assert tag_exists is True +@pytest.mark.usefixtures("tmp_commitizen_project") +@pytest.mark.parametrize( + "commit_msg", + ( + "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported", + "feat!: new user interface\n\nBREAKING CHANGE: age is no longer supported", + "feat!: new user interface", + "feat(user): new user interface\n\nBREAKING CHANGE: age is no longer supported", + "feat(user)!: new user interface\n\nBREAKING CHANGE: age is no longer supported", + "feat(user)!: new user interface", + "BREAKING CHANGE: age is no longer supported", + "BREAKING-CHANGE: age is no longer supported", + ), +) +def test_bump_major_increment_major_version_zero(commit_msg, mocker): + create_file_and_commit(commit_msg) + + testargs = ["cz", "bump", "--yes", "--major-version-zero"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("0.2.0") + assert tag_exists is True + + @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.parametrize( "commit_msg,increment,expected_tag", @@ -320,6 +345,34 @@ def test_bump_when_version_inconsistent_in_version_files( assert partial_expected_error_message in str(excinfo.value) +def test_bump_major_version_zero_when_major_is_not_zero(mocker, tmp_commitizen_project): + tmp_version_file = tmp_commitizen_project.join("__version__.py") + tmp_version_file.write("1.0.0") + tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") + tmp_commitizen_cfg_file.write( + f"[tool.commitizen]\n" + 'version="1.0.0"\n' + f'version_files = ["{str(tmp_version_file)}"]' + ) + tmp_changelog_file = tmp_commitizen_project.join("CHANGELOG.md") + tmp_changelog_file.write("## v1.0.0") + + create_file_and_commit("feat(user): new file") + create_tag("v1.0.0") + create_file_and_commit("feat(user)!: new file") + + testargs = ["cz", "bump", "--yes", "--major-version-zero"] + mocker.patch.object(sys, "argv", testargs) + + with pytest.raises(NotAllowed) as excinfo: + cli.main() + + expected_error_message = ( + "--major-version-zero is meaningless for current version 1.0.0" + ) + assert expected_error_message in str(excinfo.value) + + def test_bump_files_only(mocker, tmp_commitizen_project): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("0.1.0") @@ -683,3 +736,20 @@ def test_bump_manual_version(mocker, manual_version): cli.main() tag_exists = git.tag_exist(manual_version) assert tag_exists is True + + +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_manual_version_disallows_major_version_zero(mocker): + create_file_and_commit("feat: new file") + + manual_version = "0.2.0" + testargs = ["cz", "bump", "--yes", "--major-version-zero", manual_version] + mocker.patch.object(sys, "argv", testargs) + + with pytest.raises(NotAllowed) as excinfo: + cli.main() + + expected_error_message = ( + "--major-version-zero cannot be combined with MANUAL_VERSION" + ) + assert expected_error_message in str(excinfo.value) diff --git a/tests/test_conf.py b/tests/test_conf.py index f051a1f56f..c4020d640b 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -48,6 +48,7 @@ "changelog_start_rev": None, "update_changelog_on_bump": False, "use_shortcuts": False, + "major_version_zero": False, } _new_settings = { @@ -63,6 +64,7 @@ "changelog_start_rev": None, "update_changelog_on_bump": False, "use_shortcuts": False, + "major_version_zero": False, } _read_settings = { diff --git a/tests/utils.py b/tests/utils.py index 462efca511..5dcf6722e9 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -26,6 +26,12 @@ def create_file_and_commit(message: str, filename: Optional[str] = None): raise exceptions.CommitError(c.err) +def create_tag(tag: str): + c = git.tag(tag) + if c.return_code != 0: + raise exceptions.CommitError(c.err) + + def wait_for_tag(): """Wait for tag. From 04ac8deea8bc06610bfbc409495f61579669353c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 28 Oct 2022 09:09:03 +0000 Subject: [PATCH 2/2] =?UTF-8?q?bump:=20version=202.36.0=20=E2=86=92=202.37?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 6 ++++++ commitizen/__version__.py | 2 +- pyproject.toml | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bafa7aa7fc..c584dba255 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: no-commit-to-branch - repo: https://github.com/commitizen-tools/commitizen - rev: v2.36.0 # automatically updated by Commitizen + rev: v2.37.0 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cdfbb28de..a8b46e2e2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ +## v2.37.0 (2022-10-28) + +### Feat + +- add major-version-zero option to support initial package development + ## v2.36.0 (2022-10-28) ### Feat diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 07d3c71f32..09f14b57bb 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "2.36.0" +__version__ = "2.37.0" diff --git a/pyproject.toml b/pyproject.toml index bc02a2244f..03fae47472 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.commitizen] -version = "2.36.0" +version = "2.37.0" tag_format = "v$version" version_files = [ "pyproject.toml:version", @@ -30,7 +30,7 @@ exclude = ''' [tool.poetry] name = "commitizen" -version = "2.36.0" +version = "2.37.0" description = "Python commitizen client tool" authors = ["Santiago Fraire "] license = "MIT"