from __future__ import annotations import inspect import re import sys from pathlib import Path from textwrap import dedent from unittest.mock import MagicMock, call import py import pytest from pytest_mock import MockFixture import commitizen.commands.bump as bump from commitizen import cli, cmd, git, hooks from commitizen.changelog_formats import ChangelogFormat from commitizen.cz.base import BaseCommitizen from commitizen.exceptions import ( BumpTagFailedError, CommitizenException, CurrentVersionNotFoundError, DryRunExit, ExitCode, ExpectedExit, GetNextExit, InvalidManualVersion, NoCommitsFoundError, NoneIncrementExit, NoPatternMapError, NotAGitProjectError, NotAllowed, NoVersionSpecifiedError, ) from tests.utils import create_file_and_commit, create_tag, skip_below_py_3_13 @pytest.mark.parametrize( "commit_msg", ( "fix: username exception", "fix(user): username exception", "refactor: remove ini configuration support", "refactor(config): remove ini configuration support", "perf: update to use multiproess", "perf(worker): update to use multiproess", ), ) @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_patch_increment(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.1.1") assert tag_exists is True @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_minor_increment(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") cmd_res = cmd.run('git for-each-ref refs/tags --format "%(objecttype):%(refname)"') assert tag_exists is True and "commit:refs/tags/0.2.0\n" in cmd_res.out @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_minor_increment_annotated(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes", "--annotated-tag"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") cmd_res = cmd.run('git for-each-ref refs/tags --format "%(objecttype):%(refname)"') assert tag_exists is True and "tag:refs/tags/0.2.0\n" in cmd_res.out _is_signed = git.is_signed_tag("0.2.0") assert _is_signed is False @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) @pytest.mark.usefixtures("tmp_commitizen_project_with_gpg") def test_bump_minor_increment_signed(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes", "--gpg-sign"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") cmd_res = cmd.run('git for-each-ref refs/tags --format "%(objecttype):%(refname)"') assert tag_exists is True and "tag:refs/tags/0.2.0\n" in cmd_res.out _is_signed = git.is_signed_tag("0.2.0") assert _is_signed is True @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) def test_bump_minor_increment_annotated_config_file( commit_msg, mocker: MockFixture, tmp_commitizen_project ): tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_commitizen_cfg_file.write( f"{tmp_commitizen_cfg_file.read()}\nannotated_tag = 1" ) create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") cmd_res = cmd.run('git for-each-ref refs/tags --format "%(objecttype):%(refname)"') assert tag_exists is True and "tag:refs/tags/0.2.0\n" in cmd_res.out _is_signed = git.is_signed_tag("0.2.0") assert _is_signed is False @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) def test_bump_minor_increment_signed_config_file( commit_msg, mocker: MockFixture, tmp_commitizen_project_with_gpg ): tmp_commitizen_cfg_file = tmp_commitizen_project_with_gpg.join("pyproject.toml") tmp_commitizen_cfg_file.write(f"{tmp_commitizen_cfg_file.read()}\ngpg_sign = 1") create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") cmd_res = cmd.run('git for-each-ref refs/tags --format "%(objecttype):%(refname)"') assert tag_exists is True and "tag:refs/tags/0.2.0\n" in cmd_res.out _is_signed = git.is_signed_tag("0.2.0") assert _is_signed 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(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("1.0.0") 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", [ ("feat: new file", "PATCH", "0.1.1"), ("fix: username exception", "major", "1.0.0"), ("refactor: remove ini configuration support", "patch", "0.1.1"), ("BREAKING CHANGE: age is no longer supported", "minor", "0.2.0"), ], ) def test_bump_command_increment_option( commit_msg, increment, expected_tag, mocker: MockFixture ): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--increment", increment, "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist(expected_tag) assert tag_exists is True @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_command_prelease(mocker: MockFixture): create_file_and_commit("feat: location") # Create an alpha pre-release. testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0a0") assert tag_exists is True # Create a beta pre-release. testargs = ["cz", "bump", "--prerelease", "beta", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0b0") assert tag_exists is True # With a current beta pre-release, bumping alpha must bump beta # because we can't bump "backwards". testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0a1") assert tag_exists is False tag_exists = git.tag_exist("0.2.0b1") assert tag_exists is True # Create a rc pre-release. testargs = ["cz", "bump", "--prerelease", "rc", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0rc0") assert tag_exists is True # With a current rc pre-release, bumping alpha must bump rc. testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0a1") assert tag_exists is False tag_exists = git.tag_exist("0.2.0b2") assert tag_exists is False tag_exists = git.tag_exist("0.2.0rc1") assert tag_exists is True # With a current rc pre-release, bumping beta must bump rc. testargs = ["cz", "bump", "--prerelease", "beta", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0a2") assert tag_exists is False tag_exists = git.tag_exist("0.2.0b2") assert tag_exists is False tag_exists = git.tag_exist("0.2.0rc2") assert tag_exists is True # Create a final release from the current pre-release. testargs = ["cz", "bump"] 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") def test_bump_command_prelease_increment(mocker: MockFixture): # FINAL RELEASE create_file_and_commit("fix: location") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() assert git.tag_exist("0.1.1") # PRERELEASE create_file_and_commit("fix: location") testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() assert git.tag_exist("0.1.2a0") create_file_and_commit("feat: location") testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() assert git.tag_exist("0.2.0a0") create_file_and_commit("feat!: breaking") testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() assert git.tag_exist("1.0.0a0") @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_command_prelease_exact_mode(mocker: MockFixture): # PRERELEASE create_file_and_commit("feat: location") testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0a0") assert tag_exists is True # PRERELEASE + PATCH BUMP testargs = [ "cz", "bump", "--prerelease", "alpha", "--yes", "--increment-mode=exact", ] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0a1") assert tag_exists is True # PRERELEASE + MINOR BUMP # --increment-mode allows the minor version to bump, and restart the prerelease create_file_and_commit("feat: location") testargs = [ "cz", "bump", "--prerelease", "alpha", "--yes", "--increment-mode=exact", ] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.3.0a0") assert tag_exists is True # PRERELEASE + MAJOR BUMP # --increment-mode=exact allows the major version to bump, and restart the prerelease testargs = [ "cz", "bump", "--prerelease", "alpha", "--yes", "--increment=MAJOR", "--increment-mode=exact", ] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("1.0.0a0") assert tag_exists is True @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_on_git_with_hooks_no_verify_disabled(mocker: MockFixture): """Bump commit without --no-verify""" cmd.run("mkdir .git/hooks") with open(".git/hooks/pre-commit", "w", encoding="utf-8") as f: f.write('#!/usr/bin/env bash\necho "0.1.0"') cmd.run("chmod +x .git/hooks/pre-commit") # MINOR create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes"] 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") def test_bump_tag_exists_raises_exception(mocker: MockFixture): cmd.run("mkdir .git/hooks") with open(".git/hooks/post-commit", "w", encoding="utf-8") as f: f.write("#!/usr/bin/env bash\nexit 9") cmd.run("chmod +x .git/hooks/post-commit") # MINOR create_file_and_commit("feat: new file") git.tag("0.2.0") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(BumpTagFailedError) as excinfo: cli.main() assert "0.2.0" in str(excinfo.value) # This should be a fatal error @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_on_git_with_hooks_no_verify_enabled(mocker: MockFixture): cmd.run("mkdir .git/hooks") with open(".git/hooks/pre-commit", "w", encoding="utf-8") as f: f.write('#!/usr/bin/env bash\necho "0.1.0"') cmd.run("chmod +x .git/hooks/pre-commit") # MINOR create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--no-verify"] 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") def test_bump_when_bumpping_is_not_support(mocker: MockFixture): create_file_and_commit( "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported" ) testargs = ["cz", "-n", "cz_jira", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(NoPatternMapError) as excinfo: cli.main() assert "'cz_jira' rule does not support bump" in str(excinfo.value) @pytest.mark.usefixtures("tmp_git_project") def test_bump_when_version_is_not_specify(mocker: MockFixture): mocker.patch.object(sys, "argv", ["cz", "bump"]) with pytest.raises(NoVersionSpecifiedError) as excinfo: cli.main() assert NoVersionSpecifiedError.message in str(excinfo.value) @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_when_no_new_commit(mocker: MockFixture): """bump without any commits since the last bump.""" # We need this first commit otherwise the revision is invalid. create_file_and_commit("feat: initial") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) # First bump. # The next bump should fail since # there is not a commit between the two bumps. cli.main() # bump without a new commit. with pytest.raises(NoCommitsFoundError) as excinfo: cli.main() expected_error_message = "[NO_COMMITS_FOUND]\nNo new commits found." assert expected_error_message in str(excinfo.value) def test_bump_when_version_inconsistent_in_version_files( tmp_commitizen_project, mocker: MockFixture ): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("100.999.10000") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_version_file_string = str(tmp_version_file).replace("\\", "/") tmp_commitizen_cfg_file.write( f"{tmp_commitizen_cfg_file.read()}\n" f'version_files = ["{tmp_version_file_string}"]' ) create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--check-consistency"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(CurrentVersionNotFoundError) as excinfo: cli.main() partial_expected_error_message = "Current version 0.1.0 is not found in" 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_version_file_string = str(tmp_version_file).replace("\\", "/") 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_string)}"]' ) 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: MockFixture, tmp_commitizen_project): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("0.1.0") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_version_file_string = str(tmp_version_file).replace("\\", "/") tmp_commitizen_cfg_file.write( f"{tmp_commitizen_cfg_file.read()}\n" f'version_files = ["{tmp_version_file_string}"]' ) create_file_and_commit("feat: new user interface") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") assert tag_exists is True create_file_and_commit("feat: another new feature") testargs = ["cz", "bump", "--yes", "--files-only"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(ExpectedExit): cli.main() tag_exists = git.tag_exist("0.3.0") assert tag_exists is False with open(tmp_version_file, encoding="utf-8") as f: assert "0.3.0" in f.read() with open(tmp_commitizen_cfg_file, encoding="utf-8") as f: assert "0.3.0" in f.read() def test_bump_local_version(mocker: MockFixture, tmp_commitizen_project): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("4.5.1+0.1.0") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_version_file_string = str(tmp_version_file).replace("\\", "/") tmp_commitizen_cfg_file.write( f"[tool.commitizen]\n" 'version="4.5.1+0.1.0"\n' f'version_files = ["{tmp_version_file_string}"]' ) create_file_and_commit("feat: new user interface") testargs = ["cz", "bump", "--yes", "--local-version"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("4.5.1+0.2.0") assert tag_exists is True with open(tmp_version_file, encoding="utf-8") as f: assert "4.5.1+0.2.0" in f.read() @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_dry_run(mocker: MockFixture, capsys): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--dry-run"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(DryRunExit): cli.main() out, _ = capsys.readouterr() assert "0.2.0" in out tag_exists = git.tag_exist("0.2.0") assert tag_exists is False def test_bump_in_non_git_project(tmpdir, config, mocker: MockFixture): testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) with tmpdir.as_cwd(): with pytest.raises(NotAGitProjectError): with pytest.raises(ExpectedExit): cli.main() def test_none_increment_exit_should_be_a_class(): assert inspect.isclass(NoneIncrementExit) def test_none_increment_exit_should_be_expected_exit_subclass(): assert issubclass(NoneIncrementExit, CommitizenException) def test_none_increment_exit_should_exist_in_bump(): assert hasattr(bump, "NoneIncrementExit") def test_none_increment_exit_is_exception(): assert bump.NoneIncrementExit == NoneIncrementExit @pytest.mark.usefixtures("tmp_commitizen_project") def test_none_increment_should_not_call_git_tag_and_error_code_is_not_zero( mocker: MockFixture, ): create_file_and_commit("test(test_get_all_droplets): fix bad comparison test") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) # stash git.tag for later restore stashed_git_tag = git.tag dummy_value = git.tag("0.0.2") git.tag = MagicMock(return_value=dummy_value) with pytest.raises(NoneIncrementExit): try: cli.main() except NoneIncrementExit as e: git.tag.assert_not_called() assert e.exit_code == ExitCode.NO_INCREMENT raise e # restore pop stashed git.tag = stashed_git_tag @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_with_changelog_arg(mocker: MockFixture, changelog_path): create_file_and_commit("feat(user): 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, encoding="utf-8") as f: out = f.read() assert out.startswith("#") assert "0.2.0" in out @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_with_changelog_config(mocker: MockFixture, changelog_path, config_path): create_file_and_commit("feat(user): new file") with open(config_path, "a", encoding="utf-8") as fp: fp.write("update_changelog_on_bump = true\n") testargs = ["cz", "bump", "--yes"] 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, encoding="utf-8") as f: out = f.read() assert out.startswith("#") assert "0.2.0" 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") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert "0.2.0" in out create_file_and_commit("test: new file") testargs = ["cz", "bump", "-pr", "beta"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(NoCommitsFoundError) as excinfo: cli.main() expected_error_message = ( "[NO_COMMITS_FOUND]\nNo commits found to generate a pre-release." ) assert expected_error_message in str(excinfo.value) @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_with_changelog_to_stdout_arg(mocker: MockFixture, capsys, changelog_path): create_file_and_commit("feat(user): this should appear in stdout") testargs = ["cz", "bump", "--yes", "--changelog-to-stdout"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert "this should appear in stdout" in out tag_exists = git.tag_exist("0.2.0") assert tag_exists is True with open(changelog_path, encoding="utf-8") as f: out = f.read() assert out.startswith("#") assert "0.2.0" in out @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_with_changelog_to_stdout_dry_run_arg( mocker: MockFixture, capsys, changelog_path ): create_file_and_commit( "feat(user): this should appear in stdout with dry-run enabled" ) testargs = ["cz", "bump", "--yes", "--changelog-to-stdout", "--dry-run"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(DryRunExit): cli.main() out, _ = capsys.readouterr() tag_exists = git.tag_exist("0.2.0") assert tag_exists is False assert out.startswith("#") assert "this should appear in stdout with dry-run enabled" in out assert "0.2.0" in out @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_without_git_to_stdout_arg(mocker: MockFixture, capsys, changelog_path): create_file_and_commit("feat(user): this should appear in stdout") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert ( re.search(r"^\[master \w+] bump: version 0.1.0 → 0.2.0", out, re.MULTILINE) is not None ) @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_with_git_to_stdout_arg(mocker: MockFixture, capsys, changelog_path): create_file_and_commit("feat(user): this should appear in stdout") testargs = ["cz", "bump", "--yes", "--git-output-to-stderr"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert ( re.search(r"^\[master \w+] bump: version 0.1.0 → 0.2.0", out, re.MULTILINE) is None ) @pytest.mark.parametrize( "version_filepath, version_regex, version_file_content", [ pytest.param( "pyproject.toml", "pyproject.toml:^version", """ [tool.poetry] name = "my_package" version = "0.1.0" """, id="version in pyproject.toml with regex", ), pytest.param( "pyproject.toml", "pyproject.toml", """ [tool.poetry] name = "my_package" version = "0.1.0" """, id="version in pyproject.toml without regex", ), pytest.param( "__init__.py", "__init__.py:^__version__", """ '''This is a test file.''' __version__ = "0.1.0" """, id="version in __init__.py with regex", ), pytest.param( "pyproject.toml", "*.toml:^version", """ [tool.poetry] name = "my_package" version = "0.1.0" """, id="version in pyproject.toml with glob and regex", ), ], ) @pytest.mark.parametrize( "cli_bump_changelog_args", [ ("cz", "bump", "--changelog", "--yes"), ( "cz", "bump", "--changelog", "--changelog-to-stdout", "--annotated-tag", "--check-consistency", "--yes", ), ], ids=lambda cmd_tuple: " ".join(cmd_tuple), ) def test_bump_changelog_command_commits_untracked_changelog_and_version_files( tmp_commitizen_project, mocker, cli_bump_changelog_args: tuple[str, ...], version_filepath: str, version_regex: str, version_file_content: str, ): """Ensure that changelog always gets committed, no matter what version file or cli options get passed. Steps: - Append the version file's name and regex commitizen configuration lines to `pyproject.toml`. - Append to or create the version file. - Add a commit of type fix to be eligible for a version bump. - Call commitizen main cli and assert that the `CHANGELOG.md` and the version file were committed. """ with tmp_commitizen_project.join("pyproject.toml").open( mode="a", encoding="utf-8", ) as commitizen_config: commitizen_config.write(f"version_files = [\n'{version_regex}'\n]") with tmp_commitizen_project.join(version_filepath).open( mode="a+", encoding="utf-8" ) as version_file: version_file.write(version_file_content) create_file_and_commit("fix: some test commit") mocker.patch.object(sys, "argv", cli_bump_changelog_args) cli.main() commit_file_names = git.get_filenames_in_commit() assert "CHANGELOG.md" in commit_file_names assert version_filepath in commit_file_names @pytest.mark.parametrize( "testargs", [ ["cz", "bump", "--local-version", "1.2.3"], ["cz", "bump", "--prerelease", "rc", "1.2.3"], ["cz", "bump", "--devrelease", "0", "1.2.3"], ["cz", "bump", "--devrelease", "1", "1.2.3"], ["cz", "bump", "--increment", "PATCH", "1.2.3"], ["cz", "bump", "--build-metadata=a.b.c", "1.2.3"], ["cz", "bump", "--local-version", "--build-metadata=a.b.c"], ], ) @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_invalid_manual_args_raises_exception(mocker, testargs): mocker.patch.object(sys, "argv", testargs) with pytest.raises(NotAllowed): cli.main() @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.parametrize( "manual_version", [ "noversion", "1.2..3", ], ) def test_bump_invalid_manual_version_raises_exception(mocker, manual_version): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", manual_version] mocker.patch.object(sys, "argv", testargs) with pytest.raises(InvalidManualVersion) as excinfo: cli.main() expected_error_message = ( f"[INVALID_MANUAL_VERSION]\nInvalid manual version: '{manual_version}'" ) assert expected_error_message in str(excinfo.value) @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.parametrize( "manual_version", [ "0.0.1", "0.1.0rc2", "0.1.0.dev2", "0.1.0+1.0.0", "0.1.0rc2.dev2+1.0.0", "0.1.1", "0.2.0", "1.0.0", ], ) def test_bump_manual_version(mocker, manual_version): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", manual_version] mocker.patch.object(sys, "argv", testargs) 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) @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) def test_bump_with_pre_bump_hooks( commit_msg, mocker: MockFixture, tmp_commitizen_project ): pre_bump_hook = "scripts/pre_bump_hook.sh" post_bump_hook = "scripts/post_bump_hook.sh" tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_commitizen_cfg_file.write( f"{tmp_commitizen_cfg_file.read()}\n" f'pre_bump_hooks = ["{pre_bump_hook}"]\n' f'post_bump_hooks = ["{post_bump_hook}"]\n' ) run_mock = mocker.Mock() mocker.patch.object(hooks, "run", run_mock) create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") assert tag_exists is True run_mock.assert_has_calls( [ call( [pre_bump_hook], _env_prefix="CZ_PRE_", is_initial=True, current_version="0.1.0", current_tag_version="0.1.0", new_version="0.2.0", new_tag_version="0.2.0", message="bump: version 0.1.0 → 0.2.0", increment="MINOR", changelog_file_name=None, ), call( [post_bump_hook], _env_prefix="CZ_POST_", was_initial=True, previous_version="0.1.0", previous_tag_version="0.1.0", current_version="0.2.0", current_tag_version="0.2.0", message="bump: version 0.1.0 → 0.2.0", increment="MINOR", changelog_file_name=None, ), ] ) def test_bump_with_hooks_and_increment(mocker: MockFixture, tmp_commitizen_project): pre_bump_hook = "scripts/pre_bump_hook.sh" post_bump_hook = "scripts/post_bump_hook.sh" tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_commitizen_cfg_file.write( f"{tmp_commitizen_cfg_file.read()}\n" f'pre_bump_hooks = ["{pre_bump_hook}"]\n' f'post_bump_hooks = ["{post_bump_hook}"]\n' ) run_mock = mocker.Mock() mocker.patch.object(hooks, "run", run_mock) create_file_and_commit("test: some test") testargs = ["cz", "bump", "--yes", "--increment", "MINOR"] 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_git_project") def test_bump_use_version_provider(mocker: MockFixture): mock = mocker.MagicMock(name="provider") mock.get_version.return_value = "0.0.0" get_provider = mocker.patch( "commitizen.commands.bump.get_provider", return_value=mock ) create_file_and_commit("fix: fake commit") testargs = ["cz", "bump", "--yes", "--changelog"] mocker.patch.object(sys, "argv", testargs) cli.main() assert git.tag_exist("0.0.1") get_provider.assert_called_once() mock.get_version.assert_called_once() mock.set_version.assert_called_once_with("0.0.1") def test_bump_command_prelease_scheme_via_cli( tmp_commitizen_project_initial, mocker: MockFixture ): tmp_commitizen_project = tmp_commitizen_project_initial() tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") testargs = [ "cz", "bump", "--prerelease", "alpha", "--yes", "--version-scheme", "semver", ] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0-a0") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0-a0" in f.read() # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0" in f.read() def test_bump_command_prelease_scheme_via_config( tmp_commitizen_project_initial, mocker: MockFixture ): tmp_commitizen_project = tmp_commitizen_project_initial( config_extra='version_scheme = "semver"\n', ) tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0-a0") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0-a0" in f.read() testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0-a1") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0-a1" in f.read() # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("0.2.0") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0" in f.read() def test_bump_command_prelease_scheme_check_old_tags( tmp_commitizen_project_initial, mocker: MockFixture ): tmp_commitizen_project = tmp_commitizen_project_initial( config_extra=('tag_format = "v$version"\nversion_scheme = "semver"\n'), ) tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("v0.2.0-a0") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0-a0" in f.read() testargs = ["cz", "bump", "--prerelease", "alpha"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("v0.2.0-a1") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0-a1" in f.read() # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE testargs = ["cz", "bump"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist("v0.2.0") assert tag_exists is True for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: with open(version_file) as f: assert "0.2.0" in f.read() @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.usefixtures("use_cz_semver") @pytest.mark.parametrize( "message, expected_tag", [ ("minor: add users", "0.2.0"), ("patch: bug affecting users", "0.1.1"), ("major: bug affecting users", "1.0.0"), ], ) def test_bump_with_plugin(mocker: MockFixture, message: str, expected_tag: str): create_file_and_commit(message) testargs = ["cz", "--name", "cz_semver", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist(expected_tag) assert tag_exists is True @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.usefixtures("use_cz_semver") @pytest.mark.parametrize( "message, expected_tag", [ ("minor: add users", "0.2.0"), ("patch: bug affecting users", "0.1.1"), ("major: bug affecting users", "0.2.0"), ], ) def test_bump_with_major_version_zero_with_plugin( mocker: MockFixture, message: str, expected_tag: str ): create_file_and_commit(message) testargs = ["cz", "--name", "cz_semver", "bump", "--yes", "--major-version-zero"] mocker.patch.object(sys, "argv", testargs) cli.main() tag_exists = git.tag_exist(expected_tag) assert tag_exists is True @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_command_version_type_deprecation(mocker: MockFixture): create_file_and_commit("feat: check deprecation on --version-type") testargs = [ "cz", "bump", "--prerelease", "alpha", "--yes", "--version-type", "semver", ] mocker.patch.object(sys, "argv", testargs) with pytest.warns(DeprecationWarning): cli.main() assert git.tag_exist("0.2.0-a0") @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_command_version_scheme_priority_over_version_type(mocker: MockFixture): create_file_and_commit("feat: check deprecation on --version-type") testargs = [ "cz", "bump", "--prerelease", "alpha", "--yes", "--version-type", "semver", "--version-scheme", "pep440", ] mocker.patch.object(sys, "argv", testargs) with pytest.warns(DeprecationWarning): cli.main() assert git.tag_exist("0.2.0a0") @pytest.mark.parametrize( "arg, cfg, expected", ( pytest.param("", "", "default", id="default"), pytest.param("", "changelog.cfg", "from config", id="from-config"), pytest.param( "--template=changelog.cmd", "changelog.cfg", "from cmd", id="from-command" ), ), ) def test_bump_template_option_precedance( mocker: MockFixture, tmp_commitizen_project: Path, any_changelog_format: ChangelogFormat, arg: str, cfg: str, expected: str, ): project_root = Path(tmp_commitizen_project) cfg_template = project_root / "changelog.cfg" cmd_template = project_root / "changelog.cmd" default_template = project_root / any_changelog_format.template changelog = project_root / any_changelog_format.default_changelog_file cfg_template.write_text("from config") cmd_template.write_text("from cmd") default_template.write_text("default") create_file_and_commit("feat: new file") if cfg: pyproject = project_root / "pyproject.toml" pyproject.write_text( dedent( f"""\ [tool.commitizen] version = "0.1.0" template = "{cfg}" """ ) ) testargs = ["cz", "bump", "--yes", "--changelog"] if arg: testargs.append(arg) mocker.patch.object(sys, "argv", testargs + ["0.1.1"]) cli.main() out = changelog.read_text() assert out == expected def test_bump_template_extras_precedance( mocker: MockFixture, tmp_commitizen_project: Path, any_changelog_format: ChangelogFormat, mock_plugin: BaseCommitizen, ): project_root = Path(tmp_commitizen_project) changelog_tpl = project_root / any_changelog_format.template changelog_tpl.write_text("{{first}} - {{second}} - {{third}}") mock_plugin.template_extras = dict( first="from-plugin", second="from-plugin", third="from-plugin" ) pyproject = project_root / "pyproject.toml" pyproject.write_text( dedent( """\ [tool.commitizen] version = "0.1.0" [tool.commitizen.extras] first = "from-config" second = "from-config" """ ) ) create_file_and_commit("feat: new file") testargs = [ "cz", "bump", "--yes", "--changelog", "--extra", "first=from-command", "0.1.1", ] mocker.patch.object(sys, "argv", testargs) cli.main() changelog = project_root / any_changelog_format.default_changelog_file assert changelog.read_text() == "from-command - from-config - from-plugin" def test_bump_template_extra_quotes( mocker: MockFixture, tmp_commitizen_project: Path, any_changelog_format: ChangelogFormat, ): project_root = Path(tmp_commitizen_project) changelog_tpl = project_root / any_changelog_format.template changelog_tpl.write_text("{{first}} - {{second}} - {{third}}") create_file_and_commit("feat: new file") testargs = [ "cz", "bump", "--changelog", "--yes", "-e", "first=no-quote", "-e", "second='single quotes'", "-e", 'third="double quotes"', "0.1.1", ] mocker.patch.object(sys, "argv", testargs) cli.main() changelog = project_root / any_changelog_format.default_changelog_file assert changelog.read_text() == "no-quote - single quotes - double quotes" def test_bump_changelog_contains_increment_only(mocker, tmp_commitizen_project, capsys): """Issue 1024""" # Initialize commitizen up to v1.0.0 project_root = Path(tmp_commitizen_project) tmp_commitizen_cfg_file = project_root / "pyproject.toml" tmp_commitizen_cfg_file.write_text( '[tool.commitizen]\nversion="1.0.0"\nupdate_changelog_on_bump = true\n' ) tmp_changelog_file = project_root / "CHANGELOG.md" tmp_changelog_file.write_text("## v1.0.0") create_file_and_commit("feat(user): new file") create_tag("v1.0.0") # Add a commit and bump to v2.0.0 create_file_and_commit("feat(user)!: new file") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() _ = capsys.readouterr() # Add a commit and create the incremental changelog to v3.0.0 # it should only include v3 changes create_file_and_commit("feat(next)!: next version") testargs = ["cz", "bump", "--yes", "--files-only", "--changelog-to-stdout"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(ExpectedExit): cli.main() out, _ = capsys.readouterr() assert "3.0.0" in out assert "2.0.0" not in out @skip_below_py_3_13 def test_bump_command_shows_description_when_use_help_option( mocker: MockFixture, capsys, file_regression ): testargs = ["cz", "bump", "--help"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(SystemExit): cli.main() out, _ = capsys.readouterr() file_regression.check(out, extension=".txt") @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next(mocker: MockFixture, capsys): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--get-next"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(GetNextExit): cli.main() out, _ = capsys.readouterr() assert "0.2.0" in out tag_exists = git.tag_exist("0.2.0") assert tag_exists is False @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next_update_changelog_on_bump( mocker: MockFixture, capsys, config_path ): create_file_and_commit("feat: new file") with open(config_path, "a", encoding="utf-8") as fp: fp.write("update_changelog_on_bump = true\n") testargs = ["cz", "bump", "--yes", "--get-next"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(GetNextExit): cli.main() out, _ = capsys.readouterr() assert "0.2.0" in out tag_exists = git.tag_exist("0.2.0") assert tag_exists is False @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next__changelog_is_not_allowed(mocker: MockFixture): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--get-next", "--changelog"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(NotAllowed): cli.main() @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next__changelog_to_stdout_is_not_allowed(mocker: MockFixture): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--get-next", "--changelog-to-stdout"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(NotAllowed): cli.main() @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next__manual_version_is_not_allowed(mocker: MockFixture): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--get-next", "0.2.1"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(NotAllowed): cli.main() @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_get_next__no_eligible_commits_raises(mocker: MockFixture): create_file_and_commit("chore: new commit") testargs = ["cz", "bump", "--yes", "--get-next"] mocker.patch.object(sys, "argv", testargs) with pytest.raises(NoneIncrementExit): cli.main() def test_bump_allow_no_commit_with_no_commit(mocker, tmp_commitizen_project, capsys): with tmp_commitizen_project.as_cwd(): # Create the first commit and bump to 1.0.0 create_file_and_commit("feat(user)!: new file") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() # Verify NoCommitsFoundError should be raised # when there's no new commit and "--allow-no-commit" is not set with pytest.raises(NoCommitsFoundError): testargs = ["cz", "bump"] mocker.patch.object(sys, "argv", testargs) cli.main() # bump to 1.0.1 with new commit when "--allow-no-commit" is set testargs = ["cz", "bump", "--allow-no-commit"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert "bump: version 1.0.0 → 1.0.1" in out def test_bump_allow_no_commit_with_no_eligible_commit( mocker, tmp_commitizen_project, capsys ): with tmp_commitizen_project.as_cwd(): # Create the first commit and bump to 1.0.0 create_file_and_commit("feat(user)!: new file") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() # Create a commit that is ineligible to bump create_file_and_commit("docs(bump): add description for allow no commit") # Verify NoneIncrementExit should be raised # when there's no eligible bumping commit and "--allow-no-commit" is not set with pytest.raises(NoneIncrementExit): testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() # bump to 1.0.1 with ineligible commit when "--allow-no-commit" is set testargs = ["cz", "bump", "--allow-no-commit"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert "bump: version 1.0.0 → 1.0.1" in out def test_bump_allow_no_commit_with_increment(mocker, tmp_commitizen_project, capsys): with tmp_commitizen_project.as_cwd(): # # Create the first commit and bump to 1.0.0 create_file_and_commit("feat(user)!: new file") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() # Verify NoCommitsFoundError should be raised # when there's no new commit and "--allow-no-commit" is not set with pytest.raises(NoCommitsFoundError): testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() # bump to 1.1.0 with no new commit when "--allow-no-commit" is set # and increment is specified testargs = ["cz", "bump", "--yes", "--allow-no-commit", "--increment", "MINOR"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert "bump: version 1.0.0 → 1.1.0" in out def test_bump_allow_no_commit_with_manual_version( mocker, tmp_commitizen_project, capsys ): with tmp_commitizen_project.as_cwd(): # # Create the first commit and bump to 1.0.0 create_file_and_commit("feat(user)!: new file") testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() # Verify NoCommitsFoundError should be raised # when there's no new commit and "--allow-no-commit" is not set with pytest.raises(NoCommitsFoundError): testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) cli.main() # bump to 1.1.0 with no new commit when "--allow-no-commit" is set # and increment is specified testargs = ["cz", "bump", "--yes", "--allow-no-commit", "2.0.0"] mocker.patch.object(sys, "argv", testargs) cli.main() out, _ = capsys.readouterr() assert "bump: version 1.0.0 → 2.0.0" in out def test_bump_detect_legacy_tags_from_scm( tmp_commitizen_project: py.path.local, mocker: MockFixture ): project_root = Path(tmp_commitizen_project) tmp_commitizen_cfg_file = project_root / "pyproject.toml" tmp_commitizen_cfg_file.write_text( "\n".join( [ "[tool.commitizen]", 'version_provider = "scm"', 'tag_format = "v$version"', "legacy_tag_formats = [", ' "legacy-${version}"', "]", ] ), ) create_file_and_commit("feat: new file") create_tag("legacy-0.4.2") create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--increment", "patch", "--changelog"] mocker.patch.object(sys, "argv", testargs) cli.main() assert git.tag_exist("v0.4.3")