diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 10d782d30b..5eac443f29 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,7 @@ name: 🛠 Bug report description: Create a report to help us improve title: Good bug title tells us about precise symptom, not about the root cause. -labels: [bug] +labels: ['type: bug'] body: - type: textarea id: description @@ -48,14 +48,22 @@ body: attributes: label: Environment description: | - For older commitizen versions, please include the output of the following commands manually - placeholder: | - - commitizen version: `cz version` - - python version: `python --version` - - operating system: `python3 -c "import platform; print(platform.system())"` + Please use the following command to retrieve environment information ```bash cz version --report ``` + + If `cz version --report` is not available, please run the following commands to retrieve your environment information: + + ```bash + echo "Commitizen Version: $(cz version)" + echo "Python Version: $(python3 -c 'import sys; print(sys.version)')" + echo "Operating System: $(python3 -c 'import platform; print(platform.system())')" + ``` + placeholder: | + Commitizen Version: 4.0.0 + Python Version: 3.13.3 (main, Apr 8 2025, 13:54:08) [Clang 16.0.0 (clang-1600.0.26.6)] + Operating System: Darwin validations: required: true diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml index 51d378b747..8fe40136c3 100644 --- a/.github/ISSUE_TEMPLATE/documentation.yml +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -1,7 +1,7 @@ name: 📖 Documentation description: Suggest an improvement for the documentation of this project title: Content to be added or fixed -labels: [documentation] +labels: ['type: documentation'] body: - type: checkboxes id: type diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 7d67eb18af..4bd19b9b3a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,7 @@ name: 🚀 Feature request description: Suggest an idea for this project title: "" -labels: [feature] +labels: ['type: feature'] body: - type: textarea id: description diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0064604fba..e28480e5b9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,12 +9,24 @@ Please fill in the following content to let us know better about this change. ## Checklist +- [ ] I have read the [contributing guidelines](https://commitizen-tools.github.io/commitizen/contributing/) + +### Code Changes + - [ ] Add test cases to all the changes you introduce -- [ ] Run `poetry all` locally to ensure this change passes linter check and test -- [ ] Test the changes on the local machine manually +- [ ] Run `poetry 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 +### Documentation Changes + +- [ ] Run `poetry doc` locally to ensure the documentation pages renders correctly + +## Expected Behavior @@ -25,5 +37,5 @@ Please fill in the following content to let us know better about this change. 3. ... --> -## Additional context +## Additional Context diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml index 45ca450f89..e74a36c4bd 100644 --- a/.github/workflows/label_issues.yml +++ b/.github/workflows/label_issues.yml @@ -15,9 +15,32 @@ jobs: - uses: actions/github-script@v7 with: script: | - github.rest.issues.addLabels({ + const issue = await github.rest.issues.get({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - labels: ['issue-status: needs-triage'] - }) + }); + + const body = issue.data.body || ''; + + const osLabels = new Set(); // Use a Set to avoid duplicates + + // Parse the "Environment" section, output of `cz version --report` + if (body.includes('Operating System: Darwin')) { + osLabels.add('os: macOS'); + } + + if (body.includes('Operating System: Linux')) { + osLabels.add('os: Linux'); + } + + if (body.includes('Operating System: Windows')) { + osLabels.add('os: Windows'); + } + + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['issue-status: needs-triage', ...osLabels], + }); diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad78003cf3..4e108eb9b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,7 @@ repos: - tomli - repo: https://github.com/commitizen-tools/commitizen - rev: v4.7.0 # automatically updated by Commitizen + rev: v4.8.2 # automatically updated by Commitizen hooks: - id: commitizen - id: commitizen-branch diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ca4f2aa70..5b737a851a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## v4.8.2 (2025-05-22) + +### Refactor + +- **check**: simplify code +- **check**: remove unnecessary variable + +## v4.8.1 (2025-05-22) + +### Refactor + +- **customize**: improve code readability + +## v4.8.0 (2025-05-20) + +### Feat + +- **cli**: add --tag-format argument to changelog command + +## v4.7.2 (2025-05-18) + +### Refactor + +- **default**: capitalize all constants and remove unnecessary variable + +## v4.7.1 (2025-05-16) + +### Fix + +- **bump**: don't fail if an invalid version tag is present (fix #1410) (#1418) + ## v4.7.0 (2025-05-10) ### Feat diff --git a/commitizen/__version__.py b/commitizen/__version__.py index 8355eb42a9..8e288a597c 100644 --- a/commitizen/__version__.py +++ b/commitizen/__version__.py @@ -1 +1 @@ -__version__ = "4.7.0" +__version__ = "4.8.2" diff --git a/commitizen/bump.py b/commitizen/bump.py index adfab64cb0..76a8e15893 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -8,7 +8,7 @@ from string import Template from typing import cast -from commitizen.defaults import MAJOR, MINOR, PATCH, bump_message, encoding +from commitizen.defaults import BUMP_MESSAGE, ENCODING, MAJOR, MINOR, PATCH from commitizen.exceptions import CurrentVersionNotFoundError from commitizen.git import GitCommit, smart_open from commitizen.version_schemes import Increment, Version @@ -64,7 +64,7 @@ def update_version_in_files( files: list[str], *, check_consistency: bool = False, - encoding: str = encoding, + encoding: str = ENCODING, ) -> list[str]: """Change old version to the new one in every file given. @@ -121,7 +121,7 @@ def _bump_with_regex( current_version: str, new_version: str, regex: str, - encoding: str = encoding, + encoding: str = ENCODING, ) -> tuple[bool, str]: current_version_found = False lines = [] @@ -148,6 +148,6 @@ def create_commit_message( message_template: str | None = None, ) -> str: if message_template is None: - message_template = bump_message + message_template = BUMP_MESSAGE t = Template(message_template) return t.safe_substitute(current_version=current_version, new_version=new_version) diff --git a/commitizen/cli.py b/commitizen/cli.py index 72d824380d..d08afc6706 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -450,6 +450,10 @@ def __call__( "help": "Export the changelog template into this file instead of rendering it", }, *deepcopy(tpl_arguments), + { + "name": "--tag-format", + "help": "The format of the tag, wrap around simple quotes", + }, ], }, { diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 60853094f9..0a2bbe37fc 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -68,7 +68,7 @@ def __init__(self, config: BaseConfig, arguments: dict): if arguments[key] is not None }, } - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) self.changelog_flag = arguments["changelog"] self.changelog_config = self.config.settings.get("update_changelog_on_bump") self.changelog_to_stdout = arguments["changelog_to_stdout"] diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 3b8f43e372..0e4efabfa1 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -49,7 +49,7 @@ def __init__(self, config: BaseConfig, args): ) self.encoding = self.config.settings["encoding"] - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) self.start_rev = args.get("start_rev") or self.config.settings.get( "changelog_start_rev" @@ -78,7 +78,7 @@ def __init__(self, config: BaseConfig, args): self.change_type_order = ( self.config.settings.get("change_type_order") or self.cz.change_type_order - or defaults.change_type_order + or defaults.CHANGE_TYPE_ORDER ) self.rev_range = args.get("rev_range") self.tag_format: str = ( diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index e22155cf78..1e3b8464e1 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -46,7 +46,7 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd( self.config: BaseConfig = config self.encoding = config.settings["encoding"] - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) def _valid_command_argument(self): num_exclusive_args_provided = sum( @@ -72,15 +72,11 @@ def __call__(self): raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'") pattern = self.cz.schema_pattern() - ill_formated_commits = [ - commit - for commit in commits - if not self.validate_commit_message(commit.message, pattern) - ] displayed_msgs_content = "\n".join( [ f'commit "{commit.rev}": "{commit.message}"' - for commit in ill_formated_commits + for commit in commits + if not self.validate_commit_message(commit.message, pattern) ] ) if displayed_msgs_content: @@ -92,24 +88,21 @@ def __call__(self): ) out.success("Commit validation: successful!") + def _get_commit_message(self) -> str | None: + if self.commit_msg_file is None: + # Get commit message from command line (--message) + return self.commit_msg + + with open(self.commit_msg_file, encoding=self.encoding) as commit_file: + # Get commit message from file (--commit-msg-file) + return commit_file.read() + def _get_commits(self): - msg = None - # Get commit message from file (--commit-msg-file) - if self.commit_msg_file is not None: - # Enter this branch if commit_msg_file is "". - with open(self.commit_msg_file, encoding=self.encoding) as commit_file: - msg = commit_file.read() - # Get commit message from command line (--message) - elif self.commit_msg is not None: - msg = self.commit_msg - if msg is not None: - msg = self._filter_comments(msg) - return [git.GitCommit(rev="", title="", body=msg)] + if (msg := self._get_commit_message()) is not None: + return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))] # Get commit messages from git log (--rev-range) - if self.rev_range: - return git.get_commits(end=self.rev_range) - return git.get_commits() + return git.get_commits(end=self.rev_range or "HEAD") @staticmethod def _filter_comments(msg: str) -> str: diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py index 93f048082b..cb34c41a50 100644 --- a/commitizen/commands/commit.py +++ b/commitizen/commands/commit.py @@ -35,7 +35,7 @@ def __init__(self, config: BaseConfig, arguments: dict): self.config: BaseConfig = config self.encoding = config.settings["encoding"] - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) self.arguments = arguments self.temp_file: str = get_backup_file_path() diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py index e7abe7b318..a28ad85f16 100644 --- a/commitizen/commands/example.py +++ b/commitizen/commands/example.py @@ -7,7 +7,7 @@ class Example: def __init__(self, config: BaseConfig, *args): self.config: BaseConfig = config - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) def __call__(self): out.write(self.cz.example()) diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py index afac9797e4..abd4197e7f 100644 --- a/commitizen/commands/info.py +++ b/commitizen/commands/info.py @@ -7,7 +7,7 @@ class Info: def __init__(self, config: BaseConfig, *args): self.config: BaseConfig = config - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) def __call__(self): out.write(self.cz.info()) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index df872ec7ee..0eb3d99d17 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -11,7 +11,7 @@ from commitizen.__version__ import __version__ from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig from commitizen.cz import registry -from commitizen.defaults import DEFAULT_SETTINGS, config_files +from commitizen.defaults import CONFIG_FILES, DEFAULT_SETTINGS from commitizen.exceptions import InitFailedError, NoAnswersError from commitizen.git import get_latest_tag_name, get_tag_names, smart_open from commitizen.version_schemes import KNOWN_SCHEMES, Version, get_version_scheme @@ -82,7 +82,7 @@ class Init: def __init__(self, config: BaseConfig, *args): self.config: BaseConfig = config self.encoding = config.settings["encoding"] - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) self.project_info = ProjectInfo() def __call__(self): @@ -165,7 +165,7 @@ def _ask_config_path(self) -> str: name: str = questionary.select( "Please choose a supported config file: ", - choices=config_files, + choices=CONFIG_FILES, default=default_path, style=self.cz.style, ).unsafe_ask() diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py index 0940648cde..4af5679cf5 100644 --- a/commitizen/commands/schema.py +++ b/commitizen/commands/schema.py @@ -7,7 +7,7 @@ class Schema: def __init__(self, config: BaseConfig, *args): self.config: BaseConfig = config - self.cz = factory.commiter_factory(self.config) + self.cz = factory.committer_factory(self.config) def __call__(self): out.write(self.cz.schema()) diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py index f3720bb1b3..9dfd591c40 100644 --- a/commitizen/config/__init__.py +++ b/commitizen/config/__init__.py @@ -28,7 +28,7 @@ def read_cfg(filepath: str | None = None) -> BaseConfig: cfg_paths = ( path / Path(filename) for path in cfg_search_paths - for filename in defaults.config_files + for filename in defaults.CONFIG_FILES ) for filename in cfg_paths: diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py index c7b88258cb..af29a209fc 100644 --- a/commitizen/cz/conventional_commits/conventional_commits.py +++ b/commitizen/cz/conventional_commits/conventional_commits.py @@ -28,9 +28,9 @@ def parse_subject(text): class ConventionalCommitsCz(BaseCommitizen): - bump_pattern = defaults.bump_pattern - bump_map = defaults.bump_map - bump_map_major_version_zero = defaults.bump_map_major_version_zero + bump_pattern = defaults.BUMP_PATTERN + bump_map = defaults.BUMP_MAP + bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO commit_parser = r"^((?Pfeat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P[^()\r\n]*)\)|\()?(?P!)?|\w+!):\s(?P.*)?" # noqa change_type_map = { "feat": "Feat", @@ -38,7 +38,7 @@ class ConventionalCommitsCz(BaseCommitizen): "refactor": "Refactor", "perf": "Perf", } - changelog_pattern = defaults.bump_pattern + changelog_pattern = defaults.BUMP_PATTERN def questions(self) -> Questions: questions: Questions = [ diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py index d53ae29f1b..53ada4b2c0 100644 --- a/commitizen/cz/customize/customize.py +++ b/commitizen/cz/customize/customize.py @@ -21,10 +21,10 @@ class CustomizeCommitsCz(BaseCommitizen): - bump_pattern = defaults.bump_pattern - bump_map = defaults.bump_map - bump_map_major_version_zero = defaults.bump_map_major_version_zero - change_type_order = defaults.change_type_order + bump_pattern = defaults.BUMP_PATTERN + bump_map = defaults.BUMP_MAP + bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO + change_type_order = defaults.CHANGE_TYPE_ORDER def __init__(self, config: BaseConfig): super().__init__(config) @@ -33,35 +33,17 @@ def __init__(self, config: BaseConfig): raise MissingCzCustomizeConfigError() self.custom_settings = self.config.settings["customize"] - custom_bump_pattern = self.custom_settings.get("bump_pattern") - if custom_bump_pattern: - self.bump_pattern = custom_bump_pattern - - custom_bump_map = self.custom_settings.get("bump_map") - if custom_bump_map: - self.bump_map = custom_bump_map - - custom_bump_map_major_version_zero = self.custom_settings.get( - "bump_map_major_version_zero" - ) - if custom_bump_map_major_version_zero: - self.bump_map_major_version_zero = custom_bump_map_major_version_zero - - custom_change_type_order = self.custom_settings.get("change_type_order") - if custom_change_type_order: - self.change_type_order = custom_change_type_order - - commit_parser = self.custom_settings.get("commit_parser") - if commit_parser: - self.commit_parser = commit_parser - - changelog_pattern = self.custom_settings.get("changelog_pattern") - if changelog_pattern: - self.changelog_pattern = changelog_pattern - - change_type_map = self.custom_settings.get("change_type_map") - if change_type_map: - self.change_type_map = change_type_map + for attr_name in [ + "bump_pattern", + "bump_map", + "bump_map_major_version_zero", + "change_type_order", + "commit_parser", + "changelog_pattern", + "change_type_map", + ]: + if value := self.custom_settings.get(attr_name): + setattr(self, attr_name, value) def questions(self) -> Questions: return self.custom_settings.get("questions", [{}]) @@ -70,8 +52,7 @@ def message(self, answers: dict) -> str: message_template = Template(self.custom_settings.get("message_template", "")) if getattr(Template, "substitute", None): return message_template.substitute(**answers) # type: ignore - else: - return message_template.render(**answers) + return message_template.render(**answers) def example(self) -> str: return self.custom_settings.get("example") or "" @@ -83,12 +64,7 @@ def schema(self) -> str: return self.custom_settings.get("schema") or "" def info(self) -> str: - info_path = self.custom_settings.get("info_path") - info = self.custom_settings.get("info") - if info_path: + if info_path := self.custom_settings.get("info_path"): with open(info_path, encoding=self.config.settings["encoding"]) as f: - content = f.read() - return content - elif info: - return info - return "" + return f.read() + return self.custom_settings.get("info") or "" diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 0b78e1b0bb..0b6c28e6a9 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -60,8 +60,7 @@ class Settings(TypedDict, total=False): extras: dict[str, Any] -name: str = "cz_conventional_commits" -config_files: list[str] = [ +CONFIG_FILES: list[str] = [ "pyproject.toml", ".cz.toml", ".cz.json", @@ -70,10 +69,10 @@ class Settings(TypedDict, total=False): "cz.yaml", "cz.toml", ] -encoding: str = "utf-8" +ENCODING = "utf-8" DEFAULT_SETTINGS: Settings = { - "name": name, + "name": "cz_conventional_commits", "version": None, "version_files": [], "version_provider": "commitizen", @@ -102,7 +101,7 @@ class Settings(TypedDict, total=False): "pre_bump_hooks": [], "post_bump_hooks": [], "prerelease_offset": 0, - "encoding": encoding, + "encoding": ENCODING, "always_signoff": False, "template": None, # default provided by plugin "extras": {}, @@ -114,8 +113,8 @@ class Settings(TypedDict, total=False): CHANGELOG_FORMAT = "markdown" -bump_pattern = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):" -bump_map = OrderedDict( +BUMP_PATTERN = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):" +BUMP_MAP = OrderedDict( ( (r"^.+!$", MAJOR), (r"^BREAKING[\-\ ]CHANGE", MAJOR), @@ -125,7 +124,7 @@ class Settings(TypedDict, total=False): (r"^perf", PATCH), ) ) -bump_map_major_version_zero = OrderedDict( +BUMP_MAP_MAJOR_VERSION_ZERO = OrderedDict( ( (r"^.+!$", MINOR), (r"^BREAKING[\-\ ]CHANGE", MINOR), @@ -135,8 +134,8 @@ class Settings(TypedDict, total=False): (r"^perf", PATCH), ) ) -change_type_order = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"] -bump_message = "bump: version $current_version → $new_version" +CHANGE_TYPE_ORDER = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"] +BUMP_MESSAGE = "bump: version $current_version → $new_version" def get_tag_regexes( diff --git a/commitizen/factory.py b/commitizen/factory.py index 09af5fd0f7..b5d665b65e 100644 --- a/commitizen/factory.py +++ b/commitizen/factory.py @@ -4,7 +4,7 @@ from commitizen.exceptions import NoCommitizenFoundException -def commiter_factory(config: BaseConfig) -> BaseCommitizen: +def committer_factory(config: BaseConfig) -> BaseCommitizen: """Return the correct commitizen existing in the registry.""" name: str = config.settings["name"] try: diff --git a/commitizen/tags.py b/commitizen/tags.py index 5724bb2574..2b9a4b091a 100644 --- a/commitizen/tags.py +++ b/commitizen/tags.py @@ -113,6 +113,10 @@ def _format_regex(self, tag_pattern: str, star: bool = False) -> str: format_regex = format_regex.replace(pattern, regex) return format_regex + def _version_tag_error(self, tag: str) -> str: + """Format the error message for an invalid version tag""" + return f"Invalid version tag: '{tag}' does not match any configured tag format" + def is_version_tag(self, tag: str | GitTag, warn: bool = False) -> bool: """ True if a given tag is a legit version tag. @@ -120,9 +124,9 @@ def is_version_tag(self, tag: str | GitTag, warn: bool = False) -> bool: if `warn` is `True`, it will print a warning message if the tag is not a version tag. """ tag = tag.name if isinstance(tag, GitTag) else tag - is_legit = any(regex.match(tag) for regex in self.version_regexes) + is_legit = any(regex.fullmatch(tag) for regex in self.version_regexes) if warn and not is_legit and not self.is_ignored_tag(tag): - out.warn(f"InvalidVersion {tag} doesn't match any configured tag format") + out.warn(self._version_tag_error(tag)) return is_legit def is_ignored_tag(self, tag: str | GitTag) -> bool: @@ -146,9 +150,7 @@ def extract_version(self, tag: GitTag) -> Version: m for regex in self.version_regexes if (m := regex.fullmatch(tag.name)) ) if not (m := next(candidates, None)): - raise InvalidVersion( - f"Invalid version tag: '{tag.name}' does not match any configured tag format" - ) + raise InvalidVersion(self._version_tag_error(tag.name)) if "version" in m.groupdict(): return self.scheme(m.group("version")) diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py index 2486be58c8..84ded9316e 100644 --- a/commitizen/version_schemes.py +++ b/commitizen/version_schemes.py @@ -19,7 +19,7 @@ else: import importlib_metadata as metadata -from packaging.version import InvalidVersion # noqa: F401: Rexpose the common exception +from packaging.version import InvalidVersion # noqa: F401: expose the common exception from packaging.version import Version as _BaseVersion from commitizen.defaults import MAJOR, MINOR, PATCH, Settings @@ -78,7 +78,7 @@ def is_prerelease(self) -> bool: @property def prerelease(self) -> str | None: - """The prelease potion of the version is this is a prerelease.""" + """The prerelease potion of the version is this is a prerelease.""" raise NotImplementedError("must be implemented") @property @@ -142,7 +142,7 @@ def bump( prerelease: The type of prerelease, if Any is_local_version: Whether to increment the local version instead exact_increment: Treat the increment and prerelease arguments explicitly. Disables logic - that attempts to deduce the correct increment when a prelease suffix is present. + that attempts to deduce the correct increment when a prerelease suffix is present. """ @@ -351,7 +351,7 @@ class SemVer2(SemVer): See: https://semver.org/spec/v2.0.0.html """ - _STD_PRELEASES = { + _STD_PRERELEASES = { "a": "alpha", "b": "beta", } @@ -359,7 +359,7 @@ class SemVer2(SemVer): @property def prerelease(self) -> str | None: if self.is_prerelease and self.pre: - prerelease_type = self._STD_PRELEASES.get(self.pre[0], self.pre[0]) + prerelease_type = self._STD_PRERELEASES.get(self.pre[0], self.pre[0]) return f"{prerelease_type}.{self.pre[1]}" return None diff --git a/docs/images/cli_help/cz_changelog___help.svg b/docs/images/cli_help/cz_changelog___help.svg index 1160ccf6cf..69304f40cf 100644 --- a/docs/images/cli_help/cz_changelog___help.svg +++ b/docs/images/cli_help/cz_changelog___help.svg @@ -1,4 +1,4 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - + - + - - $ cz changelog --help -usage: cz changelog [-h][--dry-run][--file-name FILE_NAME] -[--unreleased-version UNRELEASED_VERSION][--incremental] -[--start-rev START_REV][--merge-prerelease] -[--version-scheme {pep440,semver,semver2}] -[--export-template EXPORT_TEMPLATE][--template TEMPLATE] -[--extra EXTRA] - - -generate changelog (note that it will overwrite existing file) - -positional arguments: -  rev_range             generates changelog for the given version (e.g: 1.5.3) -                        or version range (e.g: 1.5.3..1.7.9) - -options: -  -h, --help            show this help message and exit -  --dry-run             show changelog to stdout -  --file-name FILE_NAME -                        file name of changelog (default: 'CHANGELOG.md') -  --unreleased-version UNRELEASED_VERSION -                        set the value for the new version (use the tag value), -                        instead of using unreleased -  --incremental         generates changelog from last created version, useful -                        if the changelog has been manually modified -  --start-rev START_REV -                        start rev of the changelog. If not set, it will -                        generate changelog from the start -  --merge-prerelease    collect all changes from prereleases into next non- -                        prerelease. If not set, it will include prereleases in -                        the changelog -  --version-scheme {pep440,semver,semver2} -                        choose version scheme -  --export-template EXPORT_TEMPLATE -                        Export the changelog template into this file instead -                        of rendering it -  --template, -t TEMPLATE -                        changelog template file name (relative to the current -                        working directory) -  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value') - + + $ cz changelog --help +usage: cz changelog [-h][--dry-run][--file-name FILE_NAME] +[--unreleased-version UNRELEASED_VERSION][--incremental] +[--start-rev START_REV][--merge-prerelease] +[--version-scheme {pep440,semver,semver2}] +[--export-template EXPORT_TEMPLATE][--template TEMPLATE] +[--extra EXTRA][--tag-format TAG_FORMAT] + + +generate changelog (note that it will overwrite existing file) + +positional arguments: +  rev_range             generates changelog for the given version (e.g: 1.5.3) +                        or version range (e.g: 1.5.3..1.7.9) + +options: +  -h, --help            show this help message and exit +  --dry-run             show changelog to stdout +  --file-name FILE_NAME +                        file name of changelog (default: 'CHANGELOG.md') +  --unreleased-version UNRELEASED_VERSION +                        set the value for the new version (use the tag value), +                        instead of using unreleased +  --incremental         generates changelog from last created version, useful +                        if the changelog has been manually modified +  --start-rev START_REV +                        start rev of the changelog. If not set, it will +                        generate changelog from the start +  --merge-prerelease    collect all changes from prereleases into next non- +                        prerelease. If not set, it will include prereleases in +                        the changelog +  --version-scheme {pep440,semver,semver2} +                        choose version scheme +  --export-template EXPORT_TEMPLATE +                        Export the changelog template into this file instead +                        of rendering it +  --template, -t TEMPLATE +                        changelog template file name (relative to the current +                        working directory) +  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value') +  --tag-format TAG_FORMAT +                        The format of the tag, wrap around simple quotes + diff --git a/pyproject.toml b/pyproject.toml index 02c6d12762..b306e5e78c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "commitizen" -version = "4.7.0" +version = "4.8.2" description = "Python commitizen client tool" authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }] maintainers = [ @@ -88,7 +88,7 @@ build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "4.7.0" +version = "4.8.2" tag_format = "v$version" version_files = [ "pyproject.toml:version", diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index b5ff7e6edb..e15539d8a7 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -209,7 +209,7 @@ def test_bump_command_increment_option( @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_command_prelease(mocker: MockFixture): +def test_bump_command_prerelease(mocker: MockFixture): create_file_and_commit("feat: location") # Create an alpha pre-release. @@ -281,7 +281,7 @@ def test_bump_command_prelease(mocker: MockFixture): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_command_prelease_increment(mocker: MockFixture): +def test_bump_command_prerelease_increment(mocker: MockFixture): # FINAL RELEASE create_file_and_commit("fix: location") @@ -317,7 +317,7 @@ def test_bump_command_prelease_increment(mocker: MockFixture): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_command_prelease_exact_mode(mocker: MockFixture): +def test_bump_command_prerelease_exact_mode(mocker: MockFixture): # PRERELEASE create_file_and_commit("feat: location") @@ -437,7 +437,7 @@ def test_bump_on_git_with_hooks_no_verify_enabled(mocker: MockFixture): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_when_bumpping_is_not_support(mocker: MockFixture): +def test_bump_when_bumping_is_not_support(mocker: MockFixture): create_file_and_commit( "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported" ) @@ -1062,7 +1062,7 @@ def test_bump_use_version_provider(mocker: MockFixture): mock.set_version.assert_called_once_with("0.0.1") -def test_bump_command_prelease_scheme_via_cli( +def test_bump_command_prerelease_scheme_via_cli( tmp_commitizen_project_initial, mocker: MockFixture ): tmp_commitizen_project = tmp_commitizen_project_initial() @@ -1101,7 +1101,7 @@ def test_bump_command_prelease_scheme_via_cli( assert "0.2.0" in f.read() -def test_bump_command_prelease_scheme_via_config( +def test_bump_command_prerelease_scheme_via_config( tmp_commitizen_project_initial, mocker: MockFixture ): tmp_commitizen_project = tmp_commitizen_project_initial( @@ -1145,7 +1145,7 @@ def test_bump_command_prelease_scheme_via_config( assert "0.2.0" in f.read() -def test_bump_command_prelease_scheme_check_old_tags( +def test_bump_command_prerelease_scheme_check_old_tags( tmp_commitizen_project_initial, mocker: MockFixture ): tmp_commitizen_project = tmp_commitizen_project_initial( @@ -1285,7 +1285,7 @@ def test_bump_command_version_scheme_priority_over_version_type(mocker: MockFixt ), ), ) -def test_bump_template_option_precedance( +def test_bump_template_option_precedence( mocker: MockFixture, tmp_commitizen_project: Path, any_changelog_format: ChangelogFormat, @@ -1327,7 +1327,7 @@ def test_bump_template_option_precedance( assert out == expected -def test_bump_template_extras_precedance( +def test_bump_template_extras_precedence( mocker: MockFixture, tmp_commitizen_project: Path, any_changelog_format: ChangelogFormat, @@ -1656,3 +1656,35 @@ def test_bump_detect_legacy_tags_from_scm( cli.main() assert git.tag_exist("v0.4.3") + + +def test_bump_warn_but_dont_fail_on_invalid_tags( + tmp_commitizen_project: py.path.local, + mocker: MockFixture, + capsys: pytest.CaptureFixture, +): + project_root = Path(tmp_commitizen_project) + tmp_commitizen_cfg_file = project_root / "pyproject.toml" + tmp_commitizen_cfg_file.write_text( + "\n".join( + [ + "[tool.commitizen]", + 'version_provider = "scm"', + 'version_scheme = "pep440"', + ] + ), + ) + create_file_and_commit("feat: new file") + create_tag("0.4.2") + create_file_and_commit("feat: new file") + create_tag("0.4.3.deadbeaf") + create_file_and_commit("feat: new file") + + testargs = ["cz", "bump", "--increment", "patch", "--changelog"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + _, err = capsys.readouterr() + + assert err.count("Invalid version tag: '0.4.3.deadbeaf'") == 1 + assert git.tag_exist("0.4.3") diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index f794b8d9f3..0eb29cdb04 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -1481,7 +1481,7 @@ def test_changelog_from_current_version_tag_with_nonversion_tag( ), ), ) -def test_changelog_template_option_precedance( +def test_changelog_template_option_precedence( mocker: MockFixture, tmp_commitizen_project: Path, any_changelog_format: ChangelogFormat, @@ -1523,7 +1523,7 @@ def test_changelog_template_option_precedance( assert out == expected -def test_changelog_template_extras_precedance( +def test_changelog_template_extras_precedence( mocker: MockFixture, tmp_commitizen_project: Path, mock_plugin: BaseCommitizen, @@ -1788,13 +1788,13 @@ def test_changelog_ignored_tags( out = open(changelog_path).read() _, err = capsys.readouterr() assert "## ignore-0.1.0" not in out - assert "InvalidVersion ignore-0.1.0" not in err + assert "Invalid version tag: 'ignore-0.1.0'" not in err assert "## ignored" not in out - assert "InvalidVersion ignored" not in err + assert "Invalid version tag: 'ignored'" not in err assert "## not-ignored" not in out - assert "InvalidVersion not-ignored" in err + assert "Invalid version tag: 'not-ignored'" in err assert "## v0.3.0" in out - assert "InvalidVersion v0.3.0" not in err + assert "Invalid version tag: 'v0.3.0'" not in err def test_changelog_template_extra_quotes( diff --git a/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt index 461eb2edd6..91b7f389b5 100644 --- a/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt +++ b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt @@ -3,7 +3,7 @@ usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME] [--start-rev START_REV] [--merge-prerelease] [--version-scheme {pep440,semver,semver2}] [--export-template EXPORT_TEMPLATE] [--template TEMPLATE] - [--extra EXTRA] + [--extra EXTRA] [--tag-format TAG_FORMAT] [rev_range] generate changelog (note that it will overwrite existing file) @@ -37,3 +37,5 @@ options: changelog template file name (relative to the current working directory) --extra, -e EXTRA a changelog extra variable (in the form 'key=value') + --tag-format TAG_FORMAT + The format of the tag, wrap around simple quotes diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index f1db446190..d95a173d8a 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -365,7 +365,7 @@ def test_check_command_with_pipe_message_and_failed(mocker: MockFixture): assert "commit validation: failed!" in str(excinfo.value) -def test_check_command_with_comment_in_messege_file(mocker: MockFixture, capsys): +def test_check_command_with_comment_in_message_file(mocker: MockFixture, capsys): testargs = ["cz", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index ea18e89a2b..f617c51d8f 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -183,7 +183,7 @@ def check_pre_commit_config(expected: list[dict[str, Any]]): @pytest.mark.usefixtures("pre_commit_installed") class TestPreCommitCases: - def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config): + def test_no_existing_pre_commit_config(_, default_choice, tmpdir, config): with tmpdir.as_cwd(): commands.Init(config)() check_cz_config(default_choice) diff --git a/tests/conftest.py b/tests/conftest.py index 3d88f19b12..60c586f2e6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -232,7 +232,7 @@ def message(self, answers: dict) -> str: @pytest.fixture def mock_plugin(mocker: MockerFixture, config: BaseConfig) -> BaseCommitizen: mock = MockPlugin(config) - mocker.patch("commitizen.factory.commiter_factory", return_value=mock) + mocker.patch("commitizen.factory.committer_factory", return_value=mock) return mock diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index 850b59c166..c14e4ad1cb 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -197,7 +197,7 @@ def test_file_version_inconsistent_error( assert expected_msg in str(excinfo.value) -def test_multiplt_versions_to_bump( +def test_multiple_versions_to_bump( multiple_versions_to_update_poetry_lock, file_regression ): old_version = "1.2.9" @@ -216,7 +216,7 @@ def test_update_version_in_globbed_files(commitizen_config_file, file_regression print(commitizen_config_file, other) copyfile(commitizen_config_file, other) - # Prepend full ppath as test assume absolute paths or cwd-relative + # Prepend full path as test assume absolute paths or cwd-relative version_files = [commitizen_config_file.dirpath("*.toml")] bump.update_version_in_files( diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 67ba273b5c..b1c7c802e1 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -556,8 +556,8 @@ @pytest.fixture -def gitcommits() -> list: - commits = [ +def gitcommits() -> list[git.GitCommit]: + return [ git.GitCommit( commit["rev"], commit["title"], @@ -568,13 +568,11 @@ def gitcommits() -> list: ) for commit in COMMITS_DATA ] - return commits @pytest.fixture -def tags() -> list: - tags = [git.GitTag(*tag) for tag in TAGS] - return tags +def tags() -> list[git.GitTag]: + return [git.GitTag(*tag) for tag in TAGS] @pytest.fixture @@ -1635,7 +1633,7 @@ def test_tags_rules_get_version_tags(capsys: pytest.CaptureFixture): } captured = capsys.readouterr() - assert captured.err.count("InvalidVersion") == 2 + assert captured.err.count("Invalid version tag:") == 2 assert captured.err.count("not-a-version") == 2 diff --git a/tests/test_changelog_format_asciidoc.py b/tests/test_changelog_format_asciidoc.py index 59ca56191e..cc81a24f2a 100644 --- a/tests/test_changelog_format_asciidoc.py +++ b/tests/test_changelog_format_asciidoc.py @@ -160,7 +160,7 @@ def test_parse_title_type_of_line( pytest.param(CHANGELOG_D, EXPECTED_D, id="D"), ), ) -def test_get_matadata( +def test_get_metadata( tmp_path: Path, format: AsciiDoc, content: str, expected: Metadata ): changelog = tmp_path / format.default_changelog_file diff --git a/tests/test_changelog_format_markdown.py b/tests/test_changelog_format_markdown.py index e1f0d67311..1abc63f29f 100644 --- a/tests/test_changelog_format_markdown.py +++ b/tests/test_changelog_format_markdown.py @@ -160,7 +160,7 @@ def test_parse_title_type_of_line( pytest.param(CHANGELOG_D, EXPECTED_D, id="D"), ), ) -def test_get_matadata( +def test_get_metadata( tmp_path: Path, format: Markdown, content: str, expected: Metadata ): changelog = tmp_path / format.default_changelog_file diff --git a/tests/test_changelog_format_restructuredtext.py b/tests/test_changelog_format_restructuredtext.py index 74b6b736f9..14bc15ec09 100644 --- a/tests/test_changelog_format_restructuredtext.py +++ b/tests/test_changelog_format_restructuredtext.py @@ -311,7 +311,7 @@ def format_with_tags(config: BaseConfig, request) -> RestructuredText: @pytest.mark.parametrize("content, expected", CASES) -def test_get_matadata( +def test_get_metadata( tmp_path: Path, format: RestructuredText, content: str, expected: Metadata ): changelog = tmp_path / format.default_changelog_file diff --git a/tests/test_changelog_format_textile.py b/tests/test_changelog_format_textile.py index eb03484ad5..812fa6bf60 100644 --- a/tests/test_changelog_format_textile.py +++ b/tests/test_changelog_format_textile.py @@ -153,7 +153,7 @@ def test_parse_title_type_of_line( pytest.param(CHANGELOG_D, EXPECTED_D, id="D"), ), ) -def test_get_matadata( +def test_get_metadata( tmp_path: Path, format: Textile, content: str, expected: Metadata ): changelog = tmp_path / format.default_changelog_file diff --git a/tests/test_conf.py b/tests/test_conf.py index 80d58983e7..f89a0049f4 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -151,7 +151,7 @@ def test_find_git_project_root(tmpdir): @pytest.mark.parametrize( - "config_files_manager", defaults.config_files.copy(), indirect=True + "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True ) def test_set_key(config_files_manager): _conf = config.read_cfg() @@ -162,7 +162,7 @@ def test_set_key(config_files_manager): class TestReadCfg: @pytest.mark.parametrize( - "config_files_manager", defaults.config_files.copy(), indirect=True + "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True ) def test_load_conf(_, config_files_manager): cfg = config.read_cfg() diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 210c8b6774..933b1aa065 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -473,16 +473,16 @@ def test_answer(config): cz = CustomizeCommitsCz(config) answers = { "change_type": "feature", - "message": "this feature enaable customize through config file", + "message": "this feature enable customize through config file", "show_message": True, } message = cz.message(answers) - assert message == "feature: this feature enaable customize through config file" + assert message == "feature: this feature enable customize through config file" cz = CustomizeCommitsCz(config) answers = { "change_type": "feature", - "message": "this feature enaable customize through config file", + "message": "this feature enable customize through config file", "show_message": False, } message = cz.message(answers) diff --git a/tests/test_factory.py b/tests/test_factory.py index 390742f467..d81a84b3d5 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -28,7 +28,7 @@ class OtherPlugin: def test_factory(): config = BaseConfig() config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) - r = factory.commiter_factory(config) + r = factory.committer_factory(config) assert isinstance(r, BaseCommitizen) @@ -36,7 +36,7 @@ def test_factory_fails(): config = BaseConfig() config.settings.update({"name": "Nothing"}) with pytest.raises(NoCommitizenFoundException) as excinfo: - factory.commiter_factory(config) + factory.committer_factory(config) assert "The committer has not been found in the system." in str(excinfo) diff --git a/tests/test_version_scheme_pep440.py b/tests/test_version_scheme_pep440.py index 6b1f621cb8..a983dad14a 100644 --- a/tests/test_version_scheme_pep440.py +++ b/tests/test_version_scheme_pep440.py @@ -144,7 +144,7 @@ (("3.1.4a0", "MAJOR", "alpha", 0, None), "4.0.0a0"), ] -excact_cases = [ +exact_cases = [ (("1.0.0", "PATCH", None, 0, None), "1.0.1"), (("1.0.0", "MINOR", None, 0, None), "1.1.0"), # with exact_increment=False: "1.0.0b0" @@ -213,7 +213,7 @@ def test_bump_pep440_version(test_input, expected): ) -@pytest.mark.parametrize("test_input, expected", excact_cases) +@pytest.mark.parametrize("test_input, expected", exact_cases) def test_bump_pep440_version_force(test_input, expected): current_version = test_input[0] increment = test_input[1] diff --git a/tests/test_version_scheme_semver.py b/tests/test_version_scheme_semver.py index 71d5e5876c..8785717a34 100644 --- a/tests/test_version_scheme_semver.py +++ b/tests/test_version_scheme_semver.py @@ -83,7 +83,7 @@ (("1.0.0-alpha1", None, "alpha", 0, None), "1.0.0-a2"), ] -excact_cases = [ +exact_cases = [ (("1.0.0", "PATCH", None, 0, None), "1.0.1"), (("1.0.0", "MINOR", None, 0, None), "1.1.0"), # with exact_increment=False: "1.0.0-b0" @@ -144,7 +144,7 @@ def test_bump_semver_version(test_input, expected): ) -@pytest.mark.parametrize("test_input, expected", excact_cases) +@pytest.mark.parametrize("test_input, expected", exact_cases) def test_bump_semver_version_force(test_input, expected): current_version = test_input[0] increment = test_input[1]