Skip to content

Commit

Permalink
feat(bump): support prereleases with start offset
Browse files Browse the repository at this point in the history
In special cases, it may be necessary for prereleases not to start with a 0, because the individual characters are encoded in bytes. Here, a 0 in the byte is interpreted as a release version, consistent with the location of bugfixes. Therefore, this commit introduces a generic option to change the start of the prerelease to accommodate such circumstances.

EXAMPLES

Before:
0.3.0 -> PATCH beta-> 0.3.1b0

Now (with offset 0 == default)
0.3.0 -> PATCH beta-> 0.3.1b0

Now (with offset 1)
0.3.0 -> PATCH beta-> 0.3.1b1
  • Loading branch information
ckagerer authored and Lee-W committed Feb 11, 2023
1 parent 32c4f3e commit 583c7dd
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 77 deletions.
15 changes: 11 additions & 4 deletions commitizen/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def find_increment(
return increment


def prerelease_generator(current_version: str, prerelease: Optional[str] = None) -> str:
def prerelease_generator(
current_version: str, prerelease: Optional[str] = None, offset: int = 0
) -> str:
"""Generate prerelease
X.YaN # Alpha release
Expand All @@ -65,7 +67,7 @@ def prerelease_generator(current_version: str, prerelease: Optional[str] = None)
prev_prerelease: int = version.pre[1]
new_prerelease_number = prev_prerelease + 1
else:
new_prerelease_number = 0
new_prerelease_number = offset
pre_version = f"{prerelease}{new_prerelease_number}"
return pre_version

Expand Down Expand Up @@ -115,6 +117,7 @@ def generate_version(
current_version: str,
increment: str,
prerelease: Optional[str] = None,
prerelease_offset: int = 0,
devrelease: Optional[int] = None,
is_local_version: bool = False,
) -> Version:
Expand All @@ -132,13 +135,17 @@ def generate_version(
if is_local_version:
version = Version(current_version)
dev_version = devrelease_generator(devrelease=devrelease)
pre_version = prerelease_generator(str(version.local), prerelease=prerelease)
pre_version = prerelease_generator(
str(version.local), prerelease=prerelease, offset=prerelease_offset
)
semver = semver_generator(str(version.local), increment=increment)

return Version(f"{version.public}+{semver}{pre_version}{dev_version}")
else:
dev_version = devrelease_generator(devrelease=devrelease)
pre_version = prerelease_generator(current_version, prerelease=prerelease)
pre_version = prerelease_generator(
current_version, prerelease=prerelease, offset=prerelease_offset
)
semver = semver_generator(current_version, increment=increment)

# TODO: post version
Expand Down
6 changes: 6 additions & 0 deletions commitizen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@
"default": None,
"help": "keep major version at zero, even for breaking changes",
},
{
"name": ["--prerelease-offset"],
"type": int,
"default": None,
"help": "start pre-releases with this offset",
},
{
"name": "manual_version",
"type": str,
Expand Down
8 changes: 8 additions & 0 deletions commitizen/commands/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
"gpg_sign",
"annotated_tag",
"major_version_zero",
"prerelease_offset",
]
if arguments[key] is not None
},
Expand Down Expand Up @@ -105,6 +106,7 @@ def __call__(self): # noqa: C901
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"]
prerelease_offset: int = self.bump_settings["prerelease_offset"]

dry_run: bool = self.arguments["dry_run"]
is_yes: bool = self.arguments["yes"]
Expand Down Expand Up @@ -135,6 +137,11 @@ def __call__(self): # noqa: C901
"--major-version-zero cannot be combined with MANUAL_VERSION"
)

if prerelease_offset:
raise NotAllowed(
"--prerelease-offset cannot be combined with MANUAL_VERSION"
)

if major_version_zero:
if not current_version.startswith("0."):
raise NotAllowed(
Expand Down Expand Up @@ -198,6 +205,7 @@ def __call__(self): # noqa: C901
current_version,
increment,
prerelease=prerelease,
prerelease_offset=prerelease_offset,
devrelease=devrelease,
is_local_version=is_local_version,
)
Expand Down
2 changes: 2 additions & 0 deletions commitizen/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Settings(TypedDict, total=False):
major_version_zero: bool
pre_bump_hooks: Optional[List[str]]
post_bump_hooks: Optional[List[str]]
prerelease_offset: int


name: str = "cz_conventional_commits"
Expand Down Expand Up @@ -69,6 +70,7 @@ class Settings(TypedDict, total=False):
"major_version_zero": False,
"pre_bump_hooks": [],
"post_bump_hooks": [],
"prerelease_offset": 0,
}

MAJOR = "MAJOR"
Expand Down
12 changes: 11 additions & 1 deletion docs/bump.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ options:
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
--prerelease-offset start pre-releases with this offset
```
### `--files-only`
Expand Down Expand Up @@ -304,7 +305,7 @@ The variables must be preceded by a `$` sign.
Supported variables:
| Variable | Description |
| ------------- | --------------------------------------------|
| ------------- | ------------------------------------------- |
| `$version` | full generated version |
| `$major` | MAJOR increment |
| `$minor` | MINOR increment |
Expand Down Expand Up @@ -464,6 +465,15 @@ release. During execution of the script, some environment variables are availabl
post_bump_hooks = [
"scripts/slack_notification.sh"
]
### `prerelease_offset`
Offset with which to start counting prereleses.
Defaults to: `0`
```toml
[tool.commitizen]
prerelease_offset = 1
```
## Custom bump
Expand Down
38 changes: 20 additions & 18 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@

## Settings

| Variable | Type | Default | Description |
| -------------------------- | ------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | `str` | `"cz_conventional_commits"` | Name of the committing rules to use |
| `version` | `str` | `None` | Current version. Example: "0.1.2" |
| `version_files` | `list` | `[ ]` | Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [See more][version_files] |
| `tag_format` | `str` | `None` | Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [See more][tag_format] |
| `update_changelog_on_bump` | `bool` | `false` | Create changelog when running `cz bump` |
| `gpg_sign` | `bool` | `false` | Use gpg signed tags instead of lightweight tags. |
| `annotated_tag` | `bool` | `false` | Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] |
| `bump_message` | `str` | `None` | Create custom commit message, useful to skip ci. [See more][bump_message] |
| `allow_abort` | `bool` | `false` | Disallow empty commit messages, useful in ci. [See more][allow_abort] |
| `changelog_file` | `str` | `CHANGELOG.md` | filename of exported changelog |
| `changelog_incremental` | `bool` | `false` | Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true` |
| `changelog_start_rev` | `str` | `None` | Start from a given git rev to generate the changelog |
| `style` | `list` | see above | Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] |
| `customize` | `dict` | `None` | **This is only supported when config through `toml`.** Custom rules for committing and bumping. [See more][customization] |
| `use_shortcuts` | `bool` | `false` | If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [See more][shortcuts] |
| `major_version_zero` | `bool` | `false` | When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero] |
| Variable | Type | Default | Description |
| -------------------------- | ------ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | `str` | `"cz_conventional_commits"` | Name of the committing rules to use |
| `version` | `str` | `None` | Current version. Example: "0.1.2" |
| `version_files` | `list` | `[ ]` | Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [See more][version_files] |
| `tag_format` | `str` | `None` | Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [See more][tag_format] |
| `update_changelog_on_bump` | `bool` | `false` | Create changelog when running `cz bump` |
| `gpg_sign` | `bool` | `false` | Use gpg signed tags instead of lightweight tags. |
| `annotated_tag` | `bool` | `false` | Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] |
| `bump_message` | `str` | `None` | Create custom commit message, useful to skip ci. [See more][bump_message] |
| `allow_abort` | `bool` | `false` | Disallow empty commit messages, useful in ci. [See more][allow_abort] |
| `changelog_file` | `str` | `CHANGELOG.md` | filename of exported changelog |
| `changelog_incremental` | `bool` | `false` | Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true` |
| `changelog_start_rev` | `str` | `None` | Start from a given git rev to generate the changelog |
| `style` | `list` | see above | Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] |
| `customize` | `dict` | `None` | **This is only supported when config through `toml`.** Custom rules for committing and bumping. [See more][customization] |
| `use_shortcuts` | `bool` | `false` | If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [See more][shortcuts] |
| `major_version_zero` | `bool` | `false` | When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero] |
| `prerelease_offset` | `int` | `0` | In special cases it may be necessary that a prerelease cannot start with a 0, e.g. in an embedded project the individual characters are encoded in bytes. This can be done by specifying an offset from which to start counting. [prerelease-offset] |

## pyproject.toml or .cz.toml

Expand Down Expand Up @@ -115,6 +116,7 @@ commitizen:
[tag_format]: bump.md#tag_format
[bump_message]: bump.md#bump_message
[major-version-zero]: bump.md#-major-version-zero
[prerelease-offset]: bump.md#-prerelease_offset
[allow_abort]: check.md#allow-abort
[additional-features]: https://github.com/tmbo/questionary#additional-features
[customization]: customization.md
Expand Down
17 changes: 17 additions & 0 deletions tests/commands/test_bump_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,3 +814,20 @@ def test_bump_with_pre_bump_hooks(
),
]
)


@pytest.mark.usefixtures("tmp_commitizen_project")
def test_bump_manual_version_disallows_prerelease_offset(mocker):
create_file_and_commit("feat: new file")

manual_version = "0.2.0"
testargs = ["cz", "bump", "--yes", "--prerelease-offset", "42", manual_version]
mocker.patch.object(sys, "argv", testargs)

with pytest.raises(NotAllowed) as excinfo:
cli.main()

expected_error_message = (
"--prerelease-offset cannot be combined with MANUAL_VERSION"
)
assert expected_error_message in str(excinfo.value)
Loading

0 comments on commit 583c7dd

Please sign in to comment.