Skip to content

feat(Conventional提交CZ): add support for customizable change type choices and bump map overrides - #2006

Open
rockleona wants to merge 2 commits into
commitizen-tools:masterfrom
rockleona:feat/override-and-extend-settings
Open

feat(Conventional提交CZ): add support for customizable change type choices and bump map overrides#2006
rockleona wants to merge 2 commits into
commitizen-tools:masterfrom
rockleona:feat/override-and-extend-settings

Conversation

@rockleona

@rockleona rockleona commented May 30, 2026

Copy link
Copy Markdown
Contributor

Description

According to #1385, I've adding override and extend attributes under 设置, basically, the content was inherited using Cz设置.

Checklist

Was generative AI tooling used to co-author this PR?

  • Yes (GitHub Copilot)

Code Changes

  • Add test cases to all the changes you introduce
  • Run uv run poe all locally to ensure this change passes linter check and tests
  • Manually test the changes:
    • Verify the feature/bug fix works as expected in real-world scenarios
    • Test edge cases and error conditions
    • Ensure backward compatibility is maintained
    • Document any manual testing steps performed
  • Update the documentation for the changes

Expected Behavior

Steps to Test This Pull Request

Override

[tool.commitizen]
name = "cz_conventional_commits"

[tool.commitizen.override]
bump_pattern = "^((feat|fix|perf)(\\(.+\\))?!?):"
commit_parser = "^((?P<change_type>feat|fix|perf)(?:\\((?P<scope>[^()\\r\\n]*)\\))?(?P<breaking>!)?):\\s(?P<message>.*)$"
changelog_pattern = "^(feat|fix|perf)"
bump_map = { "^feat" = "MINOR", "^fix" = "PATCH", "^perf" = "PATCH" }
change_type_map = { feat = "Features", fix = "Bug Fixes", perf = "Performance" }
change_type_choices = [
  { value = "feat", name = "feat: A new feature", key = "f" },
  { value = "fix", name = "fix: A bug fix", key = "x" }
]

Extend

[tool.commitizen]
name = "cz_conventional_commits"

[tool.commitizen.extend]
bump_map = { "^deps" = "PATCH" }
change_type_map = { deps = "Dependencies" }
change_type_choices = [
  { value = "deps", name = "deps: Dependency updates", key = "e" }
]

Additional Context

issue #1385

@codecov

codecov Bot commented May 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.08%. Comparing base (c3f6797) to head (1461705).
⚠️ Report is 23 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2006      +/-   ##
==========================================
+ Coverage   98.24%   99.08%   +0.84%     
==========================================
  Files          61       61              
  Lines        2785     2836      +51     
==========================================
+ Hits         2736     2810      +74     
+ Misses         49       26      -23     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the cz_conventional_commits convention to support user configuration via new override and extend settings, enabling customization of bump/changelog parsing and change type choice lists without switching to cz_customize.

Changes:

  • Add override/extend typed settings structures to the global 设置 model.
  • Teach Conventional提交Cz to apply override/extend settings and isolate mutable defaults per instance.
  • Add tests covering precedence (override > extend), supported settings application, and mutation isolation between instances.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 39 comments.

File Description
commitizen/cz/conventional_commits/conventional_commits.py Adds per-instance isolation and applies override/extend settings; refactors change type choices into a reusable attribute.
commitizen/defaults.py Extends the typed settings model with override/extend sections and typed change_type_choices.
tests/test_cz_conventional_commits.py Adds test coverage for override/extend precedence, application behavior, and instance isolation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 7 to 14
from commitizen import defaults
from commitizen.cz.base import BaseCommitizen
from commitizen.cz.utils import multiple_line_breaker, required_validator
from commitizen.question import Choice

if TYPE_CHECKING:
from commitizen.config import BaseConfig
from commitizen.question import CzQuestion
Comment on lines 46 to +48
}
change_type_choices = [
Choice(
Comment on lines +48 to +52
Choice(
value="fix",
name=("fix: A bug fix. Correlates with PATCH in SemVer"),
key="x",
),
Comment on lines +53 to +57
Choice(
value="feat",
name="feat: A new feature. Correlates with MINOR in SemVer",
key="f",
),
Comment on lines +58 to +62
Choice(
value="docs",
name="docs: Documentation only changes",
key="d",
),
Comment on lines +89 to +96
Choice(
value="build",
name=(
"build: Changes that affect the build system or "
"external dependencies (example scopes: pip, docker, npm)"
),
key="b",
),
Comment on lines +97 to +104
Choice(
value="ci",
name=(
"ci: Changes to CI configuration files and "
"scripts (example scopes: GitLabCI)"
),
key="c",
),
Comment on lines +125 to +140
def _apply_override_settings(self, settings: defaults.CzOverride设置) -> None:
if bump_pattern := settings.get("bump_pattern"):
self.bump_pattern = bump_pattern
if bump_map := settings.get("bump_map"):
self.bump_map = OrderedDict(bump_map)
if bump_map_major_version_zero := settings.get("bump_map_major_version_zero"):
self.bump_map_major_version_zero = OrderedDict(bump_map_major_version_zero)
if commit_parser := settings.get("commit_parser"):
self.commit_parser = commit_parser
if changelog_pattern := settings.get("changelog_pattern"):
self.changelog_pattern = changelog_pattern
if change_type_map := settings.get("change_type_map"):
self.change_type_map = dict(change_type_map)
if change_type_choices := settings.get("change_type_choices"):
self.change_type_choices = [*change_type_choices]

Comment on lines +141 to +155
def _apply_extend_settings(self, settings: defaults.CzExtend设置) -> None:
if bump_pattern := settings.get("bump_pattern"):
self.bump_pattern = bump_pattern
if bump_map := settings.get("bump_map"):
self.bump_map.update(bump_map)
if bump_map_major_version_zero := settings.get("bump_map_major_version_zero"):
self.bump_map_major_version_zero.update(bump_map_major_version_zero)
if commit_parser := settings.get("commit_parser"):
self.commit_parser = commit_parser
if changelog_pattern := settings.get("changelog_pattern"):
self.changelog_pattern = changelog_pattern
if change_type_map := settings.get("change_type_map"):
self.change_type_map.update(change_type_map)
if change_type_choices := settings.get("change_type_choices"):
self.change_type_choices.extend(change_type_choices)
"refactor": "Refactor",
"perf": "Perf",
}
change_type_choices = [
@bearomorphism
bearomorphism requested a review from Lee-W May 30, 2026 05:21
@bearomorphism

Copy link
Copy Markdown
Collaborator

@Lee-W Could you take a look? You have more context on this feature.

@woile

woile commented May 30, 2026

Copy link
Copy Markdown
Member

I'll take a look next week

@woile woile left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good work!

However, I don't think override and extend should happen at the conventional_commits.py level. This change should work for any custom rules.

Instead, I think the override should directly happen here:
https://github.com/commitizen-tools/commitizen/blob/master/commitizen/cli.py#L710

The hierarchy is:

settings <- override/extend <- cli

Meaning that anything I provide via cli should override the override/extend and the settings. Which is not happening now.

cc: @Lee-W

@Lee-W

Lee-W commented Jun 7, 2026

Copy link
Copy Markdown
Member

@rockleona could you try to resolve the comments on this PR? It's super hard to read with the OpenAI comments....


Yep, agreed with @woile — since bump_pattern / bump_map / bump_map_major_version_zero / change_type_map / change_type_order / changelog_pattern / commit_parser all live on BaseCommitizen, the override/extend logic should move there so it works for any rule set, not just conventional commits.

@Manny7717 Manny7717 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified review (head 1461705)

Locally verified on head (repo venv, Python 3.12):

  • Tests: test_cz_conventional_commits.py 30/30 pass (incl. 4 new); test_conf.py 67/67. Full suite 1285 passed / 6 failed / 2 xfailed — the 6 failures (test_bump_pre_commit_changelog*) fail byte-identically on origin/master (pre-existing env noise from pre-commit/prek hooks). Zero new failures.
  • Lint/type: ruff check clean on both changed files + tests; mypy clean on the two source files.
  • Behavior probes (all passed): override replaces maps wholesale; extend merges into defaults while retaining them (OrderedDict append); questions()[0] carries extended choices; bump._find_increment reads self.cz.bump_map so overrides take effect; instance isolation works (_isolate_mutable_defaults correctly prevents cross-instance leakage — verified two instances don't share the extended map); elif gives override precedence over extend when both are set.
  • Interop with #2081 (strict_config, by Manny7717): KNOWN_SETTINGS there derives from the 设置 TypedDict keys, so the new override/extend keys are auto-included when both merge — no conflict.

Finding: custom change types cannot pass cz check (integration gap)

The feature lets users extend change_type_choices, and the interactive prompt happily offers the custom type (verified: questions()[0]["choices"] ends with the extended choice). But cz check uses schema_pattern(), whose type list is hardcoded (conventional_commits.py:243-266) and is not covered by the new override/extend settings.

Verified end-to-end with the real CLI:

[tool.commitizen.extend]
change_type_choices = [{ value = "foo", name = "foo: custom type", key = "o" }]
  • cz commit accepts the foo choice and produces foo: added thing
  • cz check --message "foo: added thing"commit validation: failed! with pattern (build|bump|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)...
  • cz bump computes no increment for foo: commits unless bump_map is also extended (expected), but note commit_parser also needs overriding for changelog classification

So the three consumers (prompt, check, bump/changelog) each use different source-of-truth lists, and the settings mechanism covers only two of them. A user who follows the PR's own "Steps to Test" (extend.change_type_choices only) ends up able to create a commit that the project's own validator rejects. This is a concrete instance of the design concern woile/Lee-W raised in June (override/extend should live on BaseCommitizen / apply to all rule sets): even scoped to conventional commits, the feature is internally inconsistent.

Suggested fixes (any one):

  1. Derive schema_pattern()'s type alternatives from change_type_choices (falling back to the hardcoded list), or
  2. Allow schema_pattern in override/extend settings, or
  3. Document explicitly in the PR that custom types require overriding commit_parser + bump_map + schema_pattern-equivalent, and add a regression test that runs cz check on a message with an extended type.

Minor notes

  • Falsy-value skip: _apply_override_settings/_apply_extend_settings use if x := settings.get(...), so empty-dict values are silently ignored — override: {bump_map: {}} keeps the default map instead of clearing it. Edge case, but a user attempting to remove entries this way gets no feedback.
  • Docs: the new override/extend keys are not documented in docs/config/option.md/configuration_file.md (the PR checklist's "Update the documentation" is unchecked). For a user-facing config feature this will likely be requested before merge.
  • Staleness: no commits since 2026-05-30 and the June maintainer feedback (woile, Lee-W) is unaddressed — the design-direction question should be resolved before this lands.

注册 for free to join this conversation on GitHub. Already have an account? 登录 to comment

标签

项目

None yet

Development

Successfully merging this pull request may close these issues.

6 participants