diff --git a/.github/.codecov.yml b/.github/.codecov.yml new file mode 100644 index 0000000000..85b43f3fef --- /dev/null +++ b/.github/.codecov.yml @@ -0,0 +1,7 @@ +coverage: + status: + project: + default: + # minimum of 97% (real 96%) + target: 97% + threshold: 1% diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 322bd0d92d..1ea3e8482b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - id: no-commit-to-branch - repo: https://github.com/commitizen-tools/commitizen - rev: v3.1.1 # automatically updated by Commitizen + rev: v3.2.0 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e765c9c22..48aa326456 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,24 @@ +## v3.2.0 (2023-05-01) + +### Feat + +- **hooks**: add prepare-commit-msg and post-commit hooks +- **commit**: add --write-message-to-file option + +### Fix + +- **bump**: better match for change_type when finding increment +- **changelog**: breaking change on additional types for conventional commits +- **bump**: breaking changes on additional types for conventional commits +- improve errors message when empty .cz.json found +- **init**: poetry detection +- bump decli which is type hinted + +### Refactor + +- **commit**: change type of write_message_to_file to path + ## v3.1.1 (2023-04-28) ### Fix diff --git a/commitizen/__init__.py b/commitizen/__init__.py index 6db9e6e7db..f16def4441 100644 --- a/commitizen/__init__.py +++ b/commitizen/__init__.py @@ -1,7 +1,7 @@ import logging import logging.config -from colorama import init +from colorama import init # type: ignore from commitizen.cz.base import BaseCommitizen diff --git a/commitizen/__version__.py b/commitizen/__version__.py index d539d50ceb..11731085c0 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "3.1.1" +__version__ = "3.2.0" diff --git a/commitizen/bump.py b/commitizen/bump.py index c405414e87..ed410bbcc1 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -23,7 +23,6 @@ def find_increment( commits: List[GitCommit], regex: str, increments_map: Union[dict, OrderedDict] ) -> Optional[str]: - if isinstance(increments_map, dict): increments_map = OrderedDict(increments_map) @@ -35,8 +34,9 @@ def find_increment( for commit in commits: for message in commit.message.split("\n"): result = select_pattern.search(message) + if result: - found_keyword = result.group(0) + found_keyword = result.group(1) new_increment = None for match_pattern in increments_map.keys(): if re.match(match_pattern, found_keyword): @@ -44,7 +44,7 @@ def find_increment( break if increment == "MAJOR": - continue + break elif increment == "MINOR" and new_increment == "MAJOR": increment = new_increment elif increment == "PATCH" or increment is None: @@ -103,7 +103,6 @@ def semver_generator(current_version: str, increment: str = None) -> str: # so it doesn't matter the increment. # Example: 1.0.0a0 with PATCH/MINOR -> 1.0.0 if not version.is_prerelease: - if increment == MAJOR: increments_version[MAJOR] += 1 increments_version[MINOR] = 0 diff --git a/commitizen/cli.py b/commitizen/cli.py index b59c257db3..ed89b5675a 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -1,6 +1,7 @@ import argparse import logging import sys +from pathlib import Path from functools import partial from types import TracebackType from typing import List @@ -62,10 +63,16 @@ "action": "store_true", "help": "show output to stdout, no commit, no modified files", }, + { + "name": "--write-message-to-file", + "type": Path, + "metavar": "FILE_PATH", + "help": "write message to file before commiting (can be combined with --dry-run)", + }, { "name": ["-s", "--signoff"], "action": "store_true", - "help": "Sign off the commit", + "help": "sign off the commit", }, ], }, diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 13e61abe6d..4d9da5c3fa 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -14,6 +14,7 @@ NoAnswersError, NoCommitBackupError, NotAGitProjectError, + NotAllowed, NothingToCommitError, ) from commitizen.git import smart_open @@ -63,10 +64,14 @@ def prompt_commit_questions(self) -> str: def __call__(self): dry_run: bool = self.arguments.get("dry_run") + write_message_to_file = self.arguments.get("write_message_to_file") if git.is_staging_clean() and not dry_run: raise NothingToCommitError("No files added to staging!") + if write_message_to_file is not None and write_message_to_file.is_dir(): + raise NotAllowed(f"{write_message_to_file} is a directory") + retry: bool = self.arguments.get("retry") if retry: @@ -76,6 +81,10 @@ def __call__(self): out.info(f"\n{m}\n") + if write_message_to_file: + with smart_open(write_message_to_file, "w") as file: + file.write(m) + if dry_run: raise DryRunExit() diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index fe6167b7f7..08fdadef77 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -36,7 +36,7 @@ def is_python_poetry(self) -> bool: if not self.has_pyproject: return False with open("pyproject.toml") as f: - return "tool.poetry.version" in f.read() + return "[tool.poetry]" in f.read() @property def is_python(self) -> bool: diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py index 34a81a9ef7..29d76040f1 100644 --- a/commitizen/config/json_config.py +++ b/commitizen/config/json_config.py @@ -1,6 +1,7 @@ import json from pathlib import Path from typing import Union +from commitizen.exceptions import InvalidConfigurationError from commitizen.git import smart_open @@ -11,8 +12,8 @@ class JsonConfig(BaseConfig): def __init__(self, *, data: Union[bytes, str], path: Union[Path, str]): super(JsonConfig, self).__init__() self.is_empty_config = False - self._parse_setting(data) self.add_path(path) + self._parse_setting(data) def init_empty_config_content(self): with smart_open(self.path, "a") as json_file: @@ -43,7 +44,11 @@ def _parse_setting(self, data: Union[bytes, str]) -> None: } ``` """ - doc = json.loads(data) + try: + doc = json.loads(data) + except json.JSONDecodeError: + raise InvalidConfigurationError(f"Failed to parse {self.path}") + try: self.settings.update(doc["commitizen"]) except KeyError: diff --git a/commitizen/defaults.py b/commitizen/defaults.py index fb117547f8..a7c285edba 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -88,7 +88,7 @@ class Settings(TypedDict, total=False): MINOR = "MINOR" PATCH = "PATCH" -bump_pattern = r"^(BREAKING[\-\ ]CHANGE|feat|fix|refactor|perf)(\(.+\))?(!)?" +bump_pattern = r"^(((BREAKING[\-\ ]CHANGE|feat|fix|refactor|perf)(\(.+\))?(!)?)|\w+!):" bump_map = OrderedDict( ( (r"^.+!$", MAJOR), @@ -112,5 +112,5 @@ class Settings(TypedDict, total=False): change_type_order = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"] bump_message = "bump: version $current_version → $new_version" -commit_parser = r"^(?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?:\s(?P.*)?" # noqa +commit_parser = r"^((?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?|\w+!):\s(?P.*)?" # noqa version_parser = r"(?P([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?(\w+)?)" diff --git a/docs/README.md b/docs/README.md index c839ddda13..75f46c7b7c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,8 +42,6 @@ descriptive commits. [Python](https://www.python.org/downloads/) `3.7+` -[Poetry](https://python-poetry.org/docs/) `1.2.0+` - [Git][gitscm] `1.8.5.2+` ## Installation diff --git a/docs/commit.md b/docs/commit.md index fb3fdd65ac..2d50b111ca 100644 --- a/docs/commit.md +++ b/docs/commit.md @@ -6,6 +6,11 @@ In your terminal run `cz commit` or the shortcut `cz c` to generate a guided git A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`. +You can run `cz commit --write-message-to-file COMMIT_MSG_FILE` to additionally save the +generated message to a file. This can be combined with the `--dry-run` flag to only +write the message to a file and not modify files and create a commit. A possible use +case for this is to [automatically prepare a commit message](./tutorials/auto_prepare_commit_message.md). + !!! note To maintain platform compatibility, the `commit` command disable ANSI escaping in its output. In particular pre-commit hooks coloring will be deactivated as discussed in [commitizen-tools/commitizen#417](https://github.com/commitizen-tools/commitizen/issues/417). diff --git a/docs/config.md b/docs/config.md index 3e5734a89a..f93aca60e7 100644 --- a/docs/config.md +++ b/docs/config.md @@ -292,6 +292,13 @@ Commitizen provides some version providers for some well known formats: !!! note The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin. +An example in your `.cz.toml` would look like this: + +```toml +[tool.commitizen] +version_provider = "pep621" +``` + ### Custom version provider You can add you own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint. diff --git a/docs/contributing.md b/docs/contributing.md index c21c7b5e49..21a7ffb39d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -8,7 +8,7 @@ If you're a first-time contributor, you can check the issues with [good first is ## Install before contributing -1. Install [poetry](https://python-poetry.org/), installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer) +1. Install [poetry](https://python-poetry.org/) `1.2.0+`, installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer) 2. Install [gpg](https://gnupg.org), installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you could try [homebrew](https://brew.sh/). ## Before making a pull request diff --git a/docs/tutorials/auto_prepare_commit_message.md b/docs/tutorials/auto_prepare_commit_message.md new file mode 100644 index 0000000000..8def5f2e28 --- /dev/null +++ b/docs/tutorials/auto_prepare_commit_message.md @@ -0,0 +1,46 @@ +# Automatically prepare message before commit + +## About + +It can be desirable to use commitizen for all types of commits (i.e. regular, merge, +squash) so that the complete git history adheres to the commit message convention +without ever having to call `cz commit`. + +To automatically prepare a commit message prior to committing, you can +use a [prepare-commit-msg Git hook](prepare-commit-msg-docs): + +> This hook is invoked by git-commit right after preparing the +> default log message, and before the editor is started. + +To automatically perform arbitrary cleanup steps after a succesful commit you can use a +[post-commit Git hook][post-commit-docs]: + +> This hook is invoked by git-commit. It takes no parameters, and is invoked after a +> commit is made. + +A combination of these two hooks allows for enforcing the usage of commitizen so that +whenever a commit is about to be created, commitizen is used for creating the commit +message. Running `git commit` or `git commit -m "..."` for example, would trigger +commitizen and use the generated commit message for the commit. + +## Installation + +Copy the hooks from [here](https://github.com/commitizen-tools/hooks) into the `.git/hooks` folder and make them + executable by running the following commands from the root of your Git repository: + +```bash +wget -o .git/hooks/prepare-commit-msg https://github.com/commitizen-tools/hooks/prepare-commit-msg.py +chmod +x .git/hooks/prepare-commit-msg +wget -o .git/hooks/post-commit https://github.com/commitizen-tools/hooks/post-commit.py +chmod +x .git/hooks/post-commit +``` + +## Features + +- Commits can be created using both `cz commit` and the regular `git commit` +- The hooks automatically create a backup of the commit message that can be reused if + the commit failed +- The commit message backup can also be used via `cz commit --retry` + +[post-commit-docs]: https://git-scm.com/docs/githooks#_post_commit +[prepare-commit-msg-docs]: https://git-scm.com/docs/githooks#_prepare_commit_msg diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md index b5d05bb935..3cb92725a5 100644 --- a/docs/tutorials/github_actions.md +++ b/docs/tutorials/github_actions.md @@ -29,7 +29,7 @@ jobs: name: "Bump version and create changelog with commitizen" steps: - name: Check out - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" fetch-depth: 0 @@ -94,14 +94,21 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: "3.x" + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: latest + virtualenvs-in-project: true + virtualenvs-create: true - name: Install dependencies run: | - python -m pip install --pre -U poetry poetry --version poetry install - name: Build and publish @@ -112,7 +119,7 @@ jobs: ./scripts/publish ``` -Notice that we are calling a bash script in `./scripts/publish`, you should configure it with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish) +Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and the publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish) You can also use [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) to publish your package. Push the changes and that's it. diff --git a/docs/tutorials/gitlab_ci.md b/docs/tutorials/gitlab_ci.md index 8c3fd73711..aea7ade99d 100644 --- a/docs/tutorials/gitlab_ci.md +++ b/docs/tutorials/gitlab_ci.md @@ -91,7 +91,7 @@ auto-bump: - git config --global user.email "${CI_EMAIL}" && git config --global user.name "${CI_USERNAME}" - 'exists=`git show-ref refs/heads/master` && if [ -n "$exists" ]; then git branch -D master; fi' - git checkout -b master - - cz bump # execute auto bump and push to master + - cz bump --yes # execute auto bump and push to master - git push origin master:$CI_COMMIT_REF_NAME - TAG=$(head -n 1 VERSION) # get the new software version and save into artifacts - echo "#!/bin/sh" >> variables diff --git a/docs/tutorials/jenkins_pipeline.md b/docs/tutorials/jenkins_pipeline.md index ef39cece84..fb87820c4c 100644 --- a/docs/tutorials/jenkins_pipeline.md +++ b/docs/tutorials/jenkins_pipeline.md @@ -37,7 +37,8 @@ pipeline { def useCz(String authorName = 'Jenkins CI Server', String authorEmail = 'your-jenkins@email.com', String image = 'registry.hub.docker.com/commitizen/commitizen:latest', Closure body) { docker .image(image) - .inside("-u 0 -v $WORKSPACE:/workspace -w /workspace -e GIT_AUTHOR_NAME='${authorName}' -e GIT_AUTHOR_EMAIL='${authorEmail}'") { + .inside("-u 0 -v $WORKSPACE:/workspace -w /workspace -e GIT_AUTHOR_NAME='${authorName}' -e GIT_AUTHOR_EMAIL='${authorEmail}' -entrypoint='/bin/sh'") { + sh 'git config --global --add safe.directory "*"' sh "git config --global user.email '${authorName}'" sh "git config --global user.name '${authorEmail}'" body() diff --git a/hooks/post-commit.py b/hooks/post-commit.py new file mode 100755 index 0000000000..c2faebb738 --- /dev/null +++ b/hooks/post-commit.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import os +import tempfile +from pathlib import Path + + +def post_commit(): + backup_file = Path( + tempfile.gettempdir(), f"cz.commit{os.environ.get('USER', '')}.backup" + ) + + # remove backup file if it exists + if backup_file.is_file(): + backup_file.unlink() + + +if __name__ == "__main__": + exit(post_commit()) diff --git a/hooks/prepare-commit-msg.py b/hooks/prepare-commit-msg.py new file mode 100755 index 0000000000..58beb3a0f8 --- /dev/null +++ b/hooks/prepare-commit-msg.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +import os +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path +from subprocess import CalledProcessError + + +def prepare_commit_msg(commit_msg_file: Path) -> int: + # check that commitizen is installed + if shutil.which("cz") is None: + print("commitizen is not installed!") + return 0 + + # check if the commit message needs to be generated using commitizen + if ( + subprocess.run( + [ + "cz", + "check", + "--commit-msg-file", + commit_msg_file, + ], + capture_output=True, + ).returncode + != 0 + ): + backup_file = Path( + tempfile.gettempdir(), f"cz.commit{os.environ.get('USER', '')}.backup" + ) + + if backup_file.is_file(): + # confirm if commit message from backup file should be reused + answer = input("retry with previous message? [y/N]: ") + if answer.lower() == "y": + shutil.copyfile(backup_file, commit_msg_file) + return 0 + + # use commitizen to generate the commit message + try: + subprocess.run( + [ + "cz", + "commit", + "--dry-run", + "--write-message-to-file", + commit_msg_file, + ], + stdin=sys.stdin, + stdout=sys.stdout, + ).check_returncode() + except CalledProcessError as error: + return error.returncode + + # write message to backup file + shutil.copyfile(commit_msg_file, backup_file) + + +if __name__ == "__main__": + # make hook interactive by attaching /dev/tty to stdin + with open("/dev/tty") as tty: + sys.stdin = tty + exit(prepare_commit_msg(sys.argv[1])) diff --git a/mkdocs.yml b/mkdocs.yml index da71e30abe..57cf6af46b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -43,6 +43,7 @@ nav: - Tutorials: - Writing commits: "tutorials/writing_commits.md" - Auto check commits: "tutorials/auto_check.md" + - Auto prepare commit message: "tutorials/auto_prepare_commit_message.md" - GitLab CI: "tutorials/gitlab_ci.md" - Github Actions: "tutorials/github_actions.md" - Jenkins pipeline: "tutorials/jenkins_pipeline.md" diff --git a/poetry.lock b/poetry.lock index 1026be8db4..e37d6786ab 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. [[package]] name = "appnope" @@ -285,14 +285,14 @@ toml = ["tomli"] [[package]] name = "decli" -version = "0.5.2" +version = "0.6.0" description = "Minimal, easy-to-use, declarative cli tool" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, - {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, + {file = "decli-0.6.0-py3-none-any.whl", hash = "sha256:d5ed1d509f5a6cf765a4d7350f7ffb0be0c1770840cbd38b05fb0aab642645e8"}, + {file = "decli-0.6.0.tar.gz", hash = "sha256:2915a55525ef2b1a0ce88b8ccba62ac22df5b6ff3ed2094448e0f951f08e7ba5"}, ] [[package]] @@ -1598,4 +1598,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "f85c312b8da45408e78e463496840bcb3d770ee5731a2aada55c56bcae86e547" +content-hash = "4f5717948f57b7d1390ab77b52e038d90b84cba754650c484f5f3ae013cf86e1" diff --git a/pyproject.toml b/pyproject.toml index f27c80ca6a..95e8363aad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.commitizen] -version = "3.1.1" +version = "3.2.0" tag_format = "v$version" version_files = [ "pyproject.toml:version", @@ -9,7 +9,7 @@ version_files = [ [tool.poetry] name = "commitizen" -version = "3.1.1" +version = "3.2.0" description = "Python commitizen client tool" authors = ["Santiago Fraire "] license = "MIT" @@ -36,7 +36,7 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.7" questionary = "^1.4.0" -decli = "^0.5.2" +decli = "^0.6.0" colorama = "^0.4.1" termcolor = ">= 1.1, < 3" packaging = ">=19" @@ -140,7 +140,6 @@ convention = "google" [tool.mypy] files = "commitizen" -ignore_missing_imports = true disallow_untyped_decorators = true disallow_subclassing_any = true warn_return_any = true diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index 4f25ebcad5..30033d9c7d 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -516,6 +516,36 @@ def test_breaking_change_content_v1_multiline( file_regression.check(out, extension=".md") +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_breaking_change_content_v1_with_exclamation_mark( + mocker: MockFixture, capsys, file_regression +): + commit_message = "chore!: drop support for py36" + create_file_and_commit(commit_message) + testargs = ["cz", "changelog", "--dry-run"] + mocker.patch.object(sys, "argv", testargs) + with pytest.raises(DryRunExit): + cli.main() + out, _ = capsys.readouterr() + + file_regression.check(out, extension=".md") + + +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_breaking_change_content_v1_with_exclamation_mark_feat( + mocker: MockFixture, capsys, file_regression +): + commit_message = "feat(pipeline)!: some text with breaking change" + create_file_and_commit(commit_message) + testargs = ["cz", "changelog", "--dry-run"] + mocker.patch.object(sys, "argv", testargs) + with pytest.raises(DryRunExit): + cli.main() + out, _ = capsys.readouterr() + + file_regression.check(out, extension=".md") + + @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_config_flag_increment( mocker: MockFixture, changelog_path, config_path, file_regression diff --git a/tests/commands/test_changelog_command/test_breaking_change_content_v1_with_exclamation_mark.md b/tests/commands/test_changelog_command/test_breaking_change_content_v1_with_exclamation_mark.md new file mode 100644 index 0000000000..d12d780dab --- /dev/null +++ b/tests/commands/test_changelog_command/test_breaking_change_content_v1_with_exclamation_mark.md @@ -0,0 +1,5 @@ +## Unreleased + + +- drop support for py36 + diff --git a/tests/commands/test_changelog_command/test_breaking_change_content_v1_with_exclamation_mark_feat.md b/tests/commands/test_changelog_command/test_breaking_change_content_v1_with_exclamation_mark_feat.md new file mode 100644 index 0000000000..84c23687c4 --- /dev/null +++ b/tests/commands/test_changelog_command/test_breaking_change_content_v1_with_exclamation_mark_feat.md @@ -0,0 +1,6 @@ +## Unreleased + +### Feat + +- **pipeline**: some text with breaking change + diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index dd62fafe85..b45ac3a552 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -12,6 +12,7 @@ NoAnswersError, NoCommitBackupError, NotAGitProjectError, + NotAllowed, NothingToCommitError, ) @@ -109,6 +110,51 @@ def test_commit_command_with_dry_run_option(config, mocker: MockFixture): commit_cmd() +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_command_with_write_message_to_file_option( + config, tmp_path, mocker: MockFixture +): + tmp_file = tmp_path / "message" + + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "", + "footer": "", + } + + commit_mock = mocker.patch("commitizen.git.commit") + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) + success_mock = mocker.patch("commitizen.out.success") + + commands.Commit(config, {"write_message_to_file": tmp_file})() + success_mock.assert_called_once() + assert tmp_file.exists() + assert tmp_file.read_text() == "feat: user created" + + +@pytest.mark.usefixtures("staging_is_clean") +def test_commit_command_with_invalid_write_message_to_file_option( + config, tmp_path, mocker: MockFixture +): + prompt_mock = mocker.patch("questionary.prompt") + prompt_mock.return_value = { + "prefix": "feat", + "subject": "user created", + "scope": "", + "is_breaking_change": False, + "body": "", + "footer": "", + } + + with pytest.raises(NotAllowed): + commit_cmd = commands.Commit(config, {"write_message_to_file": tmp_path}) + commit_cmd() + + @pytest.mark.usefixtures("staging_is_clean") def test_commit_command_with_signoff_option(config, mocker: MockFixture): prompt_mock = mocker.patch("questionary.prompt") diff --git a/tests/test_bump_find_increment.py b/tests/test_bump_find_increment.py index 32a978556c..337cf17e7a 100644 --- a/tests/test_bump_find_increment.py +++ b/tests/test_bump_find_increment.py @@ -8,7 +8,12 @@ from commitizen.cz.conventional_commits import ConventionalCommitsCz from commitizen.git import GitCommit -NONE_INCREMENT_CC = ["docs(README): motivation", "ci: added travis"] +NONE_INCREMENT_CC = [ + "docs(README): motivation", + "ci: added travis", + "performance. Remove or disable the reimplemented linters", + "refactor that how this line starts", +] PATCH_INCREMENTS_CC = [ "fix(setup.py): future is now required for every python version", @@ -19,6 +24,8 @@ "feat(cli): added version", "docs(README): motivation", "fix(setup.py): future is now required for every python version", + "perf: app is much faster", + "refactor: app is much faster", ] MAJOR_INCREMENTS_BREAKING_CHANGE_CC = [ @@ -41,6 +48,16 @@ "fix(setup.py): future is now required for every python version", ] +MAJOR_INCREMENTS_EXCLAMATION_CC_SAMPLE_2 = [ + "feat(pipeline)!: some text with breaking change" +] + +MAJOR_INCREMENTS_EXCLAMATION_OTHER_TYPE_CC = [ + "chore!: drop support for Python 3.9", + "docs(README): motivation", + "fix(setup.py): future is now required for every python version", +] + PATCH_INCREMENTS_SVE = ["readme motivation PATCH", "fix setup.py PATCH"] MINOR_INCREMENTS_SVE = [ @@ -67,7 +84,9 @@ (MINOR_INCREMENTS_CC, "MINOR"), (MAJOR_INCREMENTS_BREAKING_CHANGE_CC, "MAJOR"), (MAJOR_INCREMENTS_BREAKING_CHANGE_ALT_CC, "MAJOR"), + (MAJOR_INCREMENTS_EXCLAMATION_OTHER_TYPE_CC, "MAJOR"), (MAJOR_INCREMENTS_EXCLAMATION_CC, "MAJOR"), + (MAJOR_INCREMENTS_EXCLAMATION_CC_SAMPLE_2, "MAJOR"), (NONE_INCREMENT_CC, None), ), )