Skip to content

feat(conventional_commits): Add exclamation on commit title for breaking change #1576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: v4-9-0-test
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/bumpversion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
name: "Bump version and create changelog with commitizen"
steps:
- name: Check out
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/docspublish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
update-cli-screenshots:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
fetch-depth: 0
Expand Down Expand Up @@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
needs: update-cli-screenshots
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}"
fetch-depth: 0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/homebrewpublish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/label_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
sparse-checkout: |
.github/labeler.yml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
platform: [ubuntu-22.04, macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pythonpublish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}"
fetch-depth: 0
Expand Down
10 changes: 10 additions & 0 deletions commitizen/cz/conventional_commits/conventional_commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,21 @@ def message(self, answers: ConventionalCommitsAnswers) -> str: # type: ignore[o
footer = answers["footer"]
is_breaking_change = answers["is_breaking_change"]

# Check if breaking change exclamation in title is enabled
breaking_change_exclamation_in_title = self.config.settings.get(
"breaking_change_exclamation_in_title", False
)

if scope:
scope = f"({scope})"
if body:
body = f"\n\n{body}"
if is_breaking_change:
if breaking_change_exclamation_in_title:
if scope:
scope = f"{scope}!"
else:
prefix = f"{prefix}!"
footer = f"BREAKING CHANGE: {footer}"
if footer:
footer = f"\n\n{footer}"
Expand Down
2 changes: 2 additions & 0 deletions commitizen/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class Settings(TypedDict, total=False):
version_scheme: str | None
version_type: str | None
version: str | None
breaking_change_exclamation_in_title: bool


CONFIG_FILES: list[str] = [
Expand Down Expand Up @@ -108,6 +109,7 @@ class Settings(TypedDict, total=False):
"always_signoff": False,
"template": None, # default provided by plugin
"extras": {},
"breaking_change_exclamation_in_title": False,
}

MAJOR = "MAJOR"
Expand Down
9 changes: 9 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ Default: `None`

Create custom commit message, useful to skip CI. [Read more][bump_message]

### `breaking_change_exclamation_in_title`

Type: `bool`

Default: `false`

When true, breaking changes will be indicated by an exclamation mark in the commit title (e.g., `feat!: breaking change`).
When false, breaking changes will be indicated by `BREAKING CHANGE:` in the footer. [Read more][breaking-change-exclamation]

### `retry_after_failure`

Type: `bool`
Expand Down
5 changes: 5 additions & 0 deletions docs/tutorials/writing_commits.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ add to your commit body the following `BREAKING CHANGE`.
Using these three keywords will allow the proper identification of the semantic version.
Of course, there are other keywords, but I'll leave it to the reader to explore them.

Note: You can also indicate breaking changes by adding an exclamation mark in the commit title
(e.g., `feat!: breaking change`) by setting the `breaking_change_exclamation_in_title`
configuration option to `true`. [Read more][breaking-change-config]

## Writing commits

Now to the important part: when writing commits, it's important to think about:
Expand Down Expand Up @@ -44,3 +48,4 @@ Emojis may be added as well (e.g., see [cz-emoji][cz_emoji]), which requires the
[conventional_commits]: https://www.conventionalcommits.org
[cz_emoji]: https://commitizen-tools.github.io/commitizen/third-party-commitizen/#cz-emoji
[configuration]: ../config.md#encoding
[breaking-change-config]: ../config.md#breaking_change_exclamation_in_title
2 changes: 2 additions & 0 deletions tests/test_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"always_signoff": False,
"template": None,
"extras": {},
"breaking_change_exclamation_in_title": False,
}

_new_settings: dict[str, Any] = {
Expand Down Expand Up @@ -126,6 +127,7 @@
"always_signoff": False,
"template": None,
"extras": {},
"breaking_change_exclamation_in_title": False,
}


Expand Down
49 changes: 49 additions & 0 deletions tests/test_cz_conventional_commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,55 @@ def test_breaking_change_in_footer(config):
)


@pytest.mark.parametrize(
"scope,breaking_change_exclamation_in_title,expected_message",
[
# Test with scope and breaking_change_exclamation_in_title enabled
(
"users",
True,
"feat(users)!: email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users",
),
# Test without scope and breaking_change_exclamation_in_title enabled
(
"",
True,
"feat!: email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users",
),
# Test with scope and breaking_change_exclamation_in_title disabled
(
"users",
False,
"feat(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users",
),
# Test without scope and breaking_change_exclamation_in_title disabled
(
"",
False,
"feat: email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users",
),
],
)
def test_breaking_change_message_formats(
config, scope, breaking_change_exclamation_in_title, expected_message
):
# Set the breaking_change_exclamation_in_title setting
config.settings["breaking_change_exclamation_in_title"] = (
breaking_change_exclamation_in_title
)
conventional_commits = ConventionalCommitsCz(config)
answers = {
"prefix": "feat",
"scope": scope,
"subject": "email pattern corrected",
"is_breaking_change": True,
"body": "complete content",
"footer": "migrate by renaming user to users",
}
message = conventional_commits.message(answers)
assert message == expected_message


def test_example(config):
"""just testing a string is returned. not the content"""
conventional_commits = ConventionalCommitsCz(config)
Expand Down
Loading