Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions commitizen/version_schemes.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,7 +310,7 @@ class SemVer(BaseVersion):
"""
Semantic Versioning (SemVer) scheme
See: https://semver.org/
See: https://semver.org/spec/v1.0.0.html
"""

def__str__(self) ->str:
Expand All@@ -324,9 +324,8 @@ def __str__(self) -> str:
parts.append(".".join(str(x) forxinself.release))

# Pre-release
ifself.pre:
pre="".join(str(x) forxinself.pre)
parts.append(f"-{pre}")
ifself.prerelease:
parts.append(f"-{self.prerelease}")

# Post-release
ifself.postisnotNone:
Expand All@@ -343,6 +342,60 @@ def __str__(self) -> str:
return"".join(parts)


classSemVer2(SemVer):
"""
Semantic Versioning 2.0 (SemVer2) schema
See: https://semver.org/spec/v2.0.0.html
"""

_STD_PRELEASES={
"a": "alpha",
"b": "beta",
}

@property
defprerelease(self) ->str|None:
ifself.is_prereleaseandself.pre:
prerelease_type=self._STD_PRELEASES.get(self.pre[0], self.pre[0])
returnf"{prerelease_type}.{self.pre[1]}"
returnNone

def__str__(self) ->str:
parts= []

# Epoch
ifself.epoch!=0:
parts.append(f"{self.epoch}!")

# Release segment
parts.append(".".join(str(x) forxinself.release))

# Pre-release identifiers
# See: https://semver.org/spec/v2.0.0.html#spec-item-9
prerelease_parts= []
ifself.prerelease:
prerelease_parts.append(f"{self.prerelease}")

# Post-release
ifself.postisnotNone:
prerelease_parts.append(f"post.{self.post}")

# Development release
ifself.devisnotNone:
prerelease_parts.append(f"dev.{self.dev}")

ifprerelease_parts:
parts.append("-")
parts.append(".".join(prerelease_parts))

# Local version segment
ifself.local:
parts.append(f"+{self.local}")

return"".join(parts)


DEFAULT_SCHEME: VersionScheme=Pep440

SCHEMES_ENTRYPOINT="commitizen.scheme"
Expand Down
20 changes: 10 additions & 10 deletions docs/bump.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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}] [--build-metadata BUILD_METADATA]
[--prerelease-offset PRERELEASE_OFFSET] [--version-scheme{pep440,semver,semver2}] [--version-type{pep440,semver,semver2}] [--build-metadata BUILD_METADATA]
[MANUAL_VERSION]

positional arguments:
Expand DownExpand Up@@ -97,9 +97,9 @@ options:
--major-version-zero keep major version at zero, even for breaking changes
--prerelease-offset PRERELEASE_OFFSET
start pre-releases with this offset
--version-scheme{semver,pep440}
--version-scheme{pep440,semver,semver2}
choose version scheme
--version-type{semver,pep440}
--version-type{pep440,semver,semver2}
Deprecated, use --version-scheme
--build-metadata{BUILD_METADATA}
additional metadata in the version string
Expand DownExpand Up@@ -619,14 +619,14 @@ prerelease_offset = 1
Choose version scheme
| schemes | pep440 | semver |
| -------------- | -------------- | --------------- |
| non-prerelease | `0.1.0` | `0.1.0` |
| prerelease | `0.3.1a0` | `0.3.1-a0` |
| devrelease | `0.1.1.dev1` | `0.1.1-dev1` |
| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` |
| schemes | pep440 | semver | semver2 |
| -------------- | -------------- | --------------- | --------------------- |
| non-prerelease | `0.1.0` | `0.1.0` | `0.1.0` |
| prerelease | `0.3.1a0` | `0.3.1-a0` | `0.3.1-alpha.0` |
| devrelease | `0.1.1.dev1` | `0.1.1-dev1` | `0.1.1-dev.1` |
| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | `1.0.0-alpha.3.dev.1` |
Options: `semver`, `pep440`
Options: `pep440`, `semver`, `semver2`
Defaults to: `pep440`
Expand Down
3 changes: 2 additions & 1 deletion docs/config.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,7 +40,8 @@ Type: `str`

Default: `pep440`

Select a version scheme from the following options [`pep440`, `semver`]. Useful for non-python projects. [Read more][version-scheme]
Select a version scheme from the following options [`pep440`, `semver`, `semver2`].
Useful for non-python projects. [Read more][version-scheme]

### `tag_format`

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -103,6 +103,7 @@ scm = "commitizen.providers:ScmProvider"
[tool.poetry.plugins."commitizen.scheme"]
pep440 = "commitizen.version_schemes:Pep440"
semver = "commitizen.version_schemes:SemVer"
semver2 = "commitizen.version_schemes:SemVer2"

[tool.coverage]
[tool.coverage.report]
Expand Down
211 changes: 211 additions & 0 deletions tests/test_version_scheme_semver2.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
importitertools
importrandom

importpytest

fromcommitizen.version_schemesimportSemVer2, VersionProtocol

simple_flow= [
(("0.1.0", "PATCH", None, 0, None), "0.1.1"),
(("0.1.0", "PATCH", None, 0, 1), "0.1.1-dev.1"),
(("0.1.1", "MINOR", None, 0, None), "0.2.0"),
(("0.2.0", "MINOR", None, 0, None), "0.3.0"),
(("0.2.0", "MINOR", None, 0, 1), "0.3.0-dev.1"),
(("0.3.0", "PATCH", None, 0, None), "0.3.1"),
(("0.3.0", "PATCH", "alpha", 0, None), "0.3.1-alpha.0"),
(("0.3.1-alpha.0", None, "alpha", 0, None), "0.3.1-alpha.1"),
(("0.3.0", "PATCH", "alpha", 1, None), "0.3.1-alpha.1"),
(("0.3.1-alpha.0", None, "alpha", 1, None), "0.3.1-alpha.1"),
(("0.3.1-alpha.0", None, None, 0, None), "0.3.1"),
(("0.3.1", "PATCH", None, 0, None), "0.3.2"),
(("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"),
(("1.0.0-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"),
(("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"),
(("1.0.0-alpha.1", None, "alpha", 0, 1), "1.0.0-alpha.2.dev.1"),
(("1.0.0-alpha.2.dev.0", None, "alpha", 0, 1), "1.0.0-alpha.3.dev.1"),
(("1.0.0-alpha.2.dev.0", None, "alpha", 0, 0), "1.0.0-alpha.3.dev.0"),
(("1.0.0-alpha.1", None, "beta", 0, None), "1.0.0-beta.0"),
(("1.0.0-beta.0", None, "beta", 0, None), "1.0.0-beta.1"),
(("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"),
(("1.0.0-rc.0", None, "rc", 0, None), "1.0.0-rc.1"),
(("1.0.0-rc.0", None, "rc", 0, 1), "1.0.0-rc.1.dev.1"),
(("1.0.0-rc.0", "PATCH", None, 0, None), "1.0.0"),
(("1.0.0-alpha.3.dev.0", None, "beta", 0, None), "1.0.0-beta.0"),
(("1.0.0", "PATCH", None, 0, None), "1.0.1"),
(("1.0.1", "PATCH", None, 0, None), "1.0.2"),
(("1.0.2", "MINOR", None, 0, None), "1.1.0"),
(("1.1.0", "MINOR", None, 0, None), "1.2.0"),
(("1.2.0", "PATCH", None, 0, None), "1.2.1"),
(("1.2.1", "MAJOR", None, 0, None), "2.0.0"),
]

local_versions= [
(("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"),
(("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"),
(("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"),
]

# never bump backwards on pre-releases
linear_prerelease_cases= [
(("0.1.1-beta.1", None, "alpha", 0, None), "0.1.1-beta.2"),
(("0.1.1-rc.0", None, "alpha", 0, None), "0.1.1-rc.1"),
(("0.1.1-rc.0", None, "beta", 0, None), "0.1.1-rc.1"),
]

weird_cases= [
(("1.1", "PATCH", None, 0, None), "1.1.1"),
(("1", "MINOR", None, 0, None), "1.1.0"),
(("1", "MAJOR", None, 0, None), "2.0.0"),
(("1-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"),
(("1-alpha.0", None, "alpha", 1, None), "1.0.0-alpha.1"),
(("1", None, "beta", 0, None), "1.0.0-beta.0"),
(("1", None, "beta", 1, None), "1.0.0-beta.1"),
(("1-beta", None, "beta", 0, None), "1.0.0-beta.1"),
(("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"),
(("1", None, "rc", 0, None), "1.0.0-rc.0"),
(("1.0.0-rc.1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"),
]

# test driven development
tdd_cases= [
(("0.1.1", "PATCH", None, 0, None), "0.1.2"),
(("0.1.1", "MINOR", None, 0, None), "0.2.0"),
(("2.1.1", "MAJOR", None, 0, None), "3.0.0"),
(("0.9.0", "PATCH", "alpha", 0, None), "0.9.1-alpha.0"),
(("0.9.0", "MINOR", "alpha", 0, None), "0.10.0-alpha.0"),
(("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"),
(("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0-alpha.1"),
(("1.0.0-alpha.2", None, "beta", 0, None), "1.0.0-beta.0"),
(("1.0.0-alpha.2", None, "beta", 1, None), "1.0.0-beta.1"),
(("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"),
(("1.0.0-rc.1", None, "rc", 0, None), "1.0.0-rc.2"),
(("1.0.0-alpha.0", None, "rc", 0, None), "1.0.0-rc.0"),
(("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"),
]


@pytest.mark.parametrize(
"test_input, expected",
itertools.chain(tdd_cases, weird_cases, simple_flow, linear_prerelease_cases),
)
deftest_bump_semver_version(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(
SemVer2(current_version).bump(
increment=increment,
prerelease=prerelease,
prerelease_offset=prerelease_offset,
devrelease=devrelease,
)
)
==expected
)


@pytest.mark.parametrize("test_input,expected", local_versions)
deftest_bump_semver_version_local(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]
is_local_version=True
assert (
str(
SemVer2(current_version).bump(
increment=increment,
prerelease=prerelease,
prerelease_offset=prerelease_offset,
devrelease=devrelease,
is_local_version=is_local_version,
)
)
==expected
)


deftest_semver_scheme_property():
version=SemVer2("0.0.1")
assertversion.schemeisSemVer2


deftest_semver_implement_version_protocol():
assertisinstance(SemVer2("0.0.1"), VersionProtocol)


deftest_semver_sortable():
test_input= [x[0][0] forxinsimple_flow]
test_input.extend([x[1] forxinsimple_flow])
# randomize
random_input= [SemVer2(x) forxinrandom.sample(test_input, len(test_input))]
assertlen(random_input) ==len(test_input)
sorted_result= [str(x) forxinsorted(random_input)]
assertsorted_result== [
"0.1.0",
"0.1.0",
"0.1.1-dev.1",
"0.1.1",
"0.1.1",
"0.2.0",
"0.2.0",
"0.2.0",
"0.3.0-dev.1",
"0.3.0",
"0.3.0",
"0.3.0",
"0.3.0",
"0.3.1-alpha.0",
"0.3.1-alpha.0",
"0.3.1-alpha.0",
"0.3.1-alpha.0",
"0.3.1-alpha.1",
"0.3.1-alpha.1",
"0.3.1-alpha.1",
"0.3.1",
"0.3.1",
"0.3.1",
"0.3.2",
"0.4.2",
"1.0.0-alpha.0",
"1.0.0-alpha.0",
"1.0.0-alpha.1",
"1.0.0-alpha.1",
"1.0.0-alpha.1",
"1.0.0-alpha.1",
"1.0.0-alpha.2.dev.0",
"1.0.0-alpha.2.dev.0",
"1.0.0-alpha.2.dev.1",
"1.0.0-alpha.2",
"1.0.0-alpha.3.dev.0",
"1.0.0-alpha.3.dev.0",
"1.0.0-alpha.3.dev.1",
"1.0.0-beta.0",
"1.0.0-beta.0",
"1.0.0-beta.0",
"1.0.0-beta.1",
"1.0.0-beta.1",
"1.0.0-rc.0",
"1.0.0-rc.0",
"1.0.0-rc.0",
"1.0.0-rc.0",
"1.0.0-rc.1.dev.1",
"1.0.0-rc.1",
"1.0.0",
"1.0.0",
"1.0.1",
"1.0.1",
"1.0.2",
"1.0.2",
"1.1.0",
"1.1.0",
"1.2.0",
"1.2.0",
"1.2.1",
"1.2.1",
"2.0.0",
]