Skip to content

Commit

Permalink
feat(commands): add bump --exact
Browse files Browse the repository at this point in the history
When bumping a prerelease to a new prerelease, honor the detected increment
and preserve the prerelease suffix, rather than bumping to the next
non-prerelease version
  • Loading branch information
chadrik committed Feb 5, 2024
1 parent 12b7158 commit 66896bc
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 24 deletions.
9 changes: 9 additions & 0 deletions commitizen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,15 @@ def __call__(
"choices": ["MAJOR", "MINOR", "PATCH"],
"type": str.upper,
},
{
"name": ["--exact"],
"action": "store_true",
"help": (
"treat the increment and prerelease arguments "
"explicitly. Disables logic that attempts to deduce "
"the correct increment when a prelease suffix is present."
),
},
{
"name": ["--check-consistency", "-cc"],
"help": (
Expand Down
6 changes: 6 additions & 0 deletions commitizen/commands/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
"tag_format",
"prerelease",
"increment",
"exact",
"bump_message",
"gpg_sign",
"annotated_tag",
Expand Down Expand Up @@ -157,6 +158,7 @@ def __call__(self) -> None: # noqa: C901
is_files_only: bool | None = self.arguments["files_only"]
is_local_version: bool = self.arguments["local_version"]
manual_version = self.arguments["manual_version"]
exact_mode: bool = self.arguments["exact"]

if manual_version:
if increment:
Expand All @@ -183,6 +185,9 @@ def __call__(self) -> None: # noqa: C901
"--prerelease-offset cannot be combined with MANUAL_VERSION"
)

if not prerelease and exact_mode:
raise NotAllowed("--exact is only valid with --prerelease")

Check warning on line 189 in commitizen/commands/bump.py

View check run for this annotation

Codecov / codecov/patch

commitizen/commands/bump.py#L189

Added line #L189 was not covered by tests

if major_version_zero:
if not current_version.release[0] == 0:
raise NotAllowed(
Expand Down Expand Up @@ -237,6 +242,7 @@ def __call__(self) -> None: # noqa: C901
prerelease_offset=prerelease_offset,
devrelease=devrelease,
is_local_version=is_local_version,
exact_mode=exact_mode,
)

new_tag_version = bump.normalize_tag(
Expand Down
13 changes: 10 additions & 3 deletions commitizen/version_schemes.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,17 @@ def bump(
prerelease_offset: int = 0,
devrelease: int | None = None,
is_local_version: bool = False,
force_bump: bool = False,
exact_mode: bool = False,
) -> Self:
"""
Based on the given increment, generate the next bumped version according to the version scheme
Args:
increment: The component to increase
prerelease: The type of prerelease, if Any
is_local_version: Whether to increment the local version instead
exact_mode: Treat the increment and prerelease arguments explicitly. Disables logic
that attempts to deduce the correct increment when a prelease suffix is present.
"""


Expand Down Expand Up @@ -226,7 +233,7 @@ def bump(
prerelease_offset: int = 0,
devrelease: int | None = None,
is_local_version: bool = False,
force_bump: bool = False,
exact_mode: bool = False,
) -> Self:
"""Based on the given increment a proper semver will be generated.
Expand All @@ -246,7 +253,7 @@ def bump(
else:
if not self.is_prerelease:
base = self.increment_base(increment)
elif force_bump:
elif exact_mode:
base = self.increment_base(increment)
else:
base = f"{self.major}.{self.minor}.{self.micro}"
Expand Down
49 changes: 49 additions & 0 deletions tests/commands/test_bump_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,55 @@ def test_bump_command_prelease_increment(mocker: MockFixture):
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", "--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
# --exact allows the minor version to bump, and restart the prerelease
create_file_and_commit("feat: location")

testargs = ["cz", "bump", "--prerelease", "alpha", "--yes", "--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
# --exact allows the major version to bump, and restart the prerelease
testargs = [
"cz",
"bump",
"--prerelease",
"alpha",
"--yes",
"--increment=MAJOR",
"--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"""
Expand Down
65 changes: 44 additions & 21 deletions tests/test_version_scheme_pep440.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,27 +141,29 @@
(("3.1.4a0", "MAJOR", "alpha", 0, None), "4.0.0a0"),
]


# test driven development
sortability = [
"0.10.0a0",
"0.1.1",
"0.1.2",
"2.1.1",
"3.0.0",
"0.9.1a0",
"1.0.0a1",
"1.0.0b1",
"1.0.0a1",
"1.0.0a2.dev1",
"1.0.0rc2",
"1.0.0a3.dev0",
"1.0.0a2.dev0",
"1.0.0a3.dev1",
"1.0.0a2.dev0",
"1.0.0b0",
"1.0.0rc0",
"1.0.0rc1",
excact_cases = [
(("1.0.0", "PATCH", None, 0, None), "1.0.1"),
(("1.0.0", "MINOR", None, 0, None), "1.1.0"),
# with exact_mode=False: "1.0.0b0"
(("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1b0"),
# with exact_mode=False: "1.0.0b1"
(("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1b0"),
# with exact_mode=False: "1.0.0rc0"
(("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1rc0"),
# with exact_mode=False: "1.0.0-rc1"
(("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1rc0"),
# with exact_mode=False: "1.0.0rc1-dev1"
(("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1rc0.dev1"),
# with exact_mode=False: "1.0.0b0"
(("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0b0"),
# with exact_mode=False: "1.0.0b1"
(("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0b0"),
# with exact_mode=False: "1.0.0rc0"
(("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0rc0"),
# with exact_mode=False: "1.0.0rc1"
(("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0rc0"),
# with exact_mode=False: "1.0.0rc1-dev1"
(("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0rc0.dev1"),
]


Expand Down Expand Up @@ -194,6 +196,27 @@ def test_bump_pep440_version(test_input, expected):
)


@pytest.mark.parametrize("test_input, expected", excact_cases)
def test_bump_pep440_version_force(test_input, expected):
current_version = test_input[0]
increment = test_input[1]
prerelease = test_input[2]
prerelease_offset = test_input[3]
devrelease = test_input[4]
assert (
str(
Pep440(current_version).bump(
increment=increment,
prerelease=prerelease,
prerelease_offset=prerelease_offset,
devrelease=devrelease,
exact_mode=True,
)
)
== expected
)


@pytest.mark.parametrize("test_input,expected", local_versions)
def test_bump_pep440_version_local(test_input, expected):
current_version = test_input[0]
Expand Down
46 changes: 46 additions & 0 deletions tests/test_version_scheme_semver.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,31 @@
(("1.0.0-alpha1", None, "alpha", 0, None), "1.0.0-a2"),
]

excact_cases = [
(("1.0.0", "PATCH", None, 0, None), "1.0.1"),
(("1.0.0", "MINOR", None, 0, None), "1.1.0"),
# with exact_mode=False: "1.0.0-b0"
(("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1-b0"),
# with exact_mode=False: "1.0.0-b1"
(("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1-b0"),
# with exact_mode=False: "1.0.0-rc0"
(("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1-rc0"),
# with exact_mode=False: "1.0.0-rc1"
(("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1-rc0"),
# with exact_mode=False: "1.0.0-rc1-dev1"
(("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1-rc0-dev1"),
# with exact_mode=False: "1.0.0-b0"
(("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0-b0"),
# with exact_mode=False: "1.0.0-b1"
(("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0-b0"),
# with exact_mode=False: "1.0.0-rc0"
(("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0-rc0"),
# with exact_mode=False: "1.0.0-rc1"
(("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0-rc0"),
# with exact_mode=False: "1.0.0-rc1-dev1"
(("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0-rc0-dev1"),
]


@pytest.mark.parametrize(
"test_input, expected",
Expand All @@ -107,6 +132,27 @@ def test_bump_semver_version(test_input, expected):
)


@pytest.mark.parametrize("test_input, expected", excact_cases)
def test_bump_semver_version_force(test_input, expected):
current_version = test_input[0]
increment = test_input[1]
prerelease = test_input[2]
prerelease_offset = test_input[3]
devrelease = test_input[4]
assert (
str(
SemVer(current_version).bump(
increment=increment,
prerelease=prerelease,
prerelease_offset=prerelease_offset,
devrelease=devrelease,
exact_mode=True,
)
)
== expected
)


@pytest.mark.parametrize("test_input,expected", local_versions)
def test_bump_semver_version_local(test_input, expected):
current_version = test_input[0]
Expand Down

0 comments on commit 66896bc

Please sign in to comment.